PRACTITIONER GUIDE | SUPPLY CHAIN SECURITY
Practitioner GuideUpdated 12 min read

npm audit Shows 47 High-Severity Findings, Here Is What to Actually Do

73%
of npm audit high-severity findings are in transitive dependencies with no direct call path from application code
160+
packages compromised in the May 2026 TanStack supply chain attack, many developers had no triage process ready
12M
weekly downloads of @tanstack/react-router, showing how fast a compromised transitive dep reaches production

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

When npm audit returns 47 high-severity findings, the correct response is not to run npm audit fix and hope for the best. Most of those findings are in transitive dependencies, packages your dependencies depend on, where no code path in your application ever reaches the vulnerable function. Running npm audit fix upgrades things that did not need upgrading, breaks lockfiles, and introduces new regressions, all without materially reducing your real risk. The triage process below cuts through the noise in under an hour.

Why npm audit Produces So Much Noise

npm audit flags every vulnerability in every package in your dependency tree, regardless of whether your application can trigger it. A vulnerability in a deep transitive dependency, say, a prototype pollution issue in a utility package that your test runner uses, will show as high severity even if it requires attacker-controlled input to a code path that never executes in your app.

Three categories generate most of the noise:

  1. Dev-only dependencies: Vulnerabilities in packages listed under devDependencies never reach production. A high-severity CVE in webpack or jest matters for developer machine security but not for your deployed app.

  2. Unexploitable transitive vulnerabilities: The vulnerable function exists in your tree but nothing in your application calls it. npm audit has no call-graph awareness, it cannot tell you whether your code path reaches the vulnerable code.

  3. Vulnerabilities with no fix available: npm audit flags these anyway, giving you a list item you cannot action. These need a workaround strategy, not a package update.

The result is that treating every npm audit finding as equal urgency is the same as treating every CVE as a P1. You end up either paralyzed or making changes that cause regressions without improving security.

Step 1: Separate Direct from Transitive

Run npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.isDirect == true) | .key' to list only packages you directly depend on in package.json. These are your actual attack surface, you control whether they are in your tree and at what version.

For direct dependencies with high or critical severity findings, check three things before acting:

  • Is there a patched version available? If yes, update it, test, and close the finding.
  • Is the vulnerability in a code path your application uses? Read the CVE description. A vulnerability in an HTTP parser only matters if you expose an HTTP server.
  • Is the package still maintained? If the maintainer has not released a patch and shows no sign of doing so, that is a different problem than a simple version bump.

For transitive dependencies, move them to a separate list. You address these differently.

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.

Step 2: Handle Transitive Vulnerabilities Without Breaking Your Build

You have three real options for a vulnerable transitive dependency:

Override with npm overrides (package.json): Add an overrides block to force a specific version of the transitive package across your entire tree. This works when the patched version is API-compatible with the version your direct dependency expects.

{
  "overrides": {
    "vulnerable-package": ">=2.3.1"
  }
}

Test after applying this, if the transitive package has a breaking change, your direct dependency may fail in unexpected ways.

Accept and document: If the vulnerability requires attacker-controlled input to a code path that does not exist in your application (for example, a ReDoS in a URL parser your app never calls with user input), document the decision. Write a comment in your package.json or a SECURITY.md explaining why this finding is not exploitable in your context. This creates an audit trail and prevents the same finding being re-triaged next sprint.

Replace the direct dependency: If a direct dependency is the reason you have a vulnerable transitive package, and the maintainer is not addressing it, look for an alternative. This is the highest-effort option but sometimes the right one.

Step 3: Remove Dev Dependency Findings from Your Production Risk Register

Create a separate tracking category for devDependency vulnerabilities. These are real but they represent developer machine risk and CI/CD pipeline risk, not production application risk.

For dev dependency vulnerabilities, the relevant question is: can an attacker exploit this during your build process? Supply chain attacks against build tooling are real (the XZ Utils attack targeted a build dependency). But a prototype pollution vulnerability in a test runner is not the same risk as the same vulnerability in your production HTTP server.

Practical filter: flag devDependency vulnerabilities where the attack vector is network (AV:N in CVSS) or where the package is involved in processing external input (like a template engine used in code generation). These deserve remediation. Flag devDependency vulnerabilities where the attack is local-only or requires attacker presence on the developer machine as lower priority.

Step 4: Use Socket or Snyk for Call-Path Analysis

npm audit has no understanding of your application's actual code paths. Socket Security and Snyk both offer static analysis that traces whether your application code actually calls the vulnerable function in the flagged dependency.

Socket's free tier analyzes your package.json and flags packages with active supply chain indicators, malware, dependency confusion, typosquatting, install scripts that exfiltrate data. This is more useful than CVSS scores for catching the class of attack that hit TanStack in May 2026.

Snyk's open-source tier adds reachability analysis: it tells you whether the vulnerable function in a transitive package is on a reachable code path from your application entry points. A finding marked 'not reachable' can be deprioritized without further investigation.

Building a Sustainable npm Audit Process

One-time triage solves today's backlog but does not prevent it from rebuilding. Three practices that keep the signal-to-noise ratio manageable:

Fail CI only on direct, exploitable, high-and-critical findings. Add npm audit --audit-level=high --omit=dev to your CI pipeline. This blocks merges on critical findings in production dependencies while not blocking work because of a theoretical vulnerability in a dev tool.

Run a weekly automated triage: Schedule a CI job that runs npm audit --json, filters to direct production dependencies with critical/high scores, and opens a GitHub issue if any exist. This keeps the finding list visible without requiring a developer to manually check.

Pin your lockfile and audit it on every dependency change. Most vulnerabilities enter via package updates, not initial installation. The npm ci command uses the lockfile exactly, pair it with an audit step that runs whenever package-lock.json changes.

The bottom line

npm audit is a starting point, not an action list. Your real risk surface is direct production dependencies with exploitable vulnerabilities, typically 5 to 10 percent of the findings audit surfaces. Separate that signal from transitive, dev-only, and non-reachable noise before touching anything, and you will spend your remediation time where it actually reduces risk.

Frequently asked questions

Should I run npm audit fix --force?

Rarely. npm audit fix --force upgrades packages to versions that may have breaking API changes, and it will modify your lockfile in ways that are difficult to audit. Use it only when you have comprehensive test coverage and review every change it makes. For most teams, manual targeted updates of direct dependencies are safer.

How do I suppress a false positive in npm audit?

npm does not have a built-in suppression mechanism. The standard approach is to use a tool like audit-ci, which supports an allowlist in a configuration file where you document why a specific CVE is not applicable to your codebase. This creates an auditable record rather than silently ignoring findings.

What is the difference between npm audit and npm audit fix?

npm audit reports vulnerabilities. npm audit fix attempts to automatically resolve them by upgrading packages to patched versions within the semver range specified in package.json. It will not make breaking changes unless you add --force. For anything beyond patch-level fixes, manual review is necessary.

Do npm audit findings affect production if they are only in devDependencies?

Not directly in the deployed application, devDependencies are excluded from production bundles. However, they affect your build pipeline and developer machines. If a build tool is compromised (as in the XZ Utils attack), it can modify your production output. Treat network-exploitable devDependency vulnerabilities as meaningful supply chain risk.

How often should I run npm audit?

Run it on every CI build for production branches, and run it locally before any dependency change. Additionally, subscribe to npm's security advisories for your critical direct dependencies. New vulnerabilities are disclosed continuously, daily CI runs catch them within 24 hours of advisory publication.

What should I do if a vulnerable package has no fix available?

Document the finding with an explanation of whether it is exploitable in your specific application context. If it is exploitable and no fix exists, evaluate whether you can remove the dependency, replace it, or add application-level mitigations (input sanitization, sandboxing). Set a calendar reminder to recheck for a patch monthly.

Sources & references

  1. npm audit documentation
  2. Socket Security blog
  3. OpenSSF Scorecard

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.