Five major npm supply chain attacks hit in three weeks. Over 1,100 malicious package versions published. Credential harvesters, self-propagating worms, and a CI/CD bypass that beat provenance attestation.
I scored every compromised package through getcommit.dev — behavioral supply chain scoring that measures structural risk signals like publisher concentration, release patterns, and provenance.
Six out of seven packages scored below 75 before they were attacked. The only one that scored healthy was attacked through a completely different vector.
The timeline
| Date | Target | Malicious versions | Attack vector |
|---|---|---|---|
| Apr 30 | intercom-client | 1 | Compromised GitHub account → CI/CD publish |
| May 11 | TanStack (42 packages) | 84 | GitHub Actions cache poisoning + OIDC extraction |
| May 14 | node-ipc | 3 | Stolen npm credentials |
| May 19 | @antv ecosystem (323 packages) | 639 | Self-propagating worm (Shai-Hulud) |
| May 22 | 700+ GitHub repos | ~700 | Malicious postinstall hooks |
Over 1,100 compromised versions in 23 days. npm has never seen a month like this.
The scores
Every package scored through Commit's behavioral audit, using data available before each attack:
| Package | Score | Publishers | Downloads/wk | Provenance | Risk flag |
|---|---|---|---|---|---|
| size-sensor | 66 | 1 | 1.1M | No | HIGH |
| timeago.js | 67 | 2 | 260K | No | WARN |
| node-ipc | 69 | 1 | 900K | No | WARN |
| intercom-client | 69 | 1 | 381K | Yes | — |
| echarts-for-react | 71 | 1 | 1M | No | — |
| jest-canvas-mock | 72 | 2 | 2.6M | No | WARN |
| @tanstack/react-router | 91 | 5 | 16.8M | Yes | — |
Six packages scored between 66 and 72. One scored 91.
The six low-scoring packages were all compromised through credential theft — stolen npm tokens or compromised GitHub accounts. The one high-scoring package was attacked through a three-step CI/CD pipeline exploit that bypassed provenance attestation entirely.
What the low scores share
The six credential-theft targets have a consistent structural profile:
- 1-2 npm publishers. Every one. The attack surface is a single credential.
- No or stale releases. node-ipc hadn't published in 21 months. jest-canvas-mock: nearly 3 years. Dormant accounts are easier to steal.
- No provenance. Five of six had no OIDC attestation linking source to release. intercom-client had provenance, but the attacker compromised the GitHub account that the CI workflow ran under — provenance proved the malicious build came from CI, which was true.
This is the same pattern behind the LiteLLM attack (March 2026) and the axios compromise (March 30, 2026). Single publisher. Dormant or stale. No cryptographic chain of custody.
Why TanStack is the outlier
TanStack scored 91. Five publishers. Active development — last publish 3 days before the attack. Provenance enabled.
The attackers didn't steal anyone's credentials. They chained three GitHub Actions vulnerabilities:
-
pull_request_targetworkflow (lets fork code run with base repo permissions) - Cache poisoning across the fork → base trust boundary
- Runtime memory extraction of the OIDC token from the Actions runner
The malware published 84 versions across 42 packages in six minutes. It passed SLSA provenance checks. Every tool looking at cryptographic proof of origin said "legitimate."
Socket's AI scanner flagged the packages within six minutes of publication. But behavioral scoring couldn't have predicted this — the structural signals were all healthy.
The pattern is clear
Four out of five attack events used credential theft against structurally weak packages. One used a sophisticated CI/CD exploit against a structurally strong one.
Behavioral signals catch the common attack. Provenance attestation is supposed to catch the sophisticated one — but TanStack proved it can be bypassed when the CI environment itself is compromised.
No single signal catches both. But the common attack — the one that keeps repeating, month after month — has a structural signature that's visible before it happens.
The uncomfortable count
There are 26 npm packages with over 10 million weekly downloads and a single npm publisher. Every one shares the structural profile of the packages attacked this month:
minimatch — 593M/wk, 1 publisher CRITICAL
chalk — 410M/wk, 1 publisher CRITICAL
glob — 346M/wk, 1 publisher CRITICAL
cross-spawn — 206M/wk, 1 publisher CRITICAL
zod — 181M/wk, 1 publisher CRITICAL
These aren't in your package.json. They're in your lock file.
Check your own tree
npx proof-of-commitment --file package-lock.json
Scans your full dependency tree and flags the structural risks that npm audit doesn't look at.
getcommit.dev/audit — paste packages, see scores. No install.
Data sources: Socket.dev incident reports, npm registry, getcommit.dev behavioral scoring. All scores use data available prior to each incident.
Top comments (0)