How to find outdated npm dependencies with known vulnerabilities
Par Seaworthy · 1 mai 2026
The problem with "just update everything"
Every Node.js project I've seen in the wild has the same quiet problem: a package-lock.json full of transitive dependencies that nobody consciously chose, many of which haven't been updated in months. Some of them have CVEs. Nobody noticed because nothing broke.
The advice you'll find most often is "run npm update and you're done." That's not wrong, but it's incomplete. Knowing which packages are outdated is different from knowing which outdated packages are actually dangerous. Conflating the two leads to either panic-updating everything (and breaking things) or ignoring the output entirely (and shipping vulnerabilities).
This article is about the distinction.
Start with what npm already gives you
npm ships two relevant commands out of the box. Most developers know about one of them.
npm outdated lists every package where a newer version exists. It shows the current version, the wanted version (the highest version satisfying your semver range), and the latest version on the registry.
npm outdated
Sample output:
Package Current Wanted Latest Location
express 4.17.1 4.17.3 4.21.2 node_modules/express
lodash 4.17.20 4.17.21 4.17.21 node_modules/lodash
@babel/core 7.18.0 7.18.13 7.24.7 node_modules/@babel/core
The "Wanted" column is what npm update would install. "Latest" is what actually exists. The gap between them is where semver ranges get in the way: if your package.json says "express": "^4.17.1", npm update won't jump you to 4.21.2 unless you change the range.
The second command is npm audit:
npm audit
This checks your installed dependency tree against the npm advisory database and reports CVEs, their severity, and whether a fix exists. It is not the same as npm outdated. A package can be outdated without being vulnerable. A package can also be vulnerable while sitting at its latest version (though that's rarer and usually fixed quickly).
Run both. They answer different questions.
Making sense of npm audit output
npm audit produces a lot of noise. Here is how I think about it.
Critical and high severities need your attention first. Read the advisory. Click the CVE link. Ask: does my application actually exercise the vulnerable code path? A ReDoS vulnerability in a markdown parser matters if users can submit arbitrary markdown. It matters much less in a build tool that only processes your own source files at deploy time.
Moderate and low severities are context-dependent. Don't ignore them entirely, but don't block a release over a low-severity issue in a dev dependency.
npm audit --production is underused. It filters out dev dependencies, which shrinks the noise considerably for anything that ends up in your deployed bundle.
npm audit --production
If you want machine-readable output for CI:
npm audit --json | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical" or .value.severity == "high") | .key'
That pipes the JSON audit report through jq and prints only the package names with critical or high vulnerabilities. Pipe it into your CI failure logic from there.
When npm audit fix isn't enough
npm audit fix applies semver-safe fixes automatically. npm audit fix --force goes further and will break semver ranges to get to a non-vulnerable version. I'm genuinely wary of --force in production codebases without a solid test suite. It will sometimes upgrade a major version and silently change an API your code depends on.
When npm audit fix reports that a fix is not available, or that fixing requires a breaking change, you have a few options:
- Wait for the upstream maintainer to patch it (check the GitHub issues first, it may already be in progress).
- Pin a specific version in
package.jsonto a known-safe release using anoverridesfield (npm v8.3+):
"overrides": {
"vulnerable-transitive-dep": ">=2.1.4"
}
- Evaluate whether you can replace the dependency entirely.
- Accept the risk with documented justification if the attack surface genuinely doesn't apply to your use case.
That last option is legitimate. A static site generator with a vulnerable server-side rendering library might not serve any user-controlled content at all. Document the decision. Set a reminder to revisit it.
Beyond npm: tools worth knowing
npm outdated and npm audit cover the basics, but they have blind spots.
Snyk (npx snyk test) cross-references more advisory databases than npm's own registry and often catches vulnerabilities earlier. It also does reachability analysis on some vulnerability types, which reduces noise.
socket.dev takes a different angle: it looks for supply chain risks like newly published packages with install scripts, typosquatting patterns, and suspicious maintainer activity. Useful if you're worried about the attack surface beyond known CVEs.
Dependabot (built into GitHub) opens pull requests automatically when a new version drops. I have mixed feelings about it because the PRs can pile up fast, but for security fixes it's a net positive.
None of these replace reading the advisories yourself for anything critical.
What this looks like in CI
A minimal security gate for a Node.js project:
npm ci
npm audit --production --audit-level=high
The --audit-level=high flag makes the command exit with a non-zero code only if high or critical vulnerabilities are found. That means your pipeline fails on things that matter without being blocked by every moderate advisory in a transitive dependency five levels deep.
Add npm outdated to your CI output as informational logging rather than a gate. Knowing packages are outdated is useful data. Failing the build on every outdated package is a fast track to developers disabling the check entirely.
The thing nobody mentions
Dependency hygiene is a maintenance problem, not a one-time fix. Running npm audit once is not the same as running it continuously. The advisory database gets new entries every week. A package that was safe in January might have a disclosed CVE in March.
The only durable solution is a regular cadence: audit on every CI run, review the results periodically, and prune dependencies you no longer need. Fewer dependencies means fewer things to audit.
Seaworthy's dependency hygiene checks flag outdated packages with known CVEs as part of its broader static analysis scan, alongside security, configuration, and resilience issues.
Cet article a été généré par IA et résume des sources publiquement disponibles.
Seaworthy analyse votre dépôt pour détecter les problèmes abordés dans des articles comme celui-ci.
Failles de sécurité, secrets exposés et mauvaises configurations détectés avant le déploiement. Gratuit, aucun compte requis.