
On May 11, 2026, 84 malicious npm packages were published in six minutes — and every one came from TanStack’s own release pipeline. No credentials were stolen. No maintainer was phished. The attacker never touched TanStack’s npm token. They chained three GitHub Actions vulnerabilities together and let the project’s CI do the publishing for them. That is the part developers need to internalize: your release pipeline is now an attack surface, and “we use OIDC trusted publishing” is not an answer if your workflow can be hijacked first.
How the Attack Chain Worked
The attacker didn’t break into anything. They exploited three weaknesses that were already present in TanStack’s CI configuration, triggering them in sequence:
Step 1 — The Pwn Request. The attacker renamed a fork of TanStack/router (hiding it from TanStack’s fork list) and opened a pull request. This triggered TanStack’s bundle-size.yml workflow, which used pull_request_target — an event type that runs with base-repo permissions and secrets access even when the triggering code comes from an untrusted fork. The “Pwn Request” pattern has been documented for years. TanStack’s CI was still using it.
Step 2 — Cache Poisoning. With attacker code now running under base-repo trust, it wrote a 1.1 GB poisoned pnpm-store into the GitHub Actions cache. The structural problem: Actions cache is scoped per-repo but shared across all trigger types. A pull_request_target run from a fork PR writes into the same cache namespace that push-to-main release workflows read from. When legitimate code landed on main, the release workflow restored the poisoned cache — not the real one.
Step 3 — OIDC Token Extraction. With malicious binaries now executing inside the release workflow, they read /proc/[pid]/mem of the Runner.Worker process to pull the OIDC token out of RAM at the moment it was minted for npm publishing. The packages were published using TanStack’s legitimate OIDC identity, complete with valid SLSA Build Level 3 provenance attestation — the first documented case of a validly-attested malicious npm package.
What the Payload Does
The malicious payload (router_init.js, 28KB) is a self-propagating worm. On installation, it harvests credentials from AWS, GCP, Azure, Kubernetes service-account tokens, HashiCorp Vault, GitHub tokens, npm auth tokens, SSH private keys, and over 90 developer tool configuration files, then exfiltrates everything over the Session/Oxen network. It installs a persistence daemon — a LaunchAgent on macOS, a systemd user service on Linux — that survives npm uninstall.
The worm component is what gave this attack its scale. After stealing npm OIDC credentials from any CI environment it infected, it republished itself under the identities of every maintainer it could reach. That is how it spread within hours to Mistral AI, UiPath, and OpenSearch packages — 170+ total across npm and PyPI.
Are You Affected?
If your project uses any @tanstack/router* package, check your lockfile against the GitHub security advisory GHSA-g7cv-rxg3-hmpx, which lists the exact compromised version numbers. The attack window was 19:20–21:00 UTC on May 11, 2026. If your CI ran an unfrozen install during that window, treat the runner as compromised.
The @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, and @tanstack/store families are clean — they use separate publish pipelines and were not in the compromised window. Any lockfile installed before May 11 or after May 12 is safe.
What to Do If You Were Exposed
Order matters here. Kill the persistence daemon before rotating credentials — otherwise the daemon will steal the new ones.
- Remove the persistence daemon first: on macOS, unload and delete
~/Library/LaunchAgents/com.user.gh-token-monitor.plist; on Linux, stop and disable thegh-token-monitorsystemd user service. - Delete hidden payload files from your
.claude/and.vscode/directories — look forrouter_runtime.jsandsetup.mjs. - Rotate everything reachable from the affected machine: npm tokens, GitHub PATs, AWS/GCP/Azure cloud credentials, Kubernetes service account tokens, Vault tokens, SSH private keys, and all CI/CD pipeline secrets.
- Block these IOCs at DNS or proxy level:
git-tanstack[.]com,*.getsession.org, and83.142.209[.]194.
Harden Your Own GitHub Actions
TanStack published their hardening measures after a full security sweep. Run these on your own CI regardless of whether you were affected:
- Remove
pull_request_targetfrom all workflows. Useworkflow_runtriggered against artifacts from a sandboxedpull_requestjob instead. - Pin every GitHub Action to a commit SHA, not a version tag. Tags can be rewritten; commit SHAs cannot.
- Disable GitHub Actions cache in release pipelines. The cache is a shared namespace across trust boundaries — treat it as untrusted input.
- Apply minimum permissions to
GITHUB_TOKENin every workflow. Scope it to what the job actually needs. - Enforce non-SMS two-factor authentication on both npm and GitHub accounts.
Read TanStack’s full postmortem and their hardening follow-up. The StepSecurity analysis and Snyk’s breakdown go deeper on the attack mechanics if you want to understand the full picture.
The Real Problem
The attack exploited a structural issue in GitHub Actions: cache is a shared namespace across trust boundaries, with no isolation between fork-PR runs and release runs in the same repository. Until GitHub partitions the cache by trigger source, any open-source project using pull_request_target with shared caching carries this risk.
TanStack had 12.7 million weekly downloads on @tanstack/react-router alone. The attack hit two OpenAI employee machines. These are not edge cases. CI configuration is security-critical infrastructure — and most development teams are not treating it that way. Now is a good time to start.













