On 11 May 2026, between 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 @tanstack/* npm packages. Six minutes. That is how long it took for a self-spreading credential-stealing worm to embed itself inside some of the most widely used JavaScript libraries in the React ecosystem.
@tanstack/react-router has roughly 12 million weekly downloads. Within five hours of those six minutes, the worm had autonomously spread to over 172 packages across the npm and PyPI ecosystems, producing more than 400 additional malicious versions across organisations including Mistral AI, UiPath, OpenSearch, and Guardrails AI.
This was not typosquatting. No fake package names, no dependency confusion. The attackers hijacked TanStack's legitimate CI/CD pipeline and published through the real @tanstack namespace. Each of the 84 malicious packages carried valid cryptographic provenance, certified by Sigstore. That last detail is what sets this apart from most supply chain attacks, and it deserves careful attention.
The Three-Step Attack Chain
Threat group TeamPCP (also tracked as DeadCatx3 and ShellForce) chained three vulnerabilities. Each one is documented and well-known in isolation. Together they bypassed every standard control.
Step 1: Exploiting pull_request_target
The attacker forked the TanStack/router repository and opened a pull request. The repository's GitHub Actions workflows used the pull_request_target trigger, a common pattern for running CI against PRs from external forks. The problem: pull_request_target runs the base repo's workflow with full secrets access while checking out and executing the fork's code. GitHub's own documentation warns about this shape. TanStack's configuration had not been hardened against it.
This gave the attacker's fork code execution inside the base repository's CI runner, with access to repository secrets, while appearing in GitHub's audit log as a routine workflow run.
Step 2: Poisoning the GitHub Actions cache
The attacker's fork code wrote malicious binaries into the shared pnpm store cache. GitHub Actions caches are shared across workflows within a repository, including those triggered by fork PRs and those triggered by pushes to main. When legitimate TanStack maintainers ran their release workflows, those workflows restored the poisoned cache and executed the attacker-controlled binaries.
The attacker never committed anything malicious to TanStack's main branch. The entire intrusion happened through a cache entry.
Step 3: Extracting an OIDC token from runner process memory
The malicious binaries extracted a GitHub Actions OIDC token directly from the runner's process memory at runtime. That token was exchanged for authenticated publish access to the entire @tanstack npm namespace.
No npm credentials were stolen from TanStack's accounts. The attackers did not need them. GitHub-issued OIDC tokens grant short-lived access to external systems. Here, the token's scope covered the full @tanstack namespace, and the window was long enough. The attack bypassed npm's two-factor authentication requirement entirely because OIDC token exchange is a code path that does not trigger MFA.
The Sigstore Finding Nobody Wanted
Each of the 84 malicious packages carried valid SLSA Build Level 3 provenance attestations, certified by Sigstore.
SLSA (Supply-chain Levels for Software Artifacts) is the framework the industry has been pushing as the answer to exactly this class of attack. npm's provenance feature, which launched with real fanfare in 2023, is built on Sigstore. The idea was that cryptographic proof of where a package was built lets consumers verify they are not installing a tampered artefact.
Both SLSA and Sigstore worked exactly as designed. They verified that the packages were built in TanStack's legitimate GitHub Actions pipeline, from TanStack's legitimate repository. That was entirely true. The pipeline had been compromised. Sigstore correctly certified that a poisoned build environment did its job correctly.
This is not a reason to abandon provenance. SLSA and Sigstore do reduce the attack surface for a different class of threat, where someone tampers with a package after it leaves the build environment. What they cannot do is detect that the build environment itself was the vector. The attack is the first documented case of malicious npm packages carrying valid cryptographic supply chain certificates.
It puts a ceiling on what provenance alone can guarantee. That is a meaningful finding for the whole industry, not just for TanStack.
What the Payload Did
Each compromised package contained a 2.3 MB obfuscated JavaScript file called router_init.js. On install, it collected:
- GitHub Actions OIDC tokens, GitLab tokens, CircleCI credentials
- AWS IMDSv2 credentials, GCP service account tokens, Azure service principals
- Kubernetes service account tokens and HashiCorp Vault tokens
- npm publish credentials and SSH private keys reachable from the host
- Cryptocurrency wallet files and AI tool API keys
After collecting credentials, the worm used them to enumerate every npm package the compromised CI system had publish access to, and automatically published infected versions of those packages. That autonomous propagation is how 42 TanStack packages became 172 packages across multiple organisations in under five hours.
Three command-and-control channels ran simultaneously: a typosquat domain (git-tanstack.com), the Session messenger P2P network (to blend exfiltration traffic with encrypted messaging), and GitHub API dead drops using stolen tokens. All three ran in parallel so taking one down did not stop the others.
OX Security estimated 518 million cumulative downloads across affected packages. The more relevant number for any individual organisation is whether their build environment had privileged credentials attached when the install ran.
What Was and Was Not Affected
The 42 compromised packages were all in the @tanstack/router family.
Confirmed affected (two malicious versions each):
- @tanstack/react-router (1.169.5 and 1.169.8)
- @tanstack/react-start (1.167.68 and 1.167.71)
- @tanstack/react-router-devtools (1.166.16 and 1.166.19)
- @tanstack/history, @tanstack/router-cli, @tanstack/eslint-plugin-router, and approximately 36 more
The full version list is in GitHub advisory GHSA-g7cv-rxg3-hmpx.
Confirmed clean: @tanstack/query (React Query), @tanstack/table, @tanstack/form, @tanstack/virtual, and @tanstack/store. These come from separate repositories with separate release pipelines. If your project only uses React Query or TanStack Table, you were not in scope.
CVE-2026-45321, rated 9.6 Critical.
If You Ran npm Install on 11 May 2026
Check your package-lock.json or bun.lock against the versions in the advisory. If any appear in your dependency tree, treat the machine that ran the install as a full credential compromise.
Rotate without delay:
- AWS access keys and IAM roles accessible from the build environment
- GCP service account keys and Azure service principals
- GitHub personal access tokens and all Actions secrets in repositories the runner could reach
- npm publish tokens
- SSH private keys on or reachable from the affected machine
- Kubernetes service account tokens and HashiCorp Vault tokens
This is not a precaution. The payload executed immediately on install. If the install happened in a CI environment with cloud credentials attached, those credentials should be treated as exfiltrated.
For context on what exfiltrated credentials can eventually cost: the average data breach cost for an SMB runs to $3.31 million. Credentials stolen from a CI runner can sit unused for months before being sold or activated. Rotation now closes the window before that happens.
What to Fix in Your Own Pipelines
TanStack was not negligent in any simple sense. The pull_request_target workflow shape is widely used because it solves a real problem. It is also a documented footgun. The gap between knowing it exists and auditing every workflow to close it is where the attack landed.
Audit your workflows for pull_request_target today. Any workflow using that trigger should be reviewed for whether it checks out and executes fork code with access to secrets. If it does, remove the trigger or prevent the fork code from executing in the privileged context. StepSecurity's Harden-Runner action monitors egress traffic from runners and would have flagged the outbound C2 traffic in this attack at the network layer.
Pin Actions to commit SHAs, not tags. uses: actions/checkout@v4 trusts whatever is at that tag when the workflow runs. uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45769f953675 is immutable. Tag hijacking is a separate attack class; SHA pinning closes it with no additional cost.
Scope your npm publish tokens to individual packages, not namespaces. TanStack's OIDC token covered the entire @tanstack namespace. If scoped to specific packages, the blast radius would have been bounded to those packages. npm Automation tokens can be scoped to individual package names at creation time.
Treat CI/CD cache as an untrusted input. Most teams inherit cache configurations from templates and never review them. Ensure that cache keys used by protected-branch workflows cannot be populated by fork-initiated runs. Clearing the cache on release workflows and rebuilding from scratch is slower but removes this attack vector.
Filter egress traffic from CI runners. A runner that can only reach the endpoints it legitimately needs (npm registry, your cloud provider APIs, GitHub) cannot call home to git-tanstack.com. Cataloguing what your runners actually need to reach is maintenance work, but it removes a significant attacker option.
Rotate credentials on a schedule, not just on incidents. Credentials that have not been rotated in 90 days give attackers a long tail of utility even after exfiltration. Applying Zero Trust principles to your build environment, where every credential is short-lived, scoped, and rotated regularly, is the architectural answer to this class of problem.
The Bigger Picture
This was not a one-off. TeamPCP hit Aqua Security's Trivy scanner in March 2026, Bitwarden's CLI npm package in April, and Checkmarx tooling earlier in May. The Mini Shai-Hulud worm toolchain, named after the sandworm from Dune, has been operating since at least September 2025. TanStack was the highest-profile target so far.
The uncomfortable conclusion from this incident is that the controls the industry has been building toward, SLSA provenance and Sigstore attestation, were present, working correctly, and still did not help. That is not a critique of those standards. It is a reminder that supply chain security requires layered defences rather than a single verified-build certificate.
The attack surface is the CI/CD pipeline itself. Most organisations have spent years hardening production environments with firewalls, access controls, and monitoring, while leaving build pipelines running with broadly scoped credentials, unaudited workflow configurations, and shared caches across trust boundaries. TeamPCP found the gap and walked through it.
For most SMBs, the response does not require a dedicated security engineering function. It requires a one-day audit of GitHub Actions workflows, a credential rotation, a cache configuration review, and a clear answer to the question: what credentials are attached to our CI runners and what can those credentials do?
Six minutes to plant 84 malicious packages. The audit to prevent a repeat takes longer than that. Not much longer.
Sources: StepSecurity, Socket.dev, BleepingComputer, JFrog Security Research, GitHub Advisory GHSA-g7cv-rxg3-hmpx, TanStack postmortem
