Reachability analysis determines whether the vulnerable code inside a dependency is actually invoked by your application. A traditional software composition analysis (SCA) scanner flags a package because its version matches a known CVE. Reachability analysis goes one step further: it traces the call graph from your own code into that package to check whether the specific vulnerable function is ever reached. If it is not, the finding is almost always non-exploitable in your context, and can be safely deprioritized. This one distinction is what turns a list of hundreds of alerts into a handful that genuinely need action.
Frequently Asked Questions
What is reachability analysis in application security? Reachability analysis is a technique that checks whether a known-vulnerable function or code path in a dependency is actually invoked from your application's code. Instead of stopping at "you use a vulnerable version," it answers "does your code ever call the part that is vulnerable?" Safeguard uses it as a triage layer on top of software composition analysis so teams spend effort on findings that can actually be exploited.
How is reachability different from a normal SCA scan? A normal SCA scan does version matching: it compares the components in your lockfile against a vulnerability database and reports every match. Reachability adds a second question about exploitability by building a call graph. Version matching tells you a vulnerable library is present; reachability tells you whether the vulnerable code within it is used. Both are useful, but only the second one reliably ranks what to fix first.
How does the call-graph analysis actually work? The engine parses your source or compiled artifacts and builds a graph of which functions call which other functions, starting from your application's entry points. Each CVE is mapped to the specific vulnerable symbols it affects. If a path exists from your entry points to a vulnerable symbol, the vulnerability is marked reachable; if no such path exists, it is marked not-reachable. The accuracy depends heavily on the quality of the per-CVE function mapping.
What are the possible verdicts for a finding? Most reachability engines, including Safeguard's, produce three outcomes: reachable (a call path was found), not-reachable (no path was found), and unknown or inconclusive (the analysis could not resolve the path with confidence). The unknown verdict matters for honesty, because dynamic language features and reflection can defeat static tracing. Treating unknown as "assume reachable until proven otherwise" is the safe default.
Does reachability analysis run my application? No. Reachability analysis is a static technique. It examines code structure without executing the program, which means it works on source and build artifacts without a running environment or test traffic. This is different from dynamic testing, where a tool like DAST exercises a live endpoint. The two are complementary: static reachability narrows the dependency list, and dynamic testing confirms behavior at runtime.
Is a not-reachable vulnerability safe to ignore? It is safe to deprioritize, not to forget. A not-reachable verdict means the vulnerable path is not currently invoked, so the exploit risk today is low. But a future code change, a new call site, or a transitive path that the analysis missed can make it reachable later. Good practice is to suppress it from the urgent queue while keeping it tracked, and to re-evaluate on every scan.
Can reachability analysis be wrong? Yes, in both directions, though false negatives are the bigger concern. Dynamic dispatch, reflection, dependency injection, native calls, and runtime code loading can hide real call paths, producing an over-optimistic not-reachable verdict. Conversely, an overly broad function mapping can mark something reachable that is effectively dead code. This is why the honest engines expose an unknown state instead of forcing every finding into reachable or not.
Which languages and ecosystems support reachability? Support is strongest where static analysis is tractable: compiled and statically typed ecosystems like Java, Go, and C#, plus JavaScript, TypeScript, and Python with type inference. Highly dynamic code reduces precision everywhere, so coverage is best treated per-ecosystem rather than as a single number. When you evaluate vendors, ask which languages have real call-graph support versus which only do version matching dressed up as reachability.
How much noise does reachability actually remove? Independent studies and vendor data commonly report that a large majority of version-matched vulnerabilities are not reachable in a given application, with figures frequently cited in the 70 to 90 percent range depending on ecosystem and code style. The exact number varies, so treat any single percentage with caution. The consistent finding is that reachability removes most of the queue, which is the point.
Does reachability replace CVSS, EPSS, or KEV? No. Reachability answers "is this exploitable in my code," while CVSS describes technical severity, EPSS estimates exploitation likelihood in the wild, and CISA KEV lists what is being actively exploited. The strongest prioritization stacks all of them: a reachable finding that is also on KEV with a high EPSS score is a clear top priority. Reachability is a filter, not a scoring system.
How does reachability interact with transitive dependencies? Most vulnerable code lives in transitive dependencies you never imported directly, and reachability is especially valuable there. The call graph follows the path from your code through your direct dependencies and into the transitive ones, so a CVE deep in the tree can still be correctly flagged as reachable if a real path exists. Without reachability, transitive findings are the noisiest and most confusing part of any SCA report.
How does Safeguard use reachability in practice? Safeguard layers reachability on top of standard SCA, then combines the verdict with EPSS, KEV, and severity to rank findings. Griffin AI explains each finding in plain language, including why it was marked reachable or not, so developers can trust the triage rather than guess. Teams migrating from a dependency-only scanner often compare the difference in queue size directly; our Snyk comparison walks through where call-graph reachability changes the results.
Curious how many of your findings are actually reachable? Start free or read the reachability guide in the Safeguard docs.