Safeguard
Product

Reachability analysis for vulnerability prioritization

Most CVEs your scanner flags are never executed. See how reachability analysis filters noise, how Socket.dev approaches it, and how Safeguard finds real risk.

Aman Khan
AppSec Engineer
7 min read

A security team running a standard software composition analysis (SCA) scan on a mid-sized Node.js service typically sees 200-400 open CVEs across its dependency tree on any given week. Almost none of them get fixed on time, because almost none of them can be triaged fast enough. This is the core failure of vulnerability management by CVE count: a critical-severity flaw in a transitive dependency four levels deep, in a function your code never calls, gets the same red banner as a critical flaw sitting directly in your request-handling path. Reachability analysis exists to fix that gap by asking one concrete question per vulnerability — can an attacker actually get to this code? — and Socket.dev has built a meaningful part of its product pitch around answering it. Below, we break down how reachability analysis works, where Socket.dev's approach draws its line, and how Safeguard extends the same idea deeper into the call graph.

What Is Reachability Analysis, and Why Does It Matter for Vulnerability Prioritization?

Reachability analysis determines whether the specific vulnerable function in a dependency is actually invoked, directly or transitively, by your application's own code. It matters because CVSS severity scores describe how bad a flaw is in isolation, not whether your program can ever trigger it. A textbook example: CVE-2021-44228 (Log4Shell) was catastrophic specifically because the vulnerable JndiLookup class was reachable through ordinary log message formatting in an enormous number of Java applications — that's what made it a 10.0 with real-world exploitation within 48 hours of disclosure. Contrast that with a vulnerable parsing function in a logging library that's only invoked by a CLI flag your application never sets. Same package, same CVE ID, wildly different real-world risk. Reachability analysis is the mechanism that tells those two cases apart instead of flattening them into the same severity number.

How Many Flagged Vulnerabilities in a Typical Dependency Tree Are Actually Reachable?

In most production codebases, only a minority of flagged CVEs sit on a path your code ever executes. Because the average application pulls in far more transitive dependency surface area than it actually uses — a single npm install for a modern web framework routinely resolves 800-1,500 transitive packages — the bulk of reported CVEs live in code paths that are imported but never called. Teams that turn on function-level reachability filtering commonly see their "must-fix-now" queue shrink by roughly 70-90%, not because the vulnerabilities stopped existing, but because most of them were never exploitable in that specific application's context. That gap is the entire economic argument for reachability analysis: it's the difference between a security backlog measured in months of engineering time and one a two-person AppSec team can actually clear every sprint.

How Does Socket.dev Approach Reachability?

Socket.dev's reachability analysis, which the company expanded through 2023 and 2024 as part of its broader supply-chain risk platform, works primarily by analyzing whether a vulnerable function is imported and invoked somewhere in the dependency graph relative to your manifest and lockfile. Socket combines this with its behavioral analysis of packages — flagging install scripts, obfuscated code, and suspicious network or filesystem calls — and layers reachability on top as a signal to deprioritize CVEs that don't trace back to an actual call site. This is a real improvement over flat CVE lists, and it's genuinely useful for catching malicious packages and typosquats before they land, which is where Socket's product originated. The tradeoff is depth: Socket's reachability signal is strongest at the package-and-function-import level, generated largely from static analysis of package manifests and source, which is fast and scales well across the open-source ecosystem but is less precise once you need to know whether a specific vulnerable code branch is reachable through your application's actual runtime conditionals, not just its import graph.

What Are the Limitations of Import-Level Reachability Scoring?

Import-level reachability tells you a function is referenced somewhere in your code, but it doesn't tell you whether the specific vulnerable execution path within that function is ever hit. A package can be imported, and even call the vulnerable function, while the vulnerable branch inside that function only triggers under a rare parameter combination — say, a specific content-type header or a non-default configuration flag — that your application never produces. Static, manifest-level tools can also struggle with dynamic language features: reflection in Java, eval-adjacent patterns in JavaScript, and dynamic imports in Python all make it easy to under- or over-count reachability if the analysis stops at "is this symbol imported." The result is a category of false negatives (marked unreachable, actually reachable under a code path the scanner didn't model) sitting alongside the false positives that reachability analysis was supposed to eliminate in the first place. For teams making fix/defer decisions off a single reachability flag, that gap has real consequences — a deprioritized CVE that turns out to be reachable is worse than an over-flagged one, because it doesn't even make the queue.

Does Reachability Analysis Replace CVSS Scoring, or Work Alongside It?

Reachability analysis works alongside CVSS and exploit-availability data — it's a filter and a re-ranker, not a replacement scoring system. The Exploit Prediction Scoring System (EPSS), maintained by FIRST, already demonstrates why single-axis severity scoring falls short: EPSS routinely shows that fewer than 5% of published CVEs ever have exploitation attempts observed in the wild, even though CVSS assigns "critical" to a much larger share of them. Reachability answers a different, narrower question than EPSS — not "is this being exploited anywhere," but "can this specific application be exploited through this specific path" — and the two signals compound. A CVE that is both reachable in your code and has a high EPSS score is the one that should interrupt a sprint; a CVE that's unreachable in your application, regardless of its CVSS or EPSS score, is one you can schedule for the next routine dependency bump instead of an emergency patch.

How Safeguard Helps

Safeguard builds reachability analysis directly into its vulnerability prioritization pipeline, going past manifest-level import checks to construct an actual call graph for your application and its dependencies. Instead of asking "is this package imported," Safeguard traces whether the specific vulnerable function, and the specific vulnerable branch within it, sits on a path your code can execute at runtime — combining static call-graph construction with the input conditions needed to reach the flaw. That gets you past the false-negative gap that pure import-level tools leave open, and it means a "not reachable" verdict from Safeguard is a verdict you can actually act on, not just a lower-confidence guess.

On top of reachability, Safeguard correlates each finding with EPSS exploitation likelihood, known-exploited-vulnerability (KEV) status, and your own deployment context — internet-facing versus internal service, production versus staging — so the fix queue that lands in your ticketing system is ranked by actual risk, not raw CVE count. For teams evaluating Safeguard against Socket.dev specifically, the practical difference shows up in the last mile: where package-level reachability gets you from 300 CVEs to 60 "maybe relevant" ones, function-and-branch-level reachability is what gets you from 60 down to the 8 that genuinely need an engineer this week — with an explanation of the exact call path an attacker would need, so your team isn't left re-verifying the tool's judgment by hand. That's the difference between a scanner that reduces noise and a system that tells you, with evidence, what to fix first.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.