Why npm audit Is Broken and What We Built Instead
Good, I've got the voice and format dialed in. Here's the post:
Why npm audit Is Broken and What We Built Instead
Run npm audit on any non-trivial project and you'll get a wall of red. Critical vulnerabilities. High severity. Dozens of them.
Now go read the actual advisories. That "critical" in nth-check? It's a regex DoS in a CSS selector engine used by your test runner. Never touches production. That "high" in semver? It's in a transitive dependency of your linter's linter. Your users will never execute that code path.
npm audit has trained an entire generation of developers to mass-click "ignore." And that's the actual security vulnerability.
The Numbers Are Worse Than You Think
Dan Abramov spent hours reviewing every npm audit issue filed against Create React App. Every. Single. One. The result? A 100% false positive rate. Not one real vulnerability was found through npm audit across years of reports and thousands of comments.
This isn't an edge case. Aikido Security's 2026 report found 65% of development teams admit to bypassing or delaying fixes because of alert noise. Two-thirds of your industry has learned to ignore the fire alarm because it goes off every time someone makes toast.
Meanwhile, the real fires are burning. 454,648 malicious packages were published to npm in 2025 alone. The Lazarus Group had 800+ packages on the registry. The Chalk/Debug hijack hit 2.6 billion weekly downloads. The Axios compromise in March 2026 was a North Korean state actor injecting a RAT into a package with 100M+ weekly downloads.
npm audit caught zero of these. Not one.
Why It's Broken by Design
npm audit does exactly one thing: it checks your dependency tree against the GitHub Advisory Database for known CVEs. That's it. No behavioral analysis. No reachability checking. No distinction between production dependencies and dev tooling.
Here's the part that should make you angry. Rebecca Turner, who helped create npm audit, admitted that npm's need for revenue shaped the tool's design. The vulnerability counts were inflated by reporting "the number of things ever depending on the vulnerable module" rather than actual vulnerable modules. The numbers were designed to look alarming so you'd pay for fixes.
$ npm audit
# 47 vulnerabilities (12 moderate, 28 high, 7 critical)
$ npm audit --production
# 2 vulnerabilities (1 moderate, 1 high)
That --production flag helps, but it still can't tell you whether vulnerable code is actually reachable from your application. A CVE in a function you never call is not a vulnerability in your software.
The biggest JavaScript frameworks already gave up. Vite and Next.js now bundle their dependencies directly into their packages instead of using node_modules -- specifically to avoid triggering bogus npm audit warnings. When your framework authors engineer around your security tool, that tool is done.
PackageGate Made It Worse
In January 2026, researchers at Koi found 6 zero-days across npm, pnpm, vlt, and Bun. The attack? RCE even with --ignore-scripts enabled. The defense everyone told you to use -- "just disable lifecycle scripts" -- was broken.
pnpm patched within weeks. vlt patched. Bun patched.
npm closed the report as "works as expected."
Read that again. A remote code execution vulnerability that bypasses the primary mitigation the ecosystem relies on. Works as expected.
# The defense you were told would protect you:
npm install --ignore-scripts
# What PackageGate proved:
# This doesn't prevent all code execution paths.
# Packages can still run code through bin scripts,
# node_modules/.hooks, and other vectors npm "won't fix."
What Actually Works
There's no single tool that replaces npm audit. The threat surface is too wide. You need layers, and each layer catches what the others miss.
Layer 1: Know What You're Actually Running
Before you scan for vulnerabilities, you need to know what's in your dependency tree and whether it's doing anything suspicious. This is where supply chain scanners come in.
# Scan your lockfile for known vulnerabilities AND malicious packages
$ vekt scan
Scanning package-lock.json...
12,847 packages across 1 lockfile
2 advisories found
GHSA-xxxx-yyyy moderate lodash.template < 4.5.0
Fixed: 4.5.0 | Reachable: unknown
MAL-2026-1234 malware event-stream-alt 2.0.1
Malicious package | Action: remove immediately
12,845 packages clean
Vekt scans against OSV.dev for both CVEs and MAL-* malicious package advisories across 12 ecosystems -- npm, PyPI, crates.io, Go, RubyGems, and more. It covers 22 lockfile formats from a single CLI. The browser extension shows trust badges directly on npm package pages so you can check before you install.
The distinction matters. npm audit only checks CVEs. It has no concept of a MAL advisory -- a package that was published with malicious intent. When event-stream got compromised, npm audit had nothing to say about it until someone manually filed a CVE days later.
Layer 2: Behavioral Analysis at Install Time
Socket wraps npm install and monitors for suspicious behavior: obfuscated code, unexpected network calls, filesystem access, shell execution. When the Chalk/Debug hijack hit in September 2025, Socket users were protected the entire time it was live. npm audit users had no idea anything was wrong.
# Socket wraps your install command
$ socket npm install some-package
# If something looks wrong:
# some-package@2.1.0
# Network access detected (outbound to 185.xx.xx.xx)
# Obfuscated code in postinstall script
# Block install? [Y/n]
Layer 3: Reachability Analysis
This is the big one. Reachability analysis traces call graphs to determine if vulnerable code is actually executed by your application. Tools like Endor Labs and Contrast SCA report that this cuts alert volume by 70-90%. That's not a small improvement -- that's the difference between "47 critical alerts" and "2 things you actually need to fix."
Layer 4: Lockfile Pinning and CI Gates
Pin your dependencies. Review lockfile diffs. Run scans in CI, not just on your laptop.
# .github/workflows/supply-chain.yml
name: Supply Chain Check
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check lockfile for threats
run: |
curl -sSL /blog/vekt/install.sh | sh
vekt scan --format sarif > results.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
The CI gate is where you catch things before they hit production. Scan on every PR. Block merges on malware findings. Warn on CVEs but let your team triage based on reachability, not raw severity scores.
The Real Problem Is Cultural
npm audit created a security theater that made the JavaScript ecosystem measurably less safe. Developers learned to ignore warnings. Teams added npm audit || true to their CI pipelines. The Shai-Hulud worm spread across 500 packages and 25,000 repositories while developers were busy clicking "dismiss" on regex DoS warnings in their test frameworks.
The fix isn't switching to a different single tool. It's building a pipeline that separates signal from noise:
- Malware detection (Vekt, Socket) catches packages published with malicious intent
- Behavioral analysis (Socket) catches compromised legitimate packages
- Reachability analysis (Endor Labs, Contrast) tells you which CVEs actually matter
- Lockfile auditing (Vekt in CI) catches threats before they reach production
npm audit can stay in the stack as a baseline CVE check. Just stop pretending it's a security strategy.
Your supply chain is only as strong as the weakest signal you're willing to ignore. Stop ignoring all of them because one tool cried wolf 47 times.
meta_title: "Why npm audit Is Broken and What We Built Instead"
meta_description: "npm audit has a 99%+ false positive rate and misses actual malware. Here's a layered supply chain security approach that catches real threats."
tags: ["npm security", "supply chain", "JavaScript", "Vekt", "dependency scanning"]