Skip to main content
TanStack's npm Meltdown: Mini Shai-Hulud Worm Hits 42 Packages
⚙️ CI/CD

TanStack's npm Meltdown: Mini Shai-Hulud Worm Hits 42 Packages

// TIME: 8 min read AUTH: Richard Soutar
tanstacksupply-chainsecuritygithub-actionscicddevops

May 11, 2026. 19:20 UTC. While most of us were debating whether to use use or queryClient for the 47th time, TanStack’s release pipeline quietly shipped 84 malicious versions across 42 @tanstack/* packages.

Yes, the same TanStack Router family that powers half the modern React (and Solid/Vue/Svelte) apps just became a self-spreading credential-stealing worm nicknamed Mini Shai-Hulud (because nothing says “fun supply-chain attack” like a baby sandworm from Dune).

In six glorious minutes the attacker published under TanStack’s own trusted OIDC identity. No stolen npm token. No compromised maintainer account. Just pure GitHub Actions dark magic.

If you ran npm install, pnpm install, or yarn install on any affected package that day… your machine is now a potential credential farm. Welcome to the club.

What Actually Happened? (The Technical Horror Story)

TanStack’s own postmortem is a masterpiece of transparency (go read it), but here’s the distilled, slightly-less-corporate version:

  1. The Pwn Request (pull_request_target magic)
    Attacker forks TanStack/router, renames it to something boring to hide in fork lists, and opens a PR titled “WIP: simplify history build”.
    TanStack’s bundle-size.yml workflow used on: pull_request_target (the classic “trust me bro” trigger). It happily checked out the merge commit of the fork and ran attacker code on the base repo’s runner.

  2. Cache Poisoning Party
    The malicious PR dropped a sneaky vite_setup.mjs that wrote a poisoned pnpm-store entry using the exact cache key the real release.yml workflow would later restore (Linux-pnpm-store-6f9233a5…).
    GitHub Actions cache doesn’t care about your fork trust boundaries. Poisoned cache saved. Boom.

  3. OIDC Token Heist from Runner Memory
    Later that evening a legit maintainer PR merges → release.yml runs.
    Poisoned cache restores → malware wakes up, finds the GitHub Actions worker process via /proc/*/cmdline, dumps its memory (/proc/<pid>/mem), and extracts the freshly minted OIDC token (because the workflow had id-token: write).
    Then it POSTs straight to registry.npmjs.org — bypassing the actual “Publish Packages” step entirely.

Result? 42 packages (mostly router family, devtools, adapters, history, etc.) got two malicious versions each. @tanstack/query*, @tanstack/table* and friends stayed clean. Small mercies.

The malware itself? A 2.3 MB obfuscated router_init.js (hidden in the tarball, not even listed in files). It runs at install time via a cheeky optionalDependencies hack pointing to an orphan commit in a fork. The prepare script fires, does its thing, then politely exit 1 so the install “succeeds” anyway.

What the Worm Actually Did (And Why You Should Panic)

Once installed it went full supply-chain gremlin:

  • Scanned for AWS creds, GCP metadata, K8s service accounts, Vault tokens, ~/.npmrc, GitHub tokens, SSH keys — you name it.
  • Exfiltrated everything over the Session/Oxen messenger network (E2E encrypted, no obvious C2 server for defenders to block).
  • Self-propagated by querying npm for other packages the victim maintained and republishing them with the same payload.
  • Left persistence goodies in ~/.vscode and VS Code settings for good measure.
  • Bonus dead-man’s switch: if tokens get revoked it could nuke your home directory (because why not go full villain?).

518+ million cumulative downloads across the broader campaign. Oof.

The Humour in the Chaos (Because Otherwise We’d Cry)

My favourite part? The attacker didn’t even need to steal long-lived secrets. They just borrowed the pipeline’s own lunch money while it was looking the other way.

It’s like watching someone pick the lock on your front door using the spare key you left under the welcome mat… that you yourself programmed the house to hand out.

Also: “Single pane of glass? More like single point of worm.”

And yes, the irony of a routing library getting pwned by a PR is chef’s kiss.

Lessons for Us Mere Mortals (CI/CD Takeaways)

  1. Audit your pull_request_target workflows yesterday — add repository_owner guards, require approval for first-time contributors, or just stop using it for anything that touches cache or secrets.
  2. Cache is not sacred — isolate caches per workflow, per branch, or use immutable cache keys. Consider disabling cache for release jobs entirely if you’re feeling paranoid.
  3. OIDC tokens are powerful but lazy — they sit in memory. Memory scraping is a real thing now. Rotate aggressively and monitor for anomalous publishes.
  4. Sign your releases properly — provenance is nice, but when the attacker publishes through your pipeline it’s still trusted. Add package cooldowns, manual approval gates for major versions, or external signing.
  5. Treat npm install as untrustedignore-scripts in CI, lockfile audits, SBOMs, and never run npm install on production runners without verification.
  6. Monitor your own publishes — TanStack had zero internal alerting on sudden npm version spikes. Add it. Now.

TanStack already hardened everything: purged caches, restructured workflows, pinned actions to SHAs, revoked permissions, and deprecated the bad versions.

Final Thought

Supply-chain attacks like this are the new normal. We keep building bigger, shinier CI/CD castles while attackers just walk in through the side door we left open for “convenience”.

Mini Shai-Hulud is a reminder that even the best open-source teams (shoutout to Tanner and the crew for the transparent postmortem) can get caught by chained “known” vulnerabilities.

So go audit your workflows, rotate those creds, and maybe keep a copy of Dune nearby — because the worms are coming.

Until next time — may your caches stay clean, your PRs stay boring, and your npm install stay drama-free.

P.S. Full TanStack postmortem: tanstack.com/blog/npm-supply-chain-compromise-postmortem
GitHub Advisory: GHSA-g7cv-rxg3-hmpx

Stay safe out there, folks. And maybe pin your TanStack versions to pre-May 11 for a hot minute. 😏

// RELATED_ARCHIVES