Skip to content

Fix: npm audit Found Vulnerabilities – How to Fix and Manage Security Issues

FixDevs ·

Quick Answer

How to fix npm audit vulnerabilities including high and critical severity issues, dependency conflicts, and when to use npm audit fix --force safely.

The Error

You run npm audit (or it runs automatically after npm install) and see something like:

found 12 vulnerabilities (3 low, 5 moderate, 3 high, 1 critical)
  run `npm audit fix` to fix them, or `npm audit` for details

Or the more detailed version:

┌───────────────┬──────────────────────────────────────────────────────────┐
│ high          │ Prototype Pollution in minimist                         │
├───────────────┼──────────────────────────────────────────────────────────┤
│ Package       │ minimist                                                │
├───────────────┼──────────────────────────────────────────────────────────┤
│ Dependency of │ react-scripts                                           │
├───────────────┼──────────────────────────────────────────────────────────┤
│ Path          │ react-scripts > webpack > ... > minimist                │
├───────────────┼──────────────────────────────────────────────────────────┤
│ More info     │ https://github.com/advisories/GHSA-xvch-5gv4-984h      │
└───────────────┴──────────────────────────────────────────────────────────┘

The output shows which packages have known security vulnerabilities, their severity level, and a link to the GitHub Security Advisory (GHSA) with details about the issue. The path column is critical — it tells you whether the vulnerable package is a direct dependency or buried deep in a transitive dependency chain.

Why This Happens

npm maintains a registry of known security vulnerabilities. Every time you run npm install or npm audit, npm checks your installed packages against this database. When a package (or any package in its dependency tree) matches a known vulnerability, it gets flagged.

The vulnerabilities come from several sources:

  • GitHub Advisory Database (GHSA): The primary source. Security researchers and maintainers report vulnerabilities, which get assigned GHSA identifiers and often CVE numbers.
  • Snyk, Socket, and other security databases: These feed into npm’s advisory system as well.
  • Automated scanning: Tools that detect patterns like prototype pollution, ReDoS (regular expression denial of service), or path traversal in published packages.

The reason you often see dozens of vulnerabilities even in a fresh project is that modern JavaScript projects have massive dependency trees. A typical create-react-app project installs over 1,500 packages. A single vulnerable package deep in that tree triggers an audit warning even if your code never touches it.

Severity Levels

npm uses four severity levels:

  • critical: Remote code execution, credential exposure, or similar. Fix these immediately.
  • high: Significant impact but may require specific conditions to exploit.
  • moderate: Exploitable under limited circumstances. Usually worth fixing but not urgent.
  • low: Minimal impact. Informational in most cases.

Direct vs. Transitive Dependencies

This distinction matters for how you fix the vulnerability:

  • Direct dependency: A package listed in your package.json. You control its version directly.
  • Transitive dependency: A package installed because one of your dependencies (or one of their dependencies) requires it. You do not control its version directly — it is managed by the package that depends on it.

Most audit findings are in transitive dependencies. That is where the frustration comes from: you cannot simply upgrade them without cooperation from the intermediate packages.

Fix 1: Run npm audit fix

The simplest approach. npm will try to upgrade vulnerable packages to patched versions without breaking semver compatibility:

npm audit fix

This only applies non-breaking updates. If [email protected] has a vulnerability and [email protected] fixes it, npm audit fix will upgrade within the ^4.17.15 range. It will not jump to a new major version.

After running the command, check what changed:

npm audit

If all vulnerabilities are resolved, you are done. Commit the updated package-lock.json:

git add package-lock.json
git commit -m "Fix npm audit vulnerabilities"

In many cases, npm audit fix resolves zero or only a few vulnerabilities because the fixes require major version bumps that it will not apply automatically.

Fix 2: Use npm audit fix --force (With Caution)

When npm audit fix cannot resolve vulnerabilities within semver ranges, npm suggests using --force:

npm audit fix --force

This is dangerous. The --force flag allows npm to install major version upgrades of packages. This can break your application because major versions often contain breaking API changes.

What can go wrong:

  • A dependency jumps from v2 to v5 and removes APIs your code uses.
  • A transitive dependency gets upgraded, but the intermediate package does not support the new version.
  • Your lockfile ends up in an inconsistent state.

How to use it safely:

  1. Make sure your code is committed to git so you can revert.
  2. Run the command.
  3. Run your test suite immediately after.
  4. Test critical paths of your application manually.
  5. If anything breaks, revert:
git checkout package.json package-lock.json
rm -rf node_modules
npm install

If --force introduces dependency resolution conflicts, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree for how to handle those.

Fix 3: Update Specific Packages

Instead of letting npm audit fix --force make sweeping changes, target the specific vulnerable packages. First, identify exactly what needs to change:

npm audit

Read the output. Each vulnerability entry shows the vulnerable package, its current version, and the fixed version. If the vulnerable package is a direct dependency:

npm install package-name@latest

If you need a specific version:

npm install [email protected]

