What Happens When You Scan Every npm Package for Malicious Code
What Happens When You Scan Every npm Package for Malicious Code
In 2025, 454,648 malicious npm packages were published. The registry has about 10 million packages total. That means roughly 4.5% of everything published last year was weaponized.
You are not downloading from a library anymore. You are downloading from a warehouse where 1 in 22 boxes might be a bomb.
Sonatype reports that over 99% of open-source malware now targets npm specifically. They blocked 120,612 malware attacks in Q4 2025 alone. And those are just the ones anyone caught.
The scale nobody prepared for
An arXiv paper from 2025 analyzed 31,000+ npm vulnerabilities from 2017 through 2025. The biggest category was not memory bugs. Not auth bypasses. Not prototype pollution.
Embedded malicious code: 48.58% of all npm vulnerabilities. Malicious package reports jumped from 38 in 2018 to 2,168 in 2024. That is a 57x increase.
Then 2025 happened.
In Q4 2025, a campaign called IndonesianFoods published over 100,000 malicious packages in a few days. One every seven seconds. It doubled all historical npm malware volume in a single operation. While you are reading this sentence, three more got shipped.
Shai-Hulud 2.0 (November 2025) showed what industrialized self-replication looks like:
- 796 unique packages backdoored
- 20 million weekly downloads affected
- 25,000+ malicious GitHub repos created
- ~14,000 secrets exposed across 487 organizations
- Wiz measured it publishing ~1,000 packages every 30 minutes at peak
This is not a supply chain risk. It is a supply chain worm.
The 6-minute story you should actually be afraid of
On March 31, 2026, two versions of axios shipped with a working Remote Access Trojan. Axios has over 100 million weekly downloads. The maintainer account was hijacked through a phishing campaign spoofing npm's MFA flow.
Here is the part that matters. If you ran npm audit that morning:
$ npm audit
found 0 vulnerabilities
Zero. Because npm audit compares your deps against a known-vulnerability database. A zero-day malicious release from a legitimate maintainer is not in any database yet.
Socket's scanner flagged the seeded transitive dependency plain-crypto-js@4.2.1 within six minutes of publication. npm audit never had anything to say. That is the gap.
npm audit is a CVE lookup dressed up as a security tool. For modern npm attacks, it is theater.
Typosquatting is the old threat
For years, attackers waited for developers to mistype package names. lodsash instead of lodash. reqeust instead of request. You trained your team to read carefully and it mostly worked.
Naming patterns still matter. 71.2% of malicious npm packages use names over 10 characters. 67.3% include dashes. They mimic legitimate naming on purpose.
Example from February 2026: amber-src impersonated ember-source (11M weekly downloads). Removed within 5 hours. Downloaded about 50,000 times before the takedown. The malware ran in a preinstall hook, meaning it executed before npm install even finished printing "added 247 packages."
But typosquats are the background noise now. The current threat is your dependencies getting hijacked while you sleep. The package name does not change. The maintainer does not change. The malicious version just ships on a Tuesday.
Why scanning the whole registry will not save you
"Scan every package" sounds like the answer. It is not.
Endor Labs made the point cleanly: many 2025 packages stayed dormant at install time. Traditional scanners found nothing because there was nothing to find yet. The payload activated later, based on a timer or a C2 check-in.
A RAID 2025 study tested LLM-based malware detectors on npm. They scored F1 of 0.99 on "is this malicious, yes or no." But ask them to point at the actual malicious indicator? Performance degraded by ~41%. The detector could smell the smoke but not find the fire.
When attackers publish at 7-second intervals, detection is losing the math. The winning math is pre-install gating.
What actually works
Your typical project declares about 40 packages in package.json. It pulls in around 600 transitive dependencies. You cannot audit 600 packages. Nobody can.
You can, however, stop running arbitrary code from strangers at install time.
Step 1: Lockfile pinning and npm ci
# Never do this in CI
npm install
# Always do this in CI
npm ci
npm ci refuses to install anything that does not match package-lock.json exactly. No silent upgrades. No sneaky minor bumps. If the lockfile is wrong, the build fails.
Step 2: Kill install scripts
# One-off
npm install --ignore-scripts
# Project-wide, in .npmrc
ignore-scripts=true
About half of npm malware fires from preinstall, install, or postinstall hooks. Disabling them does not fix everything, but it breaks the most common infection path. You can re-enable per package if you actually need node-gyp or similar.
Step 3: Scan lockfiles, not just CVEs
This is where npm audit falls over and where Vekt picks up.
$ vekt scan .
Found 221 lockfiles, 6 ecosystems
Scanned 69,419 packages
CRITICAL: plain-crypto-js@4.2.1 (MAL-2026-0311)
transitive of: axios@1.8.0
path: package-lock.json
HIGH: amber-src@1.0.1 (MAL-2026-0042)
typosquat of: ember-source
path: package-lock.json
Vekt reads 22 lockfile formats across 12 ecosystems (npm, PyPI, crates.io, Go, RubyGems, Packagist, NuGet, Pub, SwiftURL, Hex, Hackage, CRAN). It checks packages against OSV.dev for both CVEs and MAL-* malicious package advisories. The MAL feed is where npm zero-day malware shows up, often within hours of disclosure.
It is free for 50 scans a day. No signup.
Step 4: Rotate secrets when a compromised package ran
If a bad package executed on a dev machine or a CI runner, treat that machine as compromised. Not "might be." Is. Shai-Hulud harvested secrets from ~/.npmrc, ~/.aws/credentials, GitHub tokens, environment variables, anything it could read.
Rotate:
- npm auth tokens
- GitHub personal access tokens and deploy keys
- AWS, GCP, Azure credentials on that runner
- Every environment variable in your CI/CD
- Anything the compromised process had permissions to read
"We ran npm install and it pulled a RAT" is a security incident, not a bad afternoon.
The agency math
For a small agency, the cheapest meaningful defense costs a few engineering hours:
npm ciin CI instead ofnpm installignore-scripts=truein.npmrc- A lockfile scanner in the CI pipeline
- A documented token-rotation runbook
That is it. No SOC, no vendor call. The teams getting hit in 2026 are the ones still running npm audit in CI and calling it a security gate.
If you bill a client for building their site, and you ship them 600 transitive dependencies you have never scanned for malicious code, that is now a liability conversation, not a hygiene one.
The direction of travel
In 2023 we called this "supply chain risk." In 2026 we call it "supply chain worm." The language shift matters. A risk is a possibility. A worm is a process that is already running.
Three Shai-Hulud variants shipped between September and December 2025. Axios got hit in March 2026. Researchers are openly warning about a fully weaponized multi-platform credential worm as the next step. The attacker playbook is published. The defender playbook is lockfile pinning, script disabling, and real scanning.
Scan your lockfiles. Pin your dependencies. Rotate your tokens. If you want a scanner that reads 22 lockfile formats and checks OSV.dev for malicious packages, Vekt runs locally and does not phone home.
The registry is not going to get safer. Your pipeline can.
meta_title: "What Happens When You Scan Every npm Package"
meta_description: "454k malicious npm packages shipped in 2025. Why npm audit misses them, what actually works, and how to scan your lockfiles in seconds."
tags: ["npm", "supply-chain-security", "vekt", "malware", "devops"]