← Back to blog
Published June 29, 20265 min read

SCA Reachability Analysis: When a Vulnerable Dependency Really Matters

SCAReachabilitySupply ChainPrioritization

Run a dependency scanner on almost any real application and you get a long list of vulnerable packages. Many are buried several levels deep in the dependency tree, in libraries your team has never heard of. Developers are asked to upgrade them all, upgrades break builds, and the security team struggles to explain why this particular CVE matters more than the other ones.

The uncomfortable truth is that a vulnerable package in your dependency tree is not the same as a vulnerable application. Reachability analysis is the technique that tells the two apart, and it is one of the most effective ways to turn software composition analysis (SCA) from a noise generator into a prioritization tool.

Direct vs transitive dependencies

A direct dependency is a package your project declares explicitly in its manifest. A transitive dependency is one pulled in by your dependencies, and by their dependencies, and so on. In most ecosystems the transitive tree is much larger than the list you wrote yourself.

This matters for two reasons. First, most vulnerable packages tend to be transitive, so you cannot fix them by editing one line; you may need the intermediate library to release an update, or you need to override the version. Second, the further a package sits from your code, the more likely it is that you only use a small part of it, or none of the part that is vulnerable.

Why a vulnerable package is not always exploitable

A CVE in a library usually affects a specific function, class or code path: a parser for one file format, a particular configuration option, an optional feature. For the vulnerability to be exploitable in your application, several things generally need to be true:

  • The vulnerable package is actually loaded at runtime, not just present as a development or test dependency.
  • Your code, directly or through other libraries, calls the vulnerable function.
  • Attacker-controlled data can reach that call with the conditions the exploit needs.

If the vulnerable function is never called, the risk is much lower. It is not zero, because code changes and the next commit could start using it, but it is a different priority from a vulnerability sitting on a request path exposed to the Internet.

Levels of reachability

Reachability is not a single yes or no. Tools work at different depths:

  • Package-level: is the package installed and used by production code at all? This alone removes test-only and unused dependencies.
  • Import-level: does your code import the affected module?
  • Function-level: does a call graph built from your code reach the specific vulnerable function, across your code and intermediate libraries?

Function-level reachability is the most precise. It requires knowing which functions a CVE affects and building a call graph from your entry points through the dependency tree. When it works, it lets you say "this CVE is in our tree, but nothing we run can call the affected code" with much more confidence.

Limits and caveats

Reachability is powerful, but it is static analysis, and static analysis has blind spots. Be honest with yourself about them:

  • Dynamic code. Languages and frameworks that load code by name at runtime, use dynamic dispatch heavily or evaluate strings as code can hide call paths from a static call graph.
  • Reflection and dependency injection. Calls wired up by configuration, annotations or reflection may not appear as direct calls in source code.
  • Incomplete vulnerability metadata. Function-level analysis depends on knowing which functions are affected. When advisories do not specify that, tools fall back to package- or import-level results.
  • Code changes. "Unreachable" is a statement about today's code. It should be re-evaluated on every change, not treated as a permanent exception.

The practical rule: use reachability to order work, not to ignore findings forever. Unreachable vulnerabilities still deserve an upgrade on a normal schedule; reachable ones deserve attention now.

Malicious packages are a different problem

Vulnerable packages and malicious packages are often lumped together, but they are very different risks:

  • A vulnerable package is legitimate software with a bug. Risk depends on reachability, exploitability and exposure.
  • A malicious package was published to do harm: typosquatted names, hijacked maintainer accounts, or install scripts that steal credentials or open backdoors. Reachability is irrelevant here, because the harmful code may run at install time, before your application does.

A malicious package in your tree is an incident, not a backlog item. It calls for removal, investigation of the systems that installed it, and rotation of any secrets those systems could reach.

How reachability feeds prioritization

Reachability is one signal among several. It works best combined with:

  • Likelihood of exploitation from EPSS and known exploitation from CISA KEV.
  • Whether the application is Internet-facing and how critical it is to the business.
  • Whether the same component also appears in container images or hosts, so the fix can be a single action.

A reachable, actively exploited vulnerability in an Internet-facing service belongs at the top of the list. An unreachable one with a low EPSS score in an internal tool can wait for the next planned upgrade. This is the same logic behind how to prioritize vulnerabilities, applied to your supply chain.

FAQ

Can I mark unreachable vulnerabilities as accepted risk? You can, with a documented decision and an expiry date, so they are reviewed when code changes. Silent suppression is what creates surprises.

Does reachability replace an SBOM? No. An SBOM is the inventory of what you ship; reachability analyzes how that inventory is used.

Is reachability only for SCA? It is most associated with dependencies, but the same idea underpins attack-path thinking across infrastructure, as in toxic combinations.

How priorIQ.ai helps

CodeIQ performs SCA with reachability analysis and malicious-package detection, generates CycloneDX and SPDX SBOMs, and can open AI-generated, verified fixes as pull requests. It sits alongside SAST, secrets, IaC and container scanning, covered in SAST vs SCA vs secrets vs IaC.

Those results feed PriorIQ Core, which combines reachability with CVSS, EPSS, KEV, exploit signals and business context, deduplicates the same component across code, containers and hosts, and turns it into prioritized remediation actions with tickets and an audit trail. Request a demo.