Mini Shai-Hulud: TanStack npm Supply Chain Attack
How TeamPCP chained three GitHub Actions vulnerabilities to publish 84 malicious packages with valid SLSA provenance — and compromise OpenAI
On May 11, 2026, the threat actor group TeamPCP executed the most technically sophisticated npm supply chain attack ever documented. In a six-minute window between 19:20 and 19:26 UTC, attackers published 84 malicious versions across 42 packages in the @tanstack/* namespace — without stealing a single long-lived credential. The attack then self-propagated to 169 total packages including Mistral AI's official SDK, UiPath, and OpenSearch, and confirmed the compromise of two OpenAI employee devices. What makes CVE-2026-45321 historically significant is not its scale: malicious packages carried valid SLSA Build Level 3 provenance attestations — a cryptographic guarantee that was supposed to prove a package was built from a trusted source. TeamPCP did not forge these attestations. They hijacked the legitimate build pipeline itself.
"Valid provenance attestations proved that a package was built from a specific workflow — but they could not verify that the workflow itself had not been compromised."
— Key takeaway, TanStack Postmortem, May 11, 2026
CVE Details
TanStack Router npm Supply Chain Compromise via Chained GitHub Actions Vulnerabilities (Mini Shai-Hulud)
@tanstack/* packages published between 19:20–19:26 UTC on May 11, 2026 contain malicious code injected via three chained GitHub Actions vulnerabilities: a pull_request_target Pwn Request, Actions cache poisoning, and OIDC token extraction from runner process memory. The resulting packages carry valid SLSA Build Level 3 provenance attestations issued by GitHub's Sigstore integration, making them indistinguishable from legitimate releases by automated verification tooling. The payload harvests AWS IMDS credentials, GitHub tokens, npm tokens, Kubernetes service-account tokens, and Vault tokens; exfiltrates via the Session P2P network; and self-propagates to all npm packages maintained by infected hosts.
CAMPAIGN BACKGROUND: THE SHAI-HULUD ESCALATION
CVE-2026-45321 is the fourth wave in an escalating campaign attributed to TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, and CipherForce), a threat group with a documented partnership with the Vect ransomware group. Each wave has introduced a significant technical escalation over the prior attack.
| Wave | Date | Scale | Key Technical Escalation |
|---|---|---|---|
| Shai-Hulud | Sep 14–16, 2025 | 500+ packages, 700+ repos | First self-propagating npm worm |
| Shai-Hulud 2.0 | Nov 21–23, 2025 | 492 packages, 132M monthly downloads | preinstall hook; home-directory destruction fallback |
| Mini Shai-Hulud | Apr 29, 2026 | SAP/Intercom ecosystems | First AI coding agent persistence (.claude/settings.json) |
| Mini Shai-Hulud Is Back | May 11, 2026 | 373 malicious versions, 169 packages | First npm worm with valid SLSA BL3 attestations; Session P2P C2 |
THE PAYLOAD: router_init.js
The 2.3 MB obfuscated primary payload has four distinct capabilities: (1) Credential Harvesting — queries AWS EC2 IMDS at 169.254.169.254, probes ECS/Fargate metadata, accesses Vault, and scans for GitHub tokens, npm tokens, SSH private keys, and Kubernetes service-account tokens; (2) Session P2P Exfiltration — stolen credentials exfiltrated through the decentralized Session messenger network — no single C2 server to block or seize; (3) Self-Propagation — enumerates and republishes all npm packages maintained by the victim with the same malicious injection; (4) gh-token-monitor Wiper — if a GitHub token with repo write and org membership is found, installs a daemon polling GitHub's API every 60 seconds — if the token is revoked, executes rm -rf ~/. Security teams must remove this daemon before revoking any GitHub tokens.
Attack Chain
Pwn Request via pull_request_target
On May 10, 2026 at 17:16 UTC, the attacker created a fork of TanStack/router under the account zblgg, deliberately naming it zblgg/configuration to evade fork-list searches. They opened PR #7378 targeting TanStack's bundle-size.yml workflow, which used the pull_request_target trigger — running in the context of the base repository with access to its secrets, cache, and OIDC identity. The malicious commit was authored under the spoofed identity claude <[email protected]> to impersonate Anthropic's Claude and reduce human suspicion during code review.
The pull_request_target trigger + actions/checkout of the fork ref = attacker code runs with base repo permissions.
GitHub Actions Cache Poisoning
When the benchmark job ran, the malicious vite_setup.mjs executed and poisoned the pnpm-store cache under the exact key that release.yml would later restore. Two properties made this possible: cache writes use a runner-internal token not blocked by permissions: contents: read, and caches are shared across all workflow runs — including fork PR runs and main branch runs.
Cache key: Linux-pnpm-store-${hashFiles('**/pnpm-lock.yaml')} — shared between PR runs and main branch runs.
OIDC Token Extraction from Runner Process Memory
When the legitimate release workflow restored the poisoned cache, malicious code read OIDC tokens from the GitHub Actions runner process memory via /proc/<pid>/mem. These tokens were used to POST directly to registry.npmjs.org, bypassing TanStack's normal publish step entirely. Because the requests originated from within TanStack's legitimate GitHub Actions runner, npm's Sigstore-based provenance system issued valid SLSA Build Level 3 attestations for every malicious package.
OIDC tokens read from Runner.Worker process memory → POST to registry.npmjs.org → valid SLSA BL3 provenance issued automatically.
Credential Harvesting & Self-Propagation Worm
The router_init.js payload (2.3 MB obfuscated) harvests AWS IMDS credentials, GitHub tokens, npm tokens, Kubernetes service-account tokens, and Vault tokens. It then enumerates all npm packages maintained by the victim and republishes each with the same malicious injection, transforming a 42-package compromise into a 169-package ecosystem event within hours. Mistral AI SDK, UiPath, OpenSearch, and guardrails-ai were among the self-propagated victims.
Exfiltration via Session/Oxen P2P network (filev2.getsession.org) — decentralized, onion-routed, no single C2 server to block or seize.
Persistence & Wiper Installation
The payload installs gh-token-monitor — a daemon that polls GitHub's API every 60 seconds. If the discovered GitHub token is revoked, the daemon executes rm -rf ~/, wiping the user's entire home directory. The daemon persists via macOS LaunchAgent or Linux systemd service. Critically, the payload also persists via .claude/settings.json (SessionStart hook) and .vscode/tasks.json (folderOpen task) — surviving npm uninstall and re-executing on every Claude Code session or VS Code folder open.
You MUST locate and remove gh-token-monitor BEFORE revoking any GitHub tokens — or revoking will trigger a home-directory wipe.
Impact & Indicators of Compromise
Ecosystem Scale
- 373 malicious package versions published
- 169 total npm packages compromised via worm
- @tanstack/* (42 packages) as ground zero
- Mistral AI SDK, UiPath, OpenSearch, guardrails-ai self-propagated
Credential & Device Compromise
- 2 OpenAI employee devices confirmed compromised
- Code-signing certificates for iOS, macOS, Windows, Android exposed
- OpenAI rotating all certificates — macOS users must update before June 12, 2026
- Credential exfiltration via decentralized P2P — no single seizable C2
Provenance Trust Collapse
- SLSA BL3 attestations issued on all malicious packages
- Every automated verification tool accepted packages as legitimate
- Sigstore / npm provenance unable to detect pipeline compromise
- Foundational assumption of supply-chain attestation invalidated
AI Tool Persistence Surface
- .claude/settings.json SessionStart hook re-executes malware
- .vscode/tasks.json folderOpen task re-executes malware
- Survives npm uninstall — must be manually purged
- First documented targeting of AI coding tool configs as persistence vectors
INDICATORS OF COMPROMISE
| Type | Indicator | Description |
|---|---|---|
| Domain | git-tanstack[.]com | Primary C2 / payload download domain |
| Domain | filev2[.]getsession[.]org | Session P2P exfiltration endpoint |
| IP | 83[.]142[.]209[.]194 | Attacker infrastructure |
| File | router_init.js | Primary payload · SHA256: ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c |
| File | tanstack_runner.js | Payload runner · SHA256: 2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96 |
| File | .claude/settings.json | Persistence: SessionStart hook re-executes malware on Claude Code session start |
| File | .vscode/tasks.json | Persistence: folderOpen task re-executes malware when VS Code opens |
| Commit | [email protected] | Spoofed commit author impersonating Anthropic Claude |
| String | IfYouRevokeThisToken ItWillWipeTheComputer OfTheOwner |
Threat string embedded in payload to deter incident responders from revoking tokens |
Response Checklist
Before revoking any credentials, locate and remove the wiper daemon. On macOS: ~/Library/LaunchAgents/com.user.gh-token-monitor.plist. On Linux: ~/.config/systemd/user/gh-token-monitor.service. Stop and remove the daemon before proceeding to credential rotation — revoking tokens while the daemon is active triggers rm -rf ~/ and wipes the entire home directory.
Search package-lock.json, pnpm-lock.yaml, yarn.lock, and CI build logs for affected @tanstack/* versions published between 19:20–19:26 UTC on May 11, 2026. Consult GHSA-g7cv-rxg3-hmpx for the full list of affected version ranges across all 42 packages. Also check for Mistral AI SDK, UiPath, OpenSearch, and guardrails-ai versions published in the hours following the initial compromise.
Remove router_init.js, tanstack_runner.js, router_runtime.js, and setup.mjs from .claude/ and .vscode/ directories. Inspect and restore clean versions of .claude/settings.json and .vscode/tasks.json — these files survive npm uninstall and will re-execute the malware on next Claude Code session start or VS Code folder open.
Treat any host that installed a compromised package version as fully compromised. Rotate: npm tokens, GitHub PATs and fine-grained tokens, AWS/GCP/Azure credentials and IMDS-derived tokens, Kubernetes service-account tokens, Vault tokens, SSH private keys, and all CI/CD secrets. For OpenAI-adjacent environments, note that code-signing certificates have already been rotated — macOS users must update OpenAI applications before June 12, 2026.
Any workflow using pull_request_target that checks out fork code or runs fork-controlled build scripts is vulnerable to the same initial access vector. Audit all repositories and restructure to separate trusted and untrusted phases — the untrusted phase must not execute with base repository secrets or OIDC identity.
Replace all mutable tags (actions/checkout@v4) with full SHA pins (actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683). Tags can be silently updated to point to malicious commits; full SHA references cannot be redirected. Apply this to all third-party actions across all workflow files.
Scope id-token: write permission to only the specific jobs that require it for OIDC-based publishing. Do not grant id-token: write at the workflow level or in jobs that execute untrusted code. This was the critical permission that allowed OIDC token extraction to result in valid SLSA BL3 provenance on malicious packages.
Configure package managers to enforce a minimum release age of 24–72 hours before installing newly published versions. This provides a detection and response window for fast-moving supply chain attacks — the entire malicious publish window in this attack was only 6 minutes, but self-propagation continued for hours. Release-age cooldowns would have prevented most developer machines from installing the compromised versions before the packages were yanked.
Block at DNS/proxy level: git-tanstack.com, filev2.getsession.org, *.getsession.org, and 83.142.209.194. Monitor CI/CD runners and developer endpoints for unexpected reads of /proc/*/mem on Linux systems — this is an anomalous operation and a strong indicator of OIDC token extraction activity.
Include .claude/settings.json and .vscode/tasks.json in code review workflows and file integrity monitoring. These files are now a documented attack surface for supply chain persistence. Unexpected modifications to either file — especially newly added hooks or tasks — should trigger immediate security review.
Maintain a software bill of materials for all AI SDK dependencies including @tanstack/*, @mistralai/*, and any transitive dependencies. Regularly audit for unexpected version bumps, package size increases of more than 10%, or new lifecycle scripts (preinstall, postinstall) added to packages that previously had none.
Restrict trusted publishing to specific protected branch and workflow file combinations, and add repository_owner guards to prevent workflows from running in fork contexts with base repository permissions. Consider requiring a human approval step in the release pipeline between the build job (which runs attacker-accessible code) and the publish job (which holds OIDC credentials).
Governance Framework
SLSA Provenance Is Necessary but Not Sufficient
Valid SLSA Build Level 3 attestations proved that packages were built from a specific workflow — but could not verify that the workflow itself was uncompromised. Organizations that rely solely on provenance attestation for supply-chain assurance must now also verify the integrity of the workflows generating those attestations. Build pipeline integrity monitoring is required alongside attestation verification.
GitHub Actions Cache as a Trust Boundary
Actions cache entries must be treated as potentially untrusted inputs, not merely as performance optimizations. Audit which workflows can write to shared cache namespaces. Establish a policy that any cache key shared between fork PR runs and main branch runs represents a trust boundary — and architect your pipeline accordingly, with cache isolation or re-validation before cache restoration in privileged jobs.
AI Coding Tool Configs as an Attack Surface
The deliberate targeting of .claude/settings.json and .vscode/tasks.json signals that threat actors have adapted to widespread AI coding assistant adoption. These files must be incorporated into your security model: code-reviewed on change, monitored for unauthorized modification, and excluded from any workflow that clones untrusted repositories into the developer's working directory.
References
[1] TanStack
"npm Supply Chain Compromise: Postmortem" — Official incident postmortem from TanStack, May 11, 2026. tanstack.com/blog/npm-supply-chain-compromise-postmortem
[2] GitHub Security Advisory
GHSA-g7cv-rxg3-hmpx — CVE-2026-45321: TanStack Router npm supply chain compromise via chained GitHub Actions vulnerabilities. Full list of affected package versions.
[3] OpenAI
"Our Response to the TanStack npm Supply Chain Attack" — OpenAI's public statement on device compromise, certificate rotation, and macOS update requirement. openai.com/index/our-response-to-the-tanstack-npm-supply-chain-attack/
[4] Orca Security
"TanStack npm Supply Chain Worm: Technical Analysis" — Deep-dive on the worm propagation mechanism and SLSA BL3 attestation bypass. orca.security/resources/blog/tanstack-npm-supply-chain-worm/
[5] ThreatLocker
"TeamPCP Supply Chain Attack Hits TanStack" — Threat actor attribution, campaign history, and Vect ransomware group partnership documentation. threatlocker.com/blog/teampcp-supply-chain-attack-hits-tanstack
[6] SafeDep
"Mass npm Supply Chain Attack: TanStack, Mistral, and the Shai-Hulud Campaign" — Analysis of self-propagation to Mistral AI SDK and secondary victims. safedep.io/mass-npm-supply-chain-attack-tanstack-mistral
[7] Snyk
"TanStack npm Packages Compromised" — Vulnerability report and affected version list. snyk.io/blog/tanstack-npm-packages-compromised/