The malware was signed. The signature was real. The package was poison.
TanStack's npm release pipeline published 84 malicious package versions with valid SLSA provenance. The attestation was correct. It just wasn't the question that mattered.
On May 11, 2026, between 19:20 and 19:26 UTC, an attacker published 84 malicious versions of 42 @tanstack/* packages to npm, including @tanstack/react-router at 12.7M weekly downloads. Every one of them carried valid SLSA provenance and a working Sigstore attestation. The signatures were real. They were issued by Sigstore’s Fulcio CA, bound to TanStack’s actual GitHub Actions workflow, attesting that the build ran on refs/heads/main in TanStack/router. That part was true. The build did run there. The attacker was just running inside it.
This is the first documented case of malicious npm releases shipping with working chain-of-custody signatures, per Enclave.ai’s analysis. It is worth sitting with that sentence before we talk about what to do next.
What the attestation actually said
When you enable npm publish --provenance, what gets signed is narrower than the marketing suggests. Sigstore accepts an OIDC token from the runner, issues a short-lived certificate bound to the workflow identity (repo, ref, workflow file), and signs an in-toto attestation covering the package contents and build metadata. That attestation answers exactly one question: did this specific CI job, on this ref, produce this tarball.
It does not answer whether the workflow was running the code the maintainer intended. It does not answer whether the cache was clean. It does not answer whether the triggering commit was legitimate. As Mondoo put it, provenance addresses the threat of a compromised publishing environment. It does not address the threat of attacker-controlled code riding inside a legitimate one.
The TanStack worm is a clean demonstration of the difference. Per TanStack’s postmortem and StepSecurity’s reconstruction, an attacker operating as voicproducoes opened a pull_request_target-triggered PR against TanStack/router the day before the attack. The workflow had permissions: contents: read set, which most maintainers reasonably read as “this PR can’t write anything.” It can’t write to the repo. It can write to the Actions cache, because actions/cache@v5’s post-job save step uses a runner-internal token, not the workflow GITHUB_TOKEN. And cache scope is shared between pull_request_target runs and pushes to main in the same repo.
The PR pre-computed the cache key TanStack’s release workflow would later look up, using the same hashFiles('**/pnpm-lock.yaml') formula the workflow uses, and poisoned the pnpm store at that key. When a legitimate commit later triggered the release on main, the workflow restored the poisoned cache. The attacker’s binaries were now running inside a trusted release runner. They scanned /proc/*/cmdline for Runner.Worker, dumped its heap via /proc/<pid>/mem, and extracted the short-lived OIDC token held for npm’s trusted-publisher endpoint. Then they POSTed 84 tarballs to registry.npmjs.org authenticated by TanStack’s own legitimate identity.
The whole publication window ran six minutes. Per Wiz, the workflow itself ended in status: failure afterward, but the publish step had already completed.
Why the signature kept being right
There is no failure in Sigstore here. The system did what it was designed to do. The OIDC token was real. The runner was the runner. The workflow file path matched. The certificate Fulcio issued is exactly the one a Sigstore-aware verifier should accept. CVE-2026-45321 at CVSS 9.6 was assigned against the packages, not against the signing infrastructure, because the signing infrastructure isn’t broken.
The problem is in how provenance has been talked about. “Signed by the publisher” has been shorthanded into “safe to install” in too many places, including, frankly, in some of the original npm and Sigstore launch posts. A verification chain that proves origin is not equivalent to one that proves integrity of intent. Against a credential-theft attack like the s1ngularity wave from August 2025, provenance is strong evidence: a stolen-credential publish would have lacked provenance entirely and been detectable on that basis. Against an attacker who can inject into the build pipeline, provenance becomes a liability amplifier. The malicious artifact carries the same trust badge as every legitimate release before it, and any consumer (or automated policy) trained to treat provenance as a safety signal will wave it through with less scrutiny than an unsigned package would get.
Vectra AI reports that the worm extended this further in propagation, forging Sigstore-compatible in-toto attestations for downstream republished packages using generateKeyPairSync. Whether those forgeries would survive verification against the public Rekor transparency log, or whether they only mimicked the format, isn’t settled in the sources. The distinction matters and someone with binary access should publish on it.
It is also unclear whether TanStack’s pipeline formally met SLSA Build Level 3 requirements (hermetic builds, reproducibility) or was effectively L2-with-attestation that vendor write-ups have called L3. A cache-poisoning attack is hard to square with hermeticity, so the pipeline may never have been L3 in the strict sense. That is a useful clarification for the spec discussion but does not change the outcome for the packages.
What this keeps being
This is the third Shai-Hulud wave in eight months, and the trend line is not opportunistic. Per ReversingLabs and StepSecurity’s attribution to TeamPCP, each wave has been technically more sophisticated than the last. The September 2025 wave was credential theft. The November wave added wiper capability and reached 1,092 backdoored versions. The April 2026 SAP-focused wave first demonstrated the pull_request_target -> cache-poison -> OIDC chain. The May 11 TanStack wave is that same chain at higher scale against more visible targets, with valid provenance attached. The capability is being developed deliberately and shipped on a cadence.
The common factor across waves isn’t the payload. It’s that pull_request_target is a landmine most maintainers don’t realize they have armed, and the permissions: contents: read guard reads as protection when it isn’t. GitHub’s 2026 security roadmap adds org-level Actor and Event Rules that can prohibit pull_request_target across all repos, plus workflow dependency locking and scoped secrets. Those are the right controls. They are also not shipped yet, and the worm is.
What you do this week
The dossier and the StepSecurity advisory both lead with the same step, and it is the one to do today: grep your package-lock.json or pnpm-lock.yaml for any @tanstack/* version published in the 19:20-19:26 UTC window on May 11, 2026. The affected version list is in Snyk’s advisory and Wiz’s writeup. If a match exists, audit node_modules for router_init.js or setup.mjs at package roots, and look for either file reappearing under .vscode/tasks.json persistence hooks or ~/.claude/settings.json.
One ordering detail to get right: the payload drops a gh-token-monitor daemon on developer machines, and StepSecurity warns to remove that daemon before revoking GitHub tokens. The worm’s wiper activates on token revocation. Kill the daemon, then rotate.
For ongoing hardening, two changes are worth making this week regardless of whether you’re a TanStack consumer: switch to npm ci --ignore-scripts (or pnpm with strictDepBuilds=true, which has been default since late 2025), and enable minimumReleaseAge to skip installing versions less than a day old. That second one would have closed the entire six-minute publication window for free.
OpenAI has confirmed two employee devices were compromised, no production impact disclosed, and revoked the macOS code-signing certificate with a June 12 cutoff before Gatekeeper blocks the old one. No comparable disclosure from Mistral AI, Guardrails AI, or UiPath has appeared in indexed sources, though all three are named as affected by third-party researchers. The confirmed scope so far is developer workstations and CI runners. Whether anything reached production at any of these companies is, at the moment, what they haven’t said.
The reframe
The story being told about this incident is that SLSA provenance failed. It did not. Provenance answered the question it was designed to answer, correctly, while a different question went unasked. The story worth telling is that “signed” has been quietly collapsed into “safe” across the npm ecosystem, and the first attacker to notice the gap walked through it with 84 packages and a self-propagating worm. The signatures will keep being right. That has stopped being enough.
PatchDay Alert tracks supply-chain compromises like this in the daily digest, with the IOCs and the version windows you need to grep your lockfile.
Sources
- Enclave.ai: TanStack’s CI Published the Malware Itself. SLSA Said the Build Was Fine. — 2026-05
- Mondoo: npm Supply Chain Security in 2026 — 2026
- Postmortem: TanStack npm supply-chain compromise — 2026-05
- StepSecurity: TeamPCP’s Mini Shai-Hulud Is Back — 2026-05
- Wiz: Mini Shai-Hulud Strikes Again — 2026-05-12
- GitLab Advisory CVE-2026-45321 — 2026-05
- Vectra AI: Shai-Hulud Part 2: When the Worm Forged Its Own Security Certificate — 2026
- ReversingLabs: Team PCP’s Mini Shai-Hulud tears at open-source trust — 2026-05
- What’s coming to our GitHub Actions 2026 security roadmap — GitHub Blog — 2026
- Snyk: TanStack npm Packages Hit by Mini Shai-Hulud — 2026-05
- Mitigating supply chain attacks — pnpm docs — 2026
- OpenAI: Our response to the TanStack npm supply chain attack — 2026-05
Share
Related field notes
-
What 14 days of TeamPCP told us about registry defense in 2026
Five compromises across two ecosystems in six weeks, then a 169-package npm wave on May 11. One threat actor, two very different defensive postures. The pattern is the point.
-
Three hours was the good outcome: npm's trust model and the Axios compromise
A DPRK threat actor backdoored two Axios versions on npm. Socket flagged the malicious dependency in six minutes. Nothing stopped the downstream publish fifteen minutes later. The system worked exactly as designed.
-
A valid signature is not a vouch
For 27 days the official DAEMON Tools installer carried a clean Disc Soft signature and a backdoor. The signature did exactly what it was designed to do. That is the problem.
One email, every weekday morning.
You're in. Check your inbox.