For multiple packages at once:

npm install lodash@latest minimist@latest nth-check@latest

After upgrading, run npm audit again to confirm the vulnerabilities are resolved.

If the vulnerable package is not a direct dependency but a transitive one, upgrading the direct dependency that pulls it in is often enough:

npm ls vulnerable-package

This shows you which of your direct dependencies depends on the vulnerable package. Upgrade that direct dependency:

npm install the-parent-package@latest

If the parent package has not released a version with the patched transitive dependency, you need a different approach. See Fix 4 or Fix 5.

Fix 4: Use overrides to Force a Transitive Dependency Version

When a vulnerability exists in a transitive dependency and the intermediate package has not updated to pull in the fix, you can force the resolution using npm’s overrides field in package.json:

{
  "overrides": {
    "minimist": ">=1.2.6"
  }
}

This tells npm to use [email protected] or higher everywhere in the dependency tree, regardless of what version other packages request.

For more targeted overrides, you can scope it to a specific dependency chain:

{
  "overrides": {
    "react-scripts": {
      "nth-check": ">=2.0.1"
    }
  }
}

This only overrides nth-check when it is installed as part of react-scripts’s dependency tree.

After adding overrides, reinstall and verify:

rm -rf node_modules package-lock.json
npm install
npm audit

If you use Yarn instead of npm, the equivalent feature is resolutions:

{
  "resolutions": {
    "minimist": ">=1.2.6"
  }
}

If you use older npm (before version 8.3 where overrides was introduced), you can use the npm-force-resolutions package. Install it as a dev dependency and add a preinstall script:

{
  "scripts": {
    "preinstall": "npx npm-force-resolutions"
  },
  "resolutions": {
    "minimist": ">=1.2.6"
  }
}

Then run:

rm -rf node_modules package-lock.json
npm install

npm-force-resolutions reads the resolutions field (Yarn’s format) and patches the lockfile before npm installs packages. It is a workaround for older npm versions and is no longer needed if you are on npm 8.3 or later.

Important: Overriding transitive dependency versions can break things. The intermediate package may rely on APIs that changed between versions. Test thoroughly. If the override causes lifecycle script failures, the forced version may be incompatible.

Why this matters: A critical vulnerability in a production dependency that handles user input means an attacker could potentially exploit it right now. A low-severity advisory in a build-time dev dependency that only processes your own source code is a fundamentally different risk. Treating them the same wastes time and creates alert fatigue.

Fix 5: Handle Vulnerabilities in Dev Dependencies

Not all vulnerabilities carry the same risk. A vulnerability in a package used only during development (in devDependencies) is fundamentally different from one in a production dependency.

For example, a prototype pollution vulnerability in a Webpack plugin is only exploitable if an attacker can control the input to your build process. In most setups, that is not a realistic attack vector.

Run npm audit with the production-only flag to see what actually ships:

npm audit --omit=dev

If all the vulnerabilities are in dev dependencies and your audit looks clean with --omit=dev, your production application is not affected. You can document this decision and move on.

For CI/CD pipelines, you can make this distinction in your audit check:

npm audit --omit=dev --audit-level=high

This exits with a non-zero code only if there are high or critical severity vulnerabilities in production dependencies. It is a pragmatic approach that avoids blocking deployments over low-risk findings.

Fix 6: Understand and Evaluate GHSA Advisories

Before spending hours fixing a vulnerability, read the actual advisory. Click the “More info” link in the npm audit output, which takes you to the GHSA page on GitHub.

Each advisory describes:

  • What the vulnerability is: prototype pollution, ReDoS, path traversal, etc.
  • What conditions are required to exploit it: Does the attacker need to control input? Does the vulnerable function need to be called with user-supplied data?
  • What versions are affected and which versions contain the fix.

Many reported vulnerabilities are not exploitable in your context. For example:

  • A ReDoS in a CSS parser only matters if you parse untrusted CSS at runtime. If you only use it at build time with your own stylesheets, there is no risk.
  • A prototype pollution in a deep dependency only matters if an attacker can pass crafted objects to the vulnerable function. If the data flow in your app never allows that, the vulnerability is theoretical.

This is not an excuse to ignore everything. Critical and high severity vulnerabilities in packages that handle user input, network requests, or authentication should always be fixed. But a low-severity advisory in a build tool that only runs on your CI server is not an emergency.

Fix 7: Switch to a Patched Alternative

Sometimes a vulnerable package is unmaintained. No fix is coming. The maintainer has abandoned the project or is unresponsive to security reports.

Common examples and their replacements:

Vulnerable PackageReplacementNotes
node-sasssassnode-sass is deprecated; Dart Sass is the official implementation
requestnode-fetch, axios, undicirequest is deprecated and has unpatched vulnerabilities
uuid (v3 and below)uuid@9Older versions have issues; v9 is actively maintained
minimistyargs, commanderIf you control the direct dependency

