TLDR overview
- Vulnerability reachability determines whether your code can be affected by a vulnerability inside a third-party dependency, using call graph analysis.
- It cuts through software composition analysis backlogs by separating the vulnerabilities your code can reach from the thousands it never touches.
- Reachability is a prioritization signal that complements CVSS, KEV, and EPSS. It does not prove exploitability, and unreachable does not mean safe.
- SonarQube Advanced Security runs reachability analysis on the dependency vulnerabilities it detects in supported languages and shows the result on the dependency risk card.
A typical dependency tree pulls in hundreds, sometimes thousands, of packages. Scan that tree against the CVE database and you could get a backlog no team can realistically clear, most of it in code paths your application never executes. Triage becomes a full-time job, “critical” loses its meaning, and the vulnerabilities that actually put you at risk get buried.
This page explains what vulnerability reachability is, how call graph analysis determines it, how the three reachability states work, and how reachability relates to CVSS, KEV, and EPSS. It also covers what reachability cannot tell you and where it fits in a prioritization workflow. It is written for anyone who runs software composition analysis at scale and needs a sharper way to decide what to fix first.
What is vulnerability reachability?
Vulnerability reachability is a form of analysis that determines whether your code calls the specific functions associated with a known vulnerability in a third-party dependency. It answers a question a severity score never could: does your application actually use the vulnerable code?
A dependency being present in your project does not mean the flaw inside it is relevant to you. Many dependencies expose far more functionality than any single application uses. Reachability analysis builds a call graph of your codebase, then checks whether an execution path leads from your code to the vulnerable functions in the dependency. When a path exists, the vulnerability is reachable. When no path exists, your code does not directly call the affected functions.
In plain terms, reachability separates “this vulnerability exists in a library you depend on” from “your code can actually invoke the broken part of that library.” Those are different facts, and treating them as the same is what produces unmanageable backlogs.
Why does vulnerability reachability matter?
The volume problem is the reason reachability exists. Every software composition analysis tool can tell you a dependency is vulnerable. Far fewer can tell you whether that vulnerability is something your code can trigger, and that gap is where alert fatigue comes from. When a scanner returns thousands of findings and most sit in code paths you never call, severity scores alone cannot tell you where to start.
The timeline makes this urgent. The median time to patch a known exploited vulnerability is 43 days (451 Research), while the window between disclosure and active exploitation keeps shrinking. When that window is measured in hours, security teams cannot spend them investigating vulnerabilities their code never touches. Knowing which risks are confirmed reachable, and fixing those first, is how teams keep pace. Reachability narrows thousands of theoretical risks down to the ones your code can reach, so your team fixes what matters first and stops re-litigating the rest.
How does vulnerability reachability analysis work?
Reachability analysis rests on call graph analysis, a static analysis technique that maps how functions in your code call one another and call into the libraries you depend on. The process works in stages.
First, the analysis builds a call graph of your code, a map of which functions invoke which other functions, extending across the boundary from your first-party code into your third-party dependencies. Second, for each known vulnerability, it identifies the specific functions in the dependency that carry the flaw. Third, it checks whether a path in the call graph connects your code to those vulnerable functions. If a path exists, the vulnerability is marked reachable.
This is static analysis, which means it examines code without executing it. That approach has limits worth stating plainly. Call graph analysis is a hard problem across languages with different dependency resolution models, dynamic dispatch behavior, and build tooling. Dynamic dispatch, reflection, and runtime configuration can create call paths that static analysis cannot always resolve. Reachability also requires a full call graph, which is why it is calculated during a full analysis rather than a lightweight scan.
What are the three reachability states?
Reachability analysis resolves to one of three states for each dependency vulnerability.
Reachable
Analysis found your code calling functions associated with the vulnerability. Treat these as your priority queue. A reachable vulnerability is the one most worth your team’s immediate attention, because your application contains a path to the affected code.
Unreachable
Analysis found no instances of your code directly calling the vulnerable functions. This status lowers the threat, but it does not clear the risk. You may still be exposed through transitive calls from other dependencies, or through other exploitation paths that reachability analysis does not currently cover. Unreachable means “no direct call path was found,” not “safe.”
Not analyzed
Reachability has not been calculated for this vulnerability. That happens for a few reasons: the vulnerability is in a language reachability analysis does not yet support, the vulnerable package is a transitive dependency, or deep vulnerability analysis has not run yet. A not analyzed status is an absence of information, not a verdict either way.
How does reachability differ from and complement CVSS, KEV, and EPSS?
Reachability does not replace the scoring systems your team already uses. It sits on top of them, adding the one input they cannot: specificity to your codebase. CVSS, KEV, and EPSS all describe the vulnerability in the abstract. Reachability describes the vulnerability in the context of your application.
CVSS
The Common Vulnerability Scoring System rates the technical severity of a vulnerability. It tells you how bad the flaw is in principle, independent of whether your code touches it.
KEV
CISA’s Known Exploited Vulnerabilities catalog records whether a vulnerability is being actively exploited in the wild. It tells you the flaw is a proven, real-world target, independent of your specific application.
EPSS
The Exploit Prediction Scoring System estimates the likelihood that a vulnerability will be exploited in the next 30 days. It tells you where attacker attention is likely to go next, again in the abstract.
Key distinction
CVSS, KEV, and EPSS describe the vulnerability. Reachability describes your exposure to it. A blocker-severity CVE sitting in a code path you never call is a different problem from the same CVE in a function your application invokes on every request. Reachability is often the difference between the two, which is why it works best layered on top of severity and exploitation data rather than used alone.
What does vulnerability reachability not tell you?
Reachability is a prioritization signal, not a verdict. Knowing its limits keeps teams from drawing conclusions it cannot support.
It does not prove exploitability. A reachable vulnerability means your code can call the affected function, not that an attacker can necessarily trigger the flaw through your application. Reachability raises priority; it does not confirm an exploit.
It does not clear unreachable risks. An unreachable status lowers the threat but leaves exposure through transitive calls from other dependencies and other exploitation paths the analysis does not cover. Marking an unreachable vulnerability as resolved on reachability alone is a mistake.
It does not cover everything. Reachability analysis supports a focused set of languages, and a vulnerability in an unsupported language, or in a package that is a transitive dependency, can land in the not analyzed state. That state carries no signal about risk in either direction.
It does not replace severity and exploitation data. Reachability informs priority alongside CVSS, KEV, and EPSS. It is one input to the decision, not the decision itself.
Where does reachability fit in a prioritization workflow?
Reachability earns its place as a filter applied alongside your existing severity and exploitation data, not as a replacement for them. A practical workflow layers the signals.
Start with severity and exploitation data to understand how dangerous each vulnerability is in principle, using CVSS, KEV, and EPSS. Then apply reachability to find, within that set, the vulnerabilities your code can reach. Reachable findings with high severity and known exploitation move to the front of the remediation line. Unreachable findings drop in urgency but stay on the books, because the status lowers the threat without clearing it. Not analyzed findings get triaged separately, since the absence of a reachability result is not evidence of safety.
The practices below keep reachability working as intended:
- Use reachability to rank, not to dismiss. Reachable findings rise; unreachable findings fall in priority but remain tracked.
- Never treat unreachable as safe. Transitive calls and uncovered exploitation paths mean the risk persists.
- Keep severity and exploitation data in the loop. Reachability complements CVSS, KEV, and EPSS; it does not stand in for them.
- Account for the not analyzed state. Unsupported languages and some transitive dependencies produce no signal, so handle those findings through your standard triage.
- Re-run reachability on a full analysis. Because it needs a complete call graph, reachability is current only as of your last full analysis.
How can SonarQube Advanced Security help you prioritize reachable vulnerabilities?
SonarQube Advanced Security brings reachability into your dependency risk workflow. For every vulnerability it detects in a third-party dependency, SonarQube performs call graph analysis of your codebase to determine whether your code calls the specific functions associated with that vulnerability, then surfaces the result as one of three states: Reachable, Unreachable, or Not analyzed. Reachability is available to SonarQube Advanced Security customers on SonarQube Cloud (starting with the Team plan) and SonarQube Server (Enterprise edition).
The signal appears where your team already works. A Reachability filter and a Reachable badge sit on the Dependency Risks tab, so you can surface confirmed reachable vulnerabilities instantly. In the detailed risk view, reachability status sits alongside CVSS, EPSS, and KEV data, so you get the full picture in one place. Reachability status is also included in downloadable dependency risk reports in JSON or CSV format, which supports the evidence your compliance attestations require. Reporting features are available on the Enterprise plan.
Reachability analysis is currently supported for vulnerabilities in Java/Kotlin, Python, and C#/VB.NET code. Because it requires a full call graph, reachability is calculated during a full analysis, run in CI or through automatic analysis, rather than during continual analysis. Vulnerabilities first detected through continual analysis receive their reachability status at the next full analysis. No extra configuration is required: reachability status appears on your existing dependency risks after the next full analysis.
Reachability pairs with the rest of the dependency risk workflow. Dependency risk severity is based on CVSS severity, KEV, and EPSS. Teams can mark risks Open, Confirmed, Accepted, or Safe with a justification, assign them, and configure quality gate conditions on dependency risks. SonarQube Advanced Security runs this across software composition analysis for direct and transitive dependencies, malicious package detection, and license management, as part of its software supply chain security capabilities. For SonarQube Remediation Agent subscribers, reachable vulnerabilities are exactly the ones worth automating: the agent can open a pull request that bumps the affected dependency to a fixed version.
To get started, see reviewing and fixing dependency risks in the documentation, or read the vulnerability reachability announcement.
Next steps
- Software composition analysis—how dependency scanning identifies vulnerable and malicious open-source packages across your project.
- Reviewing and fixing dependency risks—product documentation covering reachability filters, risk severities, statuses, and remediation.
- Vulnerability reachability announcement—the launch post with detail on where reachability appears and how to get started.
- Best practices for managing dependency risks—guidance on triaging and remediating dependency findings at scale.
