95%
of npm package vulnerabilities reported by SCA tools are in transitive dependencies rather than direct dependencies -- the vast majority of SCA noise is not from code you wrote
80%
of transitive dependency vulnerabilities are in code paths that are never called by the application, meaning the vulnerability is present but unexploitable in that deployment context (Endor Labs 2024)
3 tiers
for triaging transitive dependency vulnerabilities: confirmed reachable and exploitable (fix immediately), not reachable in current deployment (document and defer), and reachability unknown (use EPSS to prioritize)
500-1,500
transitive dependencies in a typical mid-sized Node.js application -- the scale that makes manual triage impossible and reachability analysis essential

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Run an SCA scan on a modern Node.js application and you might get 200 findings in a single CI run. The first time a development team sees this, the reaction is alarm. The fifth time, the reaction is to start ignoring the scanner entirely.

Both reactions are wrong. Most transitive dependency vulnerabilities are in code paths that your application never executes, which means they are genuinely unexploitable in your deployment context. But some are in code paths that are called on every request, which means they represent real risk that the scanner is correctly surfacing alongside the noise.

The challenge is separating these two categories without manually investigating 200 findings per scan. This guide covers the reachability analysis concept, the tools that automate it, the triage decision framework for findings where reachability cannot be confirmed, and the escalation criteria that ensure you are not suppressing genuine risk while managing scanner noise.

Why Transitive Dependency Scanning Generates So Much Noise

A direct dependency is a package you explicitly imported: it is in your package.json, requirements.txt, pom.xml, or go.mod. A transitive dependency is any package that your direct dependencies import, plus any packages that those packages import -- the full dependency tree.

The vulnerability report noise problem is structural. When lodash has a vulnerability, every package that depends on lodash generates a finding in your scan -- even if your code only uses the five lodash functions that have nothing to do with the vulnerable code path. When a logging library's dependency has a vulnerability, every application using that logging library has the finding -- even if the vulnerable feature of the dependency is never invoked by the logging library's code paths that you call.

The scanner cannot see which specific functions your application calls. It sees what is installed. It reports vulnerabilities in everything installed. This is technically correct -- the vulnerable code is present in your dependency tree -- but operationally misleading because most of those code paths are dead code in your specific deployment.

The 2023 Log4Shell response illustrated this at scale: organizations spent enormous effort patching Log4j2 deployments where the JndiLookup class that enabled the exploit was present but JNDI lookups were never performed, while the same organizations had other Log4j2 deployments where JNDI lookups were active and the risk was genuinely critical. Reachability analysis is what distinguishes these two scenarios.

Reachability Analysis: What It Is and Which Tools Provide It

Reachability analysis determines whether the vulnerable code path in a dependency is actually called by your application. A vulnerability in a package is only exploitable if the code path containing the vulnerable function is reachable from your application's call graph.

Tools that perform reachability analysis or reachability filtering:

Snyk (with reachability analysis enabled for Java and Python): For Java (Maven and Gradle) and Python applications, Snyk performs static call graph analysis to determine whether the vulnerable function in a dependency is called by your application code. Findings where the vulnerable function is not reachable are surfaced with a "Not Reachable" label. This does not mean the vulnerability is false -- it means it is unexploitable in your specific deployment.

Google OSV-Scanner: The open-source OSV scanner queries the OSV database and can filter results by package-level reachability for Go modules. Less precise than full call graph analysis but useful for Go ecosystems without Snyk coverage.

Semgrep Supply Chain: Performs reachability analysis for Python and JavaScript by analyzing import paths and function call chains. Shows which findings are reachable from your application's code.

Endor Labs: Provides reachability analysis across multiple languages with dependency graph visualization showing exactly which call path connects your code to the vulnerable function.

For ecosystems where reachability analysis tooling is not available (some Ruby, PHP, or older Java configurations), apply the manual reachability heuristics: if the vulnerable function is in a package feature that your application explicitly disables or never configures, the risk is substantially lower than the CVSS score implies.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

The Three-Tier Triage Decision Framework

Categorize every transitive dependency finding into one of three tiers immediately after the scan:

Tier 1: Confirmed reachable and exploitable. The vulnerable function is called from your application's code path, confirmed by reachability analysis or manual code review. Treat these as you would any other critical vulnerability: assign an owner, set a remediation SLA based on severity, and track to closure. These are typically 5 to 20% of total findings for most applications with good reachability analysis tooling.

Tier 2: Confirmed not reachable in current deployment. The vulnerable function is not called from your application's code path, confirmed by reachability analysis. The vulnerability is present in the installed code but unexploitable given how you use the library. Action: document the finding with the reachability determination, note the next review date (when the application's code or the dependency's version changes), and deprioritize remediation until a reachability-changing event occurs. Do not suppress the finding -- mark it as "risk accepted pending upstream fix" with the reachability justification.