If the vulnerable package is a transitive dependency and its parent package refuses to switch, that is a strong signal to evaluate whether the parent package itself should be replaced.

If the vulnerable package is something you import directly, switching is straightforward:

npm uninstall vulnerable-package
npm install replacement-package

Then update your imports accordingly. If the replacement has a different API, expect some refactoring. If you run into module resolution errors after swapping packages, double-check that the new package’s export structure matches what your code expects.

Fix 8: Ignore Non-Exploitable Vulnerabilities

In some cases, a vulnerability genuinely does not affect your project and there is no available fix. npm does not have a built-in way to suppress specific advisories from npm audit, but there are practical approaches.

Use a .npmrc audit configuration

You can set the minimum severity level that npm reports:

audit-level=high

This means npm audit will only exit with a non-zero code for high and critical vulnerabilities. Low and moderate findings will still appear in the output but won’t fail your CI.

Use better-npm-audit

For more granular control, use better-npm-audit, which lets you allowlist specific advisories:

npm install --save-dev better-npm-audit

Create an .nsprc file:

{
  "1234567": {
    "active": true,
    "notes": "minimist prototype pollution - only used at build time, not exploitable in our context"
  }
}

Then replace npm audit in your CI with:

npx better-npm-audit audit

Document your decisions

If you choose to accept a vulnerability, leave a record. Add a comment in your package.json or create a security decisions document. Future developers (including yourself in six months) will want to know why a known vulnerability was left unpatched.

Real-world scenario: You run npm audit fix --force to clear all warnings, but it jumps react-scripts from v4 to v5, which removes Webpack 4 support. Your custom Webpack config breaks, your build fails, and you spend hours debugging what was supposed to be a quick security fix. Always commit your code before using --force so you can revert.

Fix 9: Audit in CI/CD Pipelines

Running npm audit in CI prevents vulnerable packages from reaching production. However, a naive npm audit call will fail your pipeline on every low-severity advisory in a dev dependency, which is not useful.

A practical CI configuration:

npm audit --omit=dev --audit-level=critical

This only fails the build if there are critical vulnerabilities in production dependencies. Adjust the level based on your risk tolerance.

For GitHub Actions:

- name: Security audit
  run: npm audit --omit=dev --audit-level=high

For a more detailed report without blocking the build:

- name: Security audit (informational)
  run: npm audit --omit=dev || true

- name: Security audit (blocking)
  run: npm audit --omit=dev --audit-level=critical

This prints the full audit report for visibility but only blocks on critical issues.

If your CI pipeline fails because of vulnerabilities that cannot be fixed yet (waiting for upstream patches), use the approaches in Fix 8 to temporarily allowlist them while tracking the upstream issue.

Still Not Working?

npm audit fix says “found 0 vulnerabilities” but npm audit still shows them

This happens when the fix requires a major version change that npm audit fix will not apply without --force. The audit report tells you the fixed version. Compare it with what is installed:

npm ls vulnerable-package

If the fix requires jumping from v1.x to v2.x of a transitive dependency, use overrides (Fix 4) to force the version.

Vulnerabilities keep reappearing after fixing

Your package-lock.json might be regenerating with the old versions. Make sure you commit the lockfile after fixing:

git add package-lock.json
git commit -m "Update lockfile with patched dependencies"

Also check that no .npmrc or npm config is pinning old versions.

npm audit shows vulnerabilities in packages you don’t use

Run npm ls package-name to see which dependency pulls it in. If it is a deeply nested transitive dependency from a package you no longer use, remove the parent:

npm uninstall unused-parent-package

If you are unsure which packages are actually used, depcheck can help:

npx depcheck

The advisory says “No fix available”

This means no patched version has been published. Your options:

  1. Check the package’s GitHub repository for open PRs or issues about the vulnerability.
  2. Use overrides to pin a fork or pre-release version if one exists.
  3. Replace the package with an alternative (Fix 7).
  4. If the vulnerability is not exploitable in your context, document and accept it (Fix 8).
  5. Open an issue or submit a PR to the upstream package to encourage a fix.

Environment variables cause false audit failures

If your CI runs behind a corporate proxy or uses a private registry, npm audit may fail with network errors rather than actual vulnerability findings. Make sure your registry is configured correctly:

npm config get registry

It should return https://registry.npmjs.org/ unless you are intentionally using a private registry. If you use a private registry, ensure it proxies the npm advisory API. See Fix: Environment variable is undefined if your CI environment variables are not being read correctly.

You want to audit without installing

You can run an audit against your package-lock.json without installing node_modules:

npm audit --package-lock-only

This is useful for quick checks in CI or when you want to assess the state of a project without downloading packages. If your lockfile is missing or corrupted, you will need to run npm install first. If npm fails during install with permission errors, see Fix: EACCES permission denied when installing npm packages globally.


Related: If npm install fails with dependency tree resolution errors after upgrading packages, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree. If a lifecycle script fails during the fix process, see Fix: npm ERR! code ELIFECYCLE.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles