A scanner tells you a dependency has a CVE. It does not tell you whether your code ever runs the vulnerable part. Reachability analysis answers that second question so you can order the backlog by what can actually be hit.
This post follows the Safeguard docs page on vulnerability prioritization.
What it is
A CVE is reachable when your code calls the vulnerable function or triggers the vulnerable code path. Safeguard describes three kinds of analysis:
- Static reachability. It builds a call graph from your source, lockfile and bytecode and checks whether any path leads to the vulnerable sink.
- Dynamic reachability. A runtime collector records which modules and functions are loaded and invoked in production workloads. Paths that never execute are flagged as cold.
- Configuration reachability. It parses framework configuration, such as Spring bean definitions or Next.js route files, to see whether the code is wired in at all.
Each vulnerability then lands in one of four classes.
| Class | Meaning |
|---|---|
| Reachable | A real call path exists and is instantiated at runtime |
| Conditionally reachable | A path exists but needs a feature flag, route or configuration that is not enabled now |
| Unreachable | No call path exists in your compiled code |
| Unknown | The analyzer could not decide, for example because of reflection or native code. Treated conservatively as potentially reachable |
How it combines with other signals
Reachability is one of four signals. The others are exploitability (EPSS and the CISA KEV list, plus public proof of concept availability), runtime context (environment, exposure, data class and criticality labels) and business context declared by policy. Together they produce a Priority Score from 0 to 100 per finding, and the weights are configurable per tenant.
Unknown is the important design choice. When Safeguard cannot prove something is unreachable, it does not suppress it.
How to use it
In the ESSCM Vulnerabilities tab, the table has a Priority column and a Reachability column, sorted by Priority descending by default. The docs list filters such as these:
reachable:true
kev:true
env:production reachable:true kev:true
You can also ask Griffin: "Show my P0 findings in production that are reachable." Once a finding is prioritized, Griffin can propose a minimum-risk upgrade, explain why a finding was de-prioritized (useful in exception reviews) and open fix pull requests.
Reachability also feeds policy. A guardrail condition can combine environment, reachability class and KEV status to decide whether to block.
Language coverage
Quality varies by language. The docs table marks JVM languages, Python, JavaScript and TypeScript, Go, Rust, C and C++, and C# and .NET as generally available for static and dynamic analysis, and marks some others as preview. Check the current table on the docs page before you rely on a specific ecosystem.
When reachability does not help
The docs are direct about this. It is weakest when code runs through reflection, eval or unsafe dispatch, when a vulnerability is data driven so that malicious input matters even if the function looks unreachable, and when a code path depends on configuration the analyzer cannot enumerate. In those cases rely on KEV, EPSS and runtime observation.
Try it yourself
Click through it at your own pace, or open it full screen.
Quick check
What does Safeguard do with a finding it cannot classify?
It marks it Unknown and treats it as potentially reachable. It does not suppress it.
Which analysis looks at what production actually executes?
Dynamic reachability, which records the modules and functions loaded and invoked at runtime.