npm Under Siege: Two Coordinated Supply Chain Campaigns Demand Immediate Response
The Attack Surface
Two supply chain campaigns hit npm this week. Combined reach: 253 package names across two vectors. Both go after CI/CD secrets.
Campaign 1: TanStack ("Mini Shai-Hulud") — the attacker chained GitHub Actions vulnerabilities to exfiltrate npm publish credentials, then shipped 84 malicious versions across 42 packages. TanStack Query, Router, and Table sit in the dependency tree of most React and Vue frontends. Weekly downloads across the affected set exceed 12 million.
Campaign 2: Bun Worm — a separate Bun-based worm exploiting optionalDependencies with prepare hooks. 169 package names, 373 versions, including packages in the Mistral AI and Tanstack ecosystems. The payload runs during npm install without showing up in the primary dependency tree. It ships GitHub tokens, npm tokens, CI secrets, and cloud credentials out.
Trusted publishing provides zero protection because compromised workflows can mint legitimate tokens. Your lockfile hash will match. Your provenance check will pass. The package is 'trusted.'
Why This Is Different
The TanStack vector is GitHub Actions itself, not a maintainer credential. 2FA and key rotation do nothing here. The Bun worm uses optionalDependencies, so the malicious code runs without appearing in any direct dependency. Naive package.json audits will not see it.
Both campaigns want the same thing: every environment variable readable by the install process. In CI, that is everything. NPM_TOKEN, AWS_*, GITHUB_TOKEN, OIDC credentials, mounted .env files. The payload needs no sophistication. It needs one green build.
Cross-Source Consensus on Response
Five independent sources converge on the same response order:
- Grep lockfiles for TanStack entries and the 169 Bun worm package names. Diff against pre-May-11 resolutions.
- Rotate all secrets accessible to any CI runner that resolved during the compromise window. Start with tokens that can push packages or deploy.
- Pin affected packages to known-good versions by SHA, not range.
- Invalidate every npm cache and reinstall from pinned versions.
- Audit build logs for the window the bad versions were resolvable.
Structural Fixes This Sprint
The ecosystem fix is npm provenance with OIDC. The repo-level fix today:
- Pin all GitHub Actions to commit SHAs, not tags
- Declare
permissions: {}at workflow level, grant minimum per job - Run
npm installwith--ignore-scriptswhere possible, or in network-isolated CI - Maintain a strict allowlist of packages permitted to run install scripts
- Split the CI identity that installs dependencies from the one that holds production secrets
One source notes: "the next post-mortem will read exactly like this one, with a different name in the headline." This is a registry trust-model failure, not a TanStack failure of craft.
What to do
Audit all lockfiles for TanStack and Bun worm package names today — cross-reference against published IOC list from Aikido report
Rotate ALL secrets accessible to CI runners that resolved dependencies since May 11
Pin all GitHub Actions to commit SHAs and add permissions: {} at workflow level this sprint
Implement install-time network isolation for npm in CI (block egress except to your registry)