Reachability analysis sits between matching and prioritization. After a scanner decides a package version is affected, reachability asks a narrower question: can your code actually call the vulnerable function or load the vulnerable path? If not, the finding can move down the queue. It needs source or build information, not just a package list.
Why a separate stage exists
Matching works at the package level. An advisory says version 2.3.1 of a library is affected. But a library may expose hundreds of functions, and an advisory usually concerns one. If your project never touches that function, the risk is lower than the package-level match suggests.
Reachability adds function-level detail on top of package-level matching.
What it needs as input
| Input | Why |
|---|---|
| Which function or module is vulnerable | Taken from advisory detail, not always present |
| Your source code or compiled output | To build a call graph |
| The dependency tree | To follow calls through transitive packages |
| Entry points | Where execution can begin: routes, handlers, main |
The first input is the limit. Advisory records often describe the flaw in prose and do not list vulnerable symbols in a structured way. The OSV schema leaves database-specific and ecosystem-specific fields for extra detail, and how complete they are varies by source. Where symbol data is missing, a tool has to fall back to package-level results or to its own curated data.
What the answers mean
- Reachable. A call path from an entry point to the vulnerable code exists. Prioritize normally.
- Not reachable. No path found. Lower the priority, but record that the verdict came from analysis.
- Unknown. Analysis could not decide, perhaps because of reflection, dynamic loading or generated code. Treat as reachable until shown otherwise.
The third answer deserves care. Static analysis cannot see everything, so "not reachable" is a claim about what was analysed, not a proof of safety.
Where it should not be trusted blindly
- Dynamic behaviour. Plugins, reflection and runtime configuration can create paths a static graph misses.
- Changing code. A path that does not exist today may exist after next week's change. Recheck on every scan.
- Other exposure. Reachability says nothing about how a flaw is exploited at the network or configuration level, which has its own factors.
- Exploited issues. If a finding appears in the CISA Known Exploited Vulnerabilities catalog, many teams treat it as urgent regardless of the analysis result, then verify.
How to use it well
- Match first, so reachability only works on real candidates.
- Run reachability, and keep the verdict with the finding.
- Lower priority for "not reachable", never delete the finding.
- Re-run when dependencies or entry points change.
- Keep a human review path for "unknown".
Try it yourself
Click through it at your own pace, or open it full screen.
Watch how the list of findings shrinks once call paths are checked, and which ones remain.
Where Safeguard fits
Safeguard includes reachability analysis alongside dependency scanning, so findings that cannot be triggered are separated from those that can, and policies can act on the difference. Start with the free scan.
Quick check
Does reachability replace severity scoring?
No. It adds a second question. Severity says how bad a flaw could be, reachability says whether your code can trigger it.
Can I suppress findings marked not reachable?
Deprioritize rather than delete. Code changes, and the verdict should be rechecked on each scan.
Sources
Last reviewed: 19 October 2026.