Postmortem: TanStack NPM supply-chain compromise
Attack and root cause
- Worm (“Mini Shai-Hulud”) compromised legitimate npm packages by:
- Exploiting GitHub Actions via
pull_request_targetplus cache poisoning. - Writing a malicious pnpm store into the shared Actions cache from a fork PR.
- Having the main/release workflow later restore that poisoned cache and publish malicious tarballs with valid SLSA provenance.
- Exploiting GitHub Actions via
- Payload steals GitHub tokens and installs a “dead-man’s switch” user service / LaunchAgent that
rm -rf ~if the stolen token is revoked. - Attack also hit other popular npm packages and is spreading downstream; impact is still evolving.
GitHub Actions & Trusted Publishing concerns
pull_request_targetis widely criticized as a “landmine”; docs warn against running untrusted code with it, but it remains easy to misuse.- Core design issue: Actions cache scope is per-repo and shared between untrusted PR runs and protected main/release runs.
- Permissions model is confusing; workflows that appear “read-only” can still write caches.
- Trusted Publishing removes long‑lived registry tokens but doesn’t add a second factor; if CI or a repo admin token is compromised, an attacker can still publish.
- Some argue additional gates (environments, manual approvals) help; others note admin tokens can often bypass those via API.
Defenses and mitigations discussed
- Common recommendations:
- Enable minimum release age / cooldowns in npm, pnpm, bun, yarn, uv, pip.
- Commit lockfiles, use
npm ci/pnpm --frozen-lockfile, and pin exact versions. - Disable or tightly control lifecycle scripts (
ignore-scripts, pnpm/bun defaults,allow-git=none). - Separate caches for PR vs release; or forbid untrusted workflows from writing cache.
- Add static analysis for Actions (e.g., tools that detect
pull_request_target+ checkout patterns).
- Some advocate publishing from isolated pipelines or separate projects, and using VMs or stronger sandboxing for dev environments.
Ecosystem and dependency culture debate
- Many see npm/JS as uniquely bad: massive transitive dependency graphs, lifecycle scripts by default, frequent supply‑chain incidents.
- Others argue any ecosystem with unaudited third‑party packages is vulnerable; npm is just the biggest target.
- Discussion of alternatives: Go modules, Cargo, Java/Maven, Python, distro packaging, larger standard libraries, or “just write the code yourself.”
Broader reflections
- Tension between “audit dependencies” vs cost and complexity; some now pay for AI audits or avoid upgrades entirely.
- Frustration that registry unpublish rules and slow security response leave malicious versions installable for hours.
- Growing sentiment that current CI/package manager defaults are unsafe and need stronger, opinionated security-by-default.