The TanStack worm shows that a trusted publishing badge proves where a package came from, not that the build was clean. An attacker chained a risky pull_request_target workflow, GitHub Actions cache poisoning and a short-lived publishing token taken from memory, then published 84 malicious versions across 42 packages. A security lead should ask about workflows, caches, tokens and publish alerts.
What happened in the TanStack worm incident?
On 11 May 2026, between 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 @tanstack npm packages. TanStack states that no npm tokens were stolen and that its publish workflow itself was not compromised. The packages still carried a roughly 2.3 MB obfuscated script, and it was published through the real trusted publishing identity.
The malware steals cloud, Kubernetes and Vault secrets, among others. It sends them out over the Session messenger file-upload network, so there is no attacker-run command server to block. It also spreads: it finds other packages the victim maintains and republishes them with the same injection. Our TanStack npm worm incident page holds the full timeline and indicators. This post is about the pipeline questions.
How did the attack chain work?
Each step below relied on the one before it. The attacker needed no write access to the main repository, only the ability to fork and push to their own fork.
- Fork and pull requestThe attacker forks the repository and opens a pull request against the main one.
- Workflow runs unreviewedA pull_request_target workflow ran automatically, with no first-time-contributor approval.
- Cache poisonedA 1.1 GB poisoned cache entry was saved for the main branch.
- Release run restores itA later release run on main restored the poisoned cache.
- Token read from memoryThe malware read the runner’s memory to extract the OIDC token.
- Direct publishThe token was used to send publish requests straight to the npm registry.
The attacker then reset the pull request so it looked empty, closed it and deleted the branch, but the poisoned cache stayed. The malicious publish did not come from the defined publish step, which was skipped because tests failed. It came from the malware.
Which CI settings matter most?
TanStack says its pull_request_target workflows had not been audited, even though this is a long-known dangerous pattern. That is the first place to look. Next is the cache: cache writes use a runner-internal token, so limiting workflow permissions did not block the cache change. Read-only permissions alone would not have stopped this one.
Then the token. Once trusted publishing is configured, there is no per-publish review, so any code that runs in the release job can mint a publish token. After the incident TanStack restructured the vulnerable workflow, added guards and pinned third-party actions to fixed commits. The GitHub advisory also suggests turning off npm lifecycle scripts on install as a workaround.
- Audit pull_request_targetList every workflow that uses it and check whether it builds code from a fork.
- Review cache and token exposureFind release jobs that restore shared caches and have id-token write permission.
- Separate release cachesConsider keeping release caches apart from pull request caches.
- Pin third-party actionsPin them to commit SHAs, as TanStack did after the incident.
- Skip lifecycle scripts in CIConsider turning them off on install where you can.
- Alert on odd publishesFlag any publish from your organization that does not come from the expected workflow step.
Why do the numbers 42 and over 160 differ?
They count different things. TanStack’s own scan of all 295 of its packages confirmed a scope of 42 packages and 84 versions, all in the Router and Start repository. CERT-EU, a public body, says the wider Mini Shai-Hulud worm compromised over 160 npm and PyPI packages across several ecosystems. The first is one vendor’s confirmed scope; the second is a campaign count.
CERT-EU attributes the attack to TeamPCP. TanStack, GitHub and NVD refer only to “an attacker” and give no attribution. For a related page on a related npm campaign with a similar name, see the Shai-Hulud npm incident.
How could threat intelligence monitoring give early warning?
TanStack had no internal alerting and learned of the compromise from a third party. An external researcher opened a public issue at 19:46 UTC with a full write-up, about 20 minutes after the first publish. The first two versions were deprecated at 20:19 UTC, and the registry removed the tarballs between 22:13 and 23:55 UTC.
Threat intelligence monitoring can help in that gap. It means watching for compromised packages named in advisories, for new alerts, and for exposed secrets tied to your developers and CI. An alert on the named packages could have told you to check your lockfiles that evening. No tool can promise this, but the question is fair: how quickly would we know if one of our dependencies, or our own packages, was published by a compromised pipeline, and which secrets would that expose?
If a host installed an affected version on 11 May, TanStack says to treat it as potentially compromised and rotate the AWS, GCP, Kubernetes, Vault, GitHub, npm and SSH secrets reachable from it.
Related reading: how a Trivy compromise reached LiteLLM releases and Nx Console and GitHub as an entry path.
Last checked against sources on 9 October 2026.
