Safeguard
Tutorials

How to Audit npm Dependencies for Vulnerabilities

Go beyond npm audit's noisy output — resolve your dependency tree, prioritize by reachability, and fix both direct and transitive vulnerabilities in a Node.js project the right way.

Daniel Osei
DevSecOps Engineer
5 min read

Every Node.js project is mostly other people's code. A modest app resolves to hundreds or thousands of transitive packages, and any of them can carry a known vulnerability. The built-in npm audit is a start, but it tends to produce a long, flat, unprioritized list — and it says nothing about whether a vulnerable function is actually reachable from your code. That noise is why so many teams run npm audit, sigh, and move on. This guide shows how to audit your npm dependencies in a way that tells you what to fix first and how to fix it without breaking your build.

Prerequisites

  • A Node.js project with a committed package-lock.json (or pnpm-lock.yaml / yarn.lock).
  • Node.js and npm installed locally.
  • The Safeguard CLI installed: curl -sSfL https://get.safeguard.sh/install.sh | sh.

Step 1: Establish a baseline with npm audit

It is worth seeing the raw signal first:

npm audit --omit=dev

Note the total count and severities. Keep this number — you will compare it against a prioritized view in a moment, and the gap is the point.

Step 2: Run a reachability-aware scan

Resolve the full tree and cross-reference it against advisory data, adding reachability analysis so you know which findings touch code your app actually runs:

sg scan --ecosystem npm

The output annotates each finding with whether the vulnerable code path is reachable:

Resolved 1,046 npm components
  findings:
    critical: 1  (1 reachable)
    high:     6  (2 reachable)
    medium:   18 (0 reachable)
  [CRITICAL reachable] CVE-2024-21538 — cross-spawn 7.0.3 — RCE
      imported via: app/spawn.ts → execa → cross-spawn
  [HIGH  not reachable] CVE-2023-26136 — tough-cookie 4.1.2

The three reachable findings are your real work; the rest can wait behind a policy expiry.

Step 3: Inspect and explain a specific finding

Before changing a version, understand the fix:

sg scan finding CVE-2024-21538 --explain

You get the advisory, the exact import chain that pulls the package in, the first fixed version, and the recommended remediation command.

Step 4: Fix direct dependencies

If the vulnerable package is a direct dependency, bumping it is straightforward:

npm install cross-spawn@^7.0.5

Re-resolve and confirm the finding is gone:

sg scan --ecosystem npm --fail-on high

Step 5: Fix transitive dependencies with an override

Most vulnerable packages are transitive — pulled in by something you do depend on. You cannot npm install them directly, but you can force a resolved version with the overrides field in package.json:

{
  "overrides": {
    "cross-spawn": "^7.0.5"
  }
}

Then reinstall so the lockfile picks up the pinned version:

npm install
sg scan --ecosystem npm --fail-on high

Verify the result

Confirm the audit is clean and the fixes are locked in:

# No high-or-above findings remain
sg scan --ecosystem npm --fail-on high

# The override is reflected in the lockfile
npm ls cross-spawn

Compare the reachable-finding count now to your Step 1 npm audit total. The list you must act on should be dramatically shorter — that is prioritization doing its job.

How Safeguard streamlines this

npm audit tells you what is vulnerable; it does not tell you what is exploitable in your app or how to fix a transitive package without guesswork. Safeguard's SCA adds reachability analysis so a critical CVE in an unreached code path is ranked below a high-severity one your entrypoint actually calls — turning a thousand raw alerts into a short, ordered worklist. The CLI resolves npm, pnpm, and yarn lockfiles with the same engine your CI uses, so local and pipeline results always match. For transitive fixes, Auto-Fix computes the correct override or upgrade, applies it, runs your tests, and opens a pull request — so remediating a nested dependency is a review-and-merge instead of an afternoon of npm ls archaeology. See how it scales on the pricing page.

Ready to audit your project? Connect a repository at app.safeguard.sh/register.

Frequently Asked Questions

Is npm audit good enough on its own? It is a reasonable free baseline for detection, but it presents findings as a flat list without reachability context and often recommends fixes that require breaking major-version bumps. For a project of any size, that lack of prioritization means real, exploitable issues get buried under low-risk noise.

What does reachability mean for an npm vulnerability? Reachability answers whether the specific vulnerable function in a package is actually invoked along a code path your application executes. A CVE in a package you install but never call the affected part of is far lower risk than one on your request-handling path, and reachability is what lets you tell those apart.

How do I fix a vulnerability in a transitive dependency? Use the overrides field in package.json (or resolutions in Yarn) to force the resolved version of the nested package to a patched release, then reinstall so the lockfile updates. This works even when the direct dependency that pulls it in has not yet released an update.

Should I run the audit against dev dependencies too? Audit them, but weigh them differently. Dev dependencies do not ship to production, so a vulnerability there is lower-risk than one in a runtime dependency — though build-time supply chain attacks make it unwise to ignore them entirely. Separating the two lets you prioritize runtime issues first.

Why does my lockfile still show the old version after I ran npm install? Either the override was not added correctly, or another dependency constrains the package to the older range. Run npm ls <package> to see every version in the tree and which parent requires it; the CLI's finding explanation shows the same import chain so you can pinpoint the constraint.


Explore SCA, the Safeguard CLI, and Auto-Fix, or compare plans on the pricing page. Full ecosystem coverage is documented in the Safeguard docs.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.