On September 15, 2025, the npm registry experienced its first true self-propagating supply-chain worm. Researchers named it Shai-Hulud after the string the malware embedded in its TruffleHog scan output. By the time it was contained, the worm had compromised hundreds of packages — including ones maintained by CrowdStrike employees — and had silently published thousands of stolen secrets to public GitHub repositories named 'Shai-Hulud Migration'.
How the worm worked
The infection started with a single compromised maintainer account, almost certainly via a phishing kit targeting npm 2FA codes. Once the attacker had publish rights to one package, the postinstall script of the malicious version did three things in sequence.
- Ran TruffleHog locally to scrape secrets from the developer's machine — env files, AWS credentials, GitHub tokens, npm tokens.
- Used any discovered npm token to publish trojanized versions of every other package the maintainer owned, propagating sideways across the registry.
- Created a public GitHub repo on the victim's account and pushed the harvested secrets there, ensuring durable exfiltration even if the npm side was rolled back.
Why it spread so fast
The trick that made Shai-Hulud different from past npm incidents was the lateral movement. Most prior attacks compromised a single package and waited for downloads. This worm actively jumped to every package the victim could publish — turning one stolen credential into hundreds of poisoned releases within hours.
It also had a quiet patient mode. Several infected versions were published as patch releases of widely-used libraries, where automated dependency bots would cheerfully merge them into downstream projects before any human noticed.
How to audit your project today
- Pin the exact versions you trust. Replace any caret or tilde ranges in package.json with the exact version strings from your last known-good lockfile.
- Re-run `npm audit` AND check against the GitHub Advisory Database — the npm advisory feed lagged by days during the incident.
- Disable postinstall scripts in CI by default with `npm config set ignore-scripts true`. Whitelist the few packages that genuinely need them.
- Rotate any token that lived on a developer machine touching npm during September 2025. Don't trust 'I think we're fine' — rotate.
- Audit your GitHub for unexpected public repos, especially ones named with random words or 'Migration' in the title.
Longer-term hardening
The lessons here aren't new — they're the same ones repeated since event-stream in 2018 — but the worm raised the cost of ignoring them. If you maintain any npm package, enable required 2FA for publish, use trusted publishing via OIDC where possible, and treat every postinstall script as a potential attack vector. If you only consume packages, lockfiles plus disabled scripts plus credential rotation are the high-leverage defaults.
There will be another worm. Probably this year. The blast radius is determined entirely by what you've already automated.