Tier 3: Reachability unknown. Your tooling cannot determine reachability for this finding (ecosystem not supported, call graph analysis failed, or the vulnerability is in shared library code with no clear function-level attribution). Use EPSS score as the secondary filter: EPSS above 0.3 (30% exploitation probability) means treat as Tier 1 and investigate reachability manually. EPSS below 0.1 means document as Tier 2 equivalent with the justification "low exploitation probability, reachability unconfirmed." EPSS between 0.1 and 0.3 means schedule a manual reachability check within 30 days.

Upstream Fix Strategies: When You Cannot Update the Vulnerable Package Directly

Transitive dependency vulnerabilities often cannot be fixed by directly updating the vulnerable package because it is not in your direct dependency list. The update path depends on your package ecosystem and the dependency graph structure.

Resolution override (npm resolutions, pip constraints). In npm, the package.json resolutions field forces a specific version of a transitive dependency regardless of what version the parent package requests. This is a workable short-term fix for critical vulnerabilities where the parent package's maintainer has not yet released an updated version. Caution: resolution overrides can break the parent package if the forced version has incompatible API changes. Test thoroughly after applying a resolution override.

Parent package update. If the vulnerable transitive dependency is fixed in a newer version of your direct dependency, updating the direct dependency is the cleanest fix. Check the changelog and release notes of the direct dependency for security fix references.

Exclusion and replacement. For Maven, pom.xml exclusions remove a specific transitive dependency from the build entirely. Combined with a direct dependency declaration of the patched version, this produces the same result as a resolution override. For pip, constraints files serve the same function.

Waiting for upstream. When none of these options are available (the direct dependency does not yet have a release with the fixed transitive dependency version, and a resolution override would break functionality), document the finding as "pending upstream fix" with the CVSS score, EPSS score, the specific version required to resolve it, and the date you confirmed the upstream fix is not yet available. Review this category monthly and escalate if a Tier 1 equivalent finding remains pending upstream fix for more than 30 days.

SBOM as the Foundation for Sustainable Transitive Dependency Management

An SBOM (Software Bill of Materials) is the complete inventory of every package -- direct and transitive -- in your application build. SBOM generation has moved from a compliance checkbox to an operational requirement as the volume of transitive dependency vulnerabilities has made undocumented dependency trees unmanageable.

Two SBOM formats are in common use: SPDX (maintained by the Linux Foundation) and CycloneDX (maintained by OWASP). Both are machine-readable JSON or XML formats that enumerate every package, version, and license in the dependency tree. Most major build tools have SBOM generation plugins: cdxgen for npm and Python, syft for container images, and the Maven CycloneDX plugin for Java.

The operational value of an SBOM in transitive dependency management is threefold: it enables you to query "which of our applications are affected by CVE-XXXX-XXXXX in package-name version X.X.X?" without scanning every repository independently, it provides the inventory for reachability analysis tools (SBOM as input rather than repository scan), and it serves as the compliance artifact for supply chain security requirements including Executive Order 14028 for US federal software vendors and emerging EU requirements.

Generate SBOMs at build time as part of your CI pipeline and store them with the build artifacts. This produces a point-in-time record of the dependency tree for each deployed version -- which is the artifact you need to answer "were we affected by this vulnerability at the time of this specific deployment?" when a new CVE is published against a package you may have used.

Setting a Transitive Dependency Vulnerability SLA That Engineering Will Follow

The reason most organizations have hundreds of open transitive dependency findings is that their SLA policy treats all SCA findings the same as direct dependency findings and first-party code vulnerabilities. A SLA that requires patching every transitive dependency within 30 days of discovery is operationally impossible given the volume -- so engineering ignores it entirely.

A SLA designed for transitive dependency reality distinguishes three scenarios:

Confirmed Tier 1 (reachable and exploitable): Treat as equivalent to a first-party vulnerability. SLA follows your standard severity-based schedule: critical within 7 days, high within 30 days, medium within 90 days.

Tier 3 (unknown reachability) with EPSS above 0.3: Perform reachability determination within 7 days. If confirmed reachable, apply Tier 1 SLA. If confirmed not reachable, reclassify to Tier 2.

Confirmed Tier 2 (not reachable): Review within 90 days or when any of the following occur: the affected version of the direct parent dependency is updated (which may change which version of the transitive dependency is installed), the application code that could create a reachable path is modified, or an upstream fix is available that can be applied without introducing breaking changes.

Present this tiered SLA to engineering leadership with the finding distribution from your last scan (what percentage of findings are in each tier). If 80% of your current findings are Tier 2, the tiered SLA reduces the active remediation burden by 80% while still ensuring Tier 1 findings are treated with appropriate urgency.

The bottom line

The transitive dependency vulnerability problem is a triage problem, not a remediation volume problem. Scanners that report every vulnerability in every installed package regardless of reachability are technically correct but operationally misleading. A vulnerability in code your application never calls is not exploitable in your deployment context -- but it is also not zero risk, because future code changes might create a reachable path. The three-tier framework -- confirmed reachable (fix immediately), confirmed not reachable (document and monitor), unknown reachability (EPSS-filter and investigate) -- produces a manageable active remediation set from hundreds of raw findings. Combine it with SBOM generation for inventory clarity and a tiered SLA that engineering leadership will actually agree to follow. The goal is not zero transitive dependency findings. It is a clear, auditable record of which findings represent real risk and what you are doing about them.

Frequently asked questions

What is a transitive dependency vulnerability and why is it hard to fix?

A transitive dependency is a package installed not because your code imports it directly, but because a package you import requires it. A transitive dependency vulnerability is a CVE in one of these indirectly installed packages. Fixing it is harder than a direct dependency vulnerability because you cannot simply update the package in your dependency file -- you need to either force a specific version through a resolution override (npm resolutions, pip constraints), update the direct dependency that pulls in the vulnerable transitive package, or wait for the parent package to release an update that resolves the transitive dependency version.

What is reachability analysis and why does it matter for SCA triage?

Reachability analysis determines whether the specific function in a dependency that contains a vulnerability is actually called from your application's code. A vulnerability is only exploitable if the vulnerable code path is reachable from your application's execution. SCA scanners report vulnerabilities in all installed packages regardless of whether the vulnerable code path is ever called. Reachability analysis filters this output to show which findings are actually exploitable in your specific deployment -- typically 20% or fewer of total SCA findings for well-analyzed applications. Tools that provide reachability analysis include Snyk (Java and Python), Semgrep Supply Chain (Python and JavaScript), and Endor Labs (multi-language).

How do I reduce the number of SCA findings my development team has to deal with?

Apply a three-tier triage framework based on reachability: confirmed reachable vulnerabilities (active remediation on your standard severity SLA), confirmed not reachable vulnerabilities (documented, monitored, deprioritized until a code change creates reachability), and unknown reachability vulnerabilities (EPSS score above 0.3 triggers manual reachability investigation, below 0.1 is treated as low priority pending upstream fix). In most applications, 70 to 80% of findings fall into the confirmed not reachable or low-EPSS unknown category. Applying this framework reduces the active remediation set from hundreds of findings to the 20 to 30 that represent genuine risk.

What is an SBOM and why is it useful for managing transitive dependencies?

An SBOM (Software Bill of Materials) is a complete machine-readable inventory of every package -- direct and transitive -- in your application build, including package name, version, and license. SBOMs in SPDX or CycloneDX format enable you to query which of your applications are affected by a newly published CVE without scanning every repository independently, provide the dependency inventory input for reachability analysis tools, and serve as the compliance artifact for supply chain security requirements. Generate SBOMs at CI build time using tools like cdxgen (npm/Python) or the Maven CycloneDX plugin, and store them with the build artifacts.

How do I fix a transitive dependency vulnerability I cannot update directly?

Three approaches: resolution override (force a specific version of the transitive dependency using npm resolutions, pip constraints, or Maven exclusions plus a direct dependency declaration), parent package update (if the direct dependency that pulls in the vulnerable transitive package has released a version that uses the patched transitive dependency version), or waiting for upstream (document the finding as pending upstream fix with the required version and the date you confirmed it is not yet available, and review monthly). Resolution overrides can break parent package functionality if the forced version has incompatible API changes -- test thoroughly after applying one.

What SLA should apply to transitive dependency vulnerabilities?

Transitive dependency SLAs should distinguish three scenarios. Confirmed reachable vulnerabilities: same SLA as first-party code vulnerabilities (critical 7 days, high 30 days, medium 90 days). Unknown reachability with EPSS above 0.3: perform reachability determination within 7 days, then apply the reachable or not-reachable SLA based on the result. Confirmed not reachable: review within 90 days or when any reachability-changing event occurs (parent dependency update, application code modification near the relevant call path, or upstream fix availability). Presenting this tiered SLA with the actual finding distribution from your last scan -- showing what percentage falls in each tier -- makes engineering leadership agreement much more likely than a flat 30-day SLA applied to all findings.

How is triaging a transitive dependency vulnerability different from a direct dependency vulnerability?

Direct dependency vulnerabilities are in code you explicitly imported and control through your dependency file -- you can update the version directly and the fix is straightforward. Transitive dependency vulnerabilities are in code you did not choose to import and cannot update directly. They also require an additional triage step that direct dependency vulnerabilities typically do not: reachability analysis. Because you cannot update the transitive package directly, you must first determine whether the vulnerable code path is actually called by your application before deciding whether the effort of forcing a resolution override or waiting for an upstream fix is justified.

Sources & references

  1. Google OSV: Open Source Vulnerability Database
  2. Snyk: State of Open Source Security 2024
  3. NIST: Software Bill of Materials (SBOM) Resources
  4. EPSS: Exploit Prediction Scoring System (FIRST)
  5. Endor Labs: Dependency Management Report 2024

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.