Skip to main content
Back to blog
March 20, 2026·Updated May 2, 2026|10 min read|Antoine Duno|Web Security

CVE Vulnerabilities in npm Packages: How to Scan and Fix Them

Vulnerable npm packages are responsible for some of the most damaging supply chain attacks in recent years. This guide explains how CVEs get into your dependencies, how to use npm audit effectively, when upgrading is the right call, and how to automate security scanning so you're not caught off guard.

Antoine Duno

1,737 words

AD

Antoine Duno

Founder of ZeriFlow · 10 years fullstack engineering · About the author

Key Takeaways

  • Vulnerable npm packages are responsible for some of the most damaging supply chain attacks in recent years. This guide explains how CVEs get into your dependencies, how to use npm audit effectively, when upgrading is the right call, and how to automate security scanning so you're not caught off guard.
  • Includes copy-paste code examples and step-by-step instructions.
  • Free automated scan available to verify your implementation.

CVE Vulnerabilities in npm Packages: How to Scan and Fix Them

Your application is only as secure as its dependencies. With the average Node.js project depending on hundreds — sometimes thousands — of packages, the npm ecosystem is one of the most significant security attack surfaces in modern web development.

This guide covers how npm vulnerabilities work, how to use npm audit effectively (and interpret its output without panicking), when to upgrade versus patch, how different tools compare, and how to build a dependency security workflow that scales.


How npm Vulnerabilities Work

CVEs and the National Vulnerability Database

A CVE (Common Vulnerabilities and Exposures) is a publicly disclosed security vulnerability with a standardised identifier (e.g., CVE-2021-44228). Once a vulnerability is discovered and responsibly disclosed, it is assigned a CVE ID, given a CVSS score (0–10 for severity), and published in the National Vulnerability Database (NVD).

When a vulnerability is found in an npm package, it gets a CVE, and tools like npm audit, Snyk, and the GitHub Advisory Database cross-reference your installed packages against known CVEs.

Direct vs Transitive Dependencies

Your package.json lists direct dependencies. But each of those packages has its own dependencies (transitive dependencies), and those have further dependencies. A typical React application might have 20 direct dependencies but 800+ total packages in node_modules.

A vulnerability in a package you have never heard of can still be in your dependency tree:

your-app
└── some-library@1.2.0
    └── vulnerable-package@2.0.1  ← CVE-2024-XXXXX here

This is why npm audit sometimes reports vulnerabilities in packages that do not appear in your package.json — they are deep in the transitive tree.

The Severity Levels

CVSS ScoreSeveritynpm audit label
9.0–10.0Criticalcritical
7.0–8.9Highhigh
4.0–6.9Mediummoderate
0.1–3.9Lowlow

Using npm audit

Basic Audit

bash
npm audit

This outputs a table of vulnerabilities found in your dependency tree, grouped by severity. A typical output looks like:

# npm audit report

lodash  <4.17.21
Severity: high
Prototype Pollution - https://github.com/advisories/GHSA-xxxx
fix available via `npm audit fix`
node_modules/lodash
  some-library  >=1.0.0
  node_modules/some-library

7 vulnerabilities (3 moderate, 3 high, 1 critical)

Filtering by Severity

bash
# Only show high and critical vulnerabilities
npm audit --audit-level=high

# Exit with non-zero code if any high/critical found (useful for CI)
npm audit --audit-level=high && echo "No high/critical vulnerabilities"

JSON Output for Processing

bash
npm audit --json > audit-report.json

# Extract just the critical and high vulnerabilities
npm audit --json | jq ''.vulnerabilities | to_entries[] | select(.value.severity == "critical" or .value.severity == "high") | .key''

Checking for Production-Only Dependencies

Many vulnerabilities are in development dependencies (testing tools, linters) that never run in production. You can audit only production dependencies:

bash
npm audit --omit=dev

This significantly reduces noise in most projects — a testing framework with a prototype pollution vulnerability does not pose a production risk.


npm audit fix: When to Use It and When to Be Careful

Safe Fixes

bash
npm audit fix

This automatically updates vulnerable packages to the nearest version that resolves the vulnerability while respecting your package.json semver ranges. It only makes changes that are backwards-compatible (patch and minor updates).

This is generally safe to run. Review the changes it makes in package-lock.json and test your application.

Force Fixes (Use With Caution)

bash
npm audit fix --force

This can make major version updates and override semver ranges — changes that may introduce breaking changes. Do not run this without understanding what it will change:

bash
# Check what would change before applying
npm audit fix --force --dry-run

Read the advisory before force-fixing. Sometimes a "fix" means downgrading to an older version that is itself insecure in other ways, or it means a major version bump with a completely different API.

Manual Updates

For vulnerabilities in transitive dependencies that npm cannot auto-fix, you may need to manually override the version:

json
// package.json — override a transitive dependency version
{
  "overrides": {
    "vulnerable-package": ">=2.0.2"
  }
}

The overrides field (npm v8.3+) forces all instances of a package in the dependency tree to use the specified version.


When to Upgrade vs When to Patch

Not every vulnerability requires immediate action. The right response depends on:

Exploitability in Your Context

A prototype pollution vulnerability in a utility library is only dangerous if: 1. Untrusted user input reaches the affected code path 2. The affected code path is actually called in your application

Many npm audit findings are in packages used only during development (Webpack, Jest, ESLint plugins) or in code paths your application never exercises. Read the advisory and understand the attack vector before treating every finding as a five-alarm fire.

Asking the Right Questions

  • Is the vulnerable package installed in production (dependencies) or development (devDependencies)?
  • Does user-controlled input reach the vulnerable code?
  • Is a fix available? If yes, what is the upgrade path?
  • If no fix is available, what is the exposure window?

Priority Matrix

ScenarioAction
Critical CVE, fix available, production dependencyUpdate immediately
High CVE, fix available, production dependencyUpdate within 24–72 hours
High CVE, no fix available, productionEvaluate mitigations; monitor for fix
Critical CVE in devDependency onlyUpdate at next opportunity, low urgency
Moderate/Low CVE in productionPlan for next maintenance window

Tools Comparison: npm audit vs Snyk vs ZeriFlow

npm audit

Pros: Built-in to npm, no setup required, free Cons: Only checks against the GitHub Advisory Database; no prioritisation based on exploitability; cannot scan git history; no IDE integration in community version

bash
npm audit --audit-level=high

Best for: quick checks during development, CI/CD gate

Snyk

Pros: More comprehensive vulnerability database, exploitability scores, licence scanning, container scanning, fix PRs Cons: Free tier is limited; the proprietary vulnerability database has more coverage but costs money at scale

bash
# Install Snyk CLI
npm install -g snyk

# Authenticate
snyk auth

# Test for vulnerabilities
snyk test

# Monitor project (sends to Snyk dashboard)
snyk monitor

Best for: teams wanting a dedicated dependency security platform with dashboards

Dependabot (GitHub)

Dependabot is GitHub''s built-in dependency update service. It automatically opens pull requests to update vulnerable dependencies when a CVE is disclosed:

yaml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
    labels:
      - "security"
      - "dependencies"

Best for: automated PRs for dependency updates with no developer effort

ZeriFlow Advanced Scan

ZeriFlow''s Advanced Scan analyses your package.json and package-lock.json as part of a comprehensive codebase audit that also covers hardcoded secrets and insecure API patterns. You upload a GitHub repo URL or ZIP file, and the scan runs against the CVE database alongside all other checks.

Best for: developers who want a single tool covering both infrastructure security (headers, TLS, DNS) and codebase security (secrets, CVEs, patterns) in one pass


Automating Dependency Scanning in CI/CD

Adding a vulnerability gate to your pipeline ensures that critical CVEs cannot slip into production unnoticed.

GitHub Actions with npm audit

yaml
# .github/workflows/security.yml
name: Security Checks
on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  dependency-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: ''20''
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run security audit
        run: npm audit --audit-level=high --omit=dev
        # Fails the build if any high or critical vulnerabilities are found
        # in production dependencies

With Snyk in CI/CD

yaml
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --production

Blocking PRs with Score Thresholds

If you use ZeriFlow for infrastructure scanning, you can also block deploys based on the overall security score:

bash
# In your deploy script
SCORE=$(curl -s "https://api.zeriflow.com/v1/scan?url=$STAGING_URL&token=$ZERIFLOW_TOKEN" | jq ''.score'')

if [ "$SCORE" -lt 75 ]; then
  echo "Security score $SCORE/100 is below threshold. Deploy blocked."
  exit 1
fi

Dealing with Unresolvable Vulnerabilities

Sometimes a fix does not exist — the package maintainer has not released a patch, or the vulnerability is in a deeply nested transitive dependency you cannot easily override.

Options When No Fix Is Available

1. Add an override temporarily

json
{
  "overrides": {
    "affected-package": "1.2.3"
  }
}

Pin the affected package to a specific version while you wait for an upstream fix.

2. Replace the dependency

If a direct dependency has a persistent vulnerability and the maintainer is unresponsive, evaluate alternatives. The npm ecosystem almost always has multiple packages providing the same functionality.

3. Accept and document the risk

If the vulnerability is not exploitable in your context (e.g., it is in a devDependency or an unused code path), document your reasoning and suppress the alert:

bash
# npm suppress a specific advisory (use with care)
# In package.json:
{
  "auditConfig": {
    "ignoredAdvisories": ["1234567"]
  }
}

Document why each suppressed advisory is acceptable risk. Revisit them quarterly.

4. Isolate the vulnerability

For high-risk packages that cannot be updated, consider running them in a sandboxed environment or behind a validation layer that sanitises all input before it reaches the vulnerable code.


The Bigger Picture: Supply Chain Security

Individual CVEs are one dimension of dependency security. The broader threat is supply chain attacks — malicious actors compromising legitimate npm packages to inject malicious code.

Notable examples: - event-stream (2018): A popular package had a new maintainer add code that stole Bitcoin wallet credentials - ua-parser-js (2021): The package was temporarily hijacked and published versions that installed crypto miners and credential stealers - node-ipc (2022): A maintainer intentionally added code that wiped files on machines in Russia and Belarus

Mitigations: - Lock exact versions in package-lock.json or yarn.lock — do not allow * or loose version ranges for security-sensitive packages - Use npm ci in CI/CD instead of npm install — it installs exact versions from the lock file - Review new dependencies carefully before adding them: check the package''s GitHub activity, download counts, and maintenance status - Enable 2FA on your npm account to prevent account takeover attacks


Summary

Keeping your npm dependencies secure requires a layered approach:

  1. 1Run npm audit --omit=dev regularly and in CI/CD
  2. 2Use npm audit fix for safe, automatic patches
  3. 3Use overrides in package.json for transitive dependency fixes
  4. 4Add Dependabot or Renovate for automated update PRs
  5. 5Gate production deploys on vulnerability severity thresholds
  6. 6Read each advisory — not every npm audit finding is an actionable production risk

For a comprehensive codebase security check that covers CVEs alongside hardcoded secrets and infrastructure configuration, try ZeriFlow''s Advanced Scan.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading