The TanStack npm compromise of May 11, 2026
TeamPCP chained pull_request_target, GitHub Actions cache poisoning, and OIDC token extraction to publish 84 malicious @tanstack versions in six minutes.
- tanstack
- npm
- github-actions
- supply-chain
On May 11, 2026, between 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 @tanstack/* npm packages without stealing any maintainer npm tokens. TanStack's official postmortem documents how three GitHub Actions weaknesses chained together to turn a legitimate release workflow into a publish path for malware.
What happened
The attack combined:
pull_request_targetwith fork code checkout inbundle-size.yml, a pattern GitHub Security Lab calls the "Pwn Request"- GitHub Actions cache poisoning across the fork-to-base trust boundary (documented by Adnan Khan in 2024)
- OIDC token extraction from runner memory, reusing tradecraft from the tj-actions/changed-files compromise of March 2025
On May 10, an attacker forked TanStack/router as zblgg/configuration. On May 11, PR #7378 triggered pull_request_target workflows that checked out fork-controlled code and poisoned the pnpm store cache keyed to what release.yml would restore on the next push to main.
When release workflows ran later that evening, the poisoned cache restored attacker-controlled binaries. Those binaries read the GitHub Actions Runner.Worker process memory, extracted an OIDC token minted for npm trusted publishing, and POSTed directly to registry.npmjs.org. The publish did not come from the workflow's defined Publish Packages step, which was skipped because tests failed.
Detection and scope
External researcher ashishkurmi (StepSecurity) opened TanStack/router#7383 at 19:46 UTC, roughly 26 minutes after the first malicious publish. TanStack deprecated all 84 versions; npm removed tarballs between 22:13 and 23:55 UTC the same night.
TanStack confirmed that only Router/Start monorepo packages were affected. Families including @tanstack/query*, @tanstack/table*, and @tanstack/form* were clean. See GHSA-g7cv-rxg3-hmpx for affected version numbers.
What the payload does on install
If a developer or CI runner installed an affected version on May 11, the malicious optionalDependencies entry triggered a prepare script running router_init.js. TanStack's postmortem states the script:
- Harvests credentials from AWS, GCP, Kubernetes, Vault, npm, GitHub, and SSH key locations
- Exfiltrates over Session/Oxen messenger file-upload endpoints
- Self-propagates by searching for other packages the victim maintains and republishing them with the same injection
Anyone who installed on that date should treat the host as potentially compromised and rotate reachable credentials.
Why developer Macs matter
CI runners were the primary blast radius, but any engineer who ran pnpm install or npm install against a poisoned version on a laptop pulled the same lifecycle payload. Lockfiles updated on that machine are the artifact Koban diffs on the next heartbeat.
Frozen-lockfile installs (npm ci, pnpm install --frozen-lockfile) against a pre-incident lockfile would not have resolved the malicious versions. Teams without pinned lockfiles in local workflows were exposed.
Lessons that outlive TanStack
TanStack's postmortem highlights uncomfortable truths:
- OIDC trusted publishing minted valid tokens for attacker-controlled code. Provenance proves which workflow built an artifact, not that the workflow ran the code you intended.
- The GitHub Actions cache is a shared trust boundary that most teams do not treat as one.
pull_request_targetplus checkout of PR head code remains dangerous years after public warnings.
Visibility on developer Macs does not stop cache poisoning. It does show when a new @tanstack/* version or unexpected dependency appears in a lockfile after an incident window.
Further reading
- TanStack postmortem (updated May 15, 2026)
- TanStack/router#7383 (initial detection)
- GHSA-g7cv-rxg3-hmpx