Signed, Verified, and Malicious: The Shai-Hulud Attack on TanStack and Mistral

Signed, Verified, and Malicious: The Shai-Hulud Attack on TanStack and Mistral#
On May 11 2026, the TeamPCP threat group published malicious versions of TanStack router packages to npm. By the time most developers read about it the following morning, the attack had already spread to Mistral AI, OpenSearch, Guardrails AI, and UiPath via stolen CI/CD credentials. Over 400 package artifacts were compromised across npm and PyPI.
Attestd's auto-qualification pipeline ran its scheduled cycle at 5:00 AM EST on May 12. It added 24 npm packages and 1 PyPI package to the supply chain watchlist based on OSV advisories published overnight. BleepingComputer published their coverage at 7:29 AM EST. By the time most developers read the news, Attestd was already returning supply_chain.compromised: true for the affected versions.
Every compromised package has risk_state: "none". No CVE exists for this attack. npm audit passes. And every compromised package carried a valid SLSA Build Level 3 provenance attestation. Valid Sigstore signatures. The npm provenance badge shows "verified." Cryptographic provenance verification passes.
The only signal that catches this is a registry of known-compromised version tuples checked at deploy time.
How the attack worked#
The attackers chained three vulnerabilities in the TanStack/router repository: a risky pull_request_target workflow, GitHub Actions cache poisoning, and OIDC token theft from runner process memory.
The OIDC token gave the attacker access to npm's Trusted Publishing system. They used it to publish malicious versions through the legitimate TanStack release pipeline. Because the publish happened through the correct workflow with a valid OIDC token, npm's signing infrastructure issued a valid Sigstore attestation and submitted it to the Rekor transparency log. The packages looked cryptographically authentic because the cryptographic machinery was operating correctly. The pipeline was compromised, not the signatures.
A Git commit trick compounded the damage. The attackers pushed an orphaned commit to a fork of TanStack/router, which remained accessible through GitHub's shared fork object storage even though it belonged to no branch. This commit was referenced via a malicious optional dependency, causing npm to automatically fetch and execute attacker-controlled code during package installation. No explicit install step required.
Once the attacker had the TanStack CI/CD credentials, they moved laterally to Mistral AI, Guardrails AI, UiPath, and OpenSearch using stolen GitHub and npm tokens, republishing malicious versions across all of them.
The payload targets GitHub Actions OIDC tokens, Git credentials, npm publish tokens, AWS credentials, Kubernetes service account tokens, HashiCorp Vault tokens, SSH keys, Claude Code configurations, VS Code tasks, and .env files. It reads GitHub Actions process memory across more than 100 file paths associated with cloud providers and credential stores.
Persistence is the most concerning detail: the malware writes itself into Claude Code hooks and VS Code auto-run tasks. Uninstalling the malicious package does not remove the malware.
Exfiltration used the Session P2P network, making traffic appear as encrypted messenger traffic to complicate detection and blocking.
What Attestd caught#
The following packages in the Attestd watchlist are confirmed compromised. All return supply_chain.compromised: true and risk_state: "none" on the live API. No CVE existed for any of them at the time of the attack.
npm:
| Package | Compromised versions | Weekly downloads |
|---|---|---|
@tanstack/react-router | 1.169.5, 1.169.8 | 12.7M |
@tanstack/router-core | 1.169.5, 1.169.8 | 11.4M |
@tanstack/history | 1.161.9, 1.161.12 | 12.8M |
@mistralai/mistralai | 2.2.2, 2.2.3, 2.2.4 | |
@opensearch-project/opensearch | 3.5.3, 3.6.2, 3.7.0, 3.8.0 | 1.3M |
PyPI:
| Package | Compromised version | Weekly downloads |
|---|---|---|
mistralai | 2.4.6 | |
guardrails-ai | 0.10.1 | 68.5k |
Live API checks#
# TanStack react-router — headline package, 12.7M weekly downloads
curl "https://api.attestd.io/v1/check?product=%40tanstack%2Freact-router&version=1.169.5" \
-H "Authorization: Bearer YOUR_API_KEY"
# → supply_chain.compromised: true
# Safe version one patch earlier
curl "https://api.attestd.io/v1/check?product=%40tanstack%2Freact-router&version=1.169.4" \
-H "Authorization: Bearer YOUR_API_KEY"
# → supply_chain.compromised: false
# Mistral AI npm client
curl "https://api.attestd.io/v1/check?product=%40mistralai%2Fmistralai&version=2.2.4" \
-H "Authorization: Bearer YOUR_API_KEY"
# → supply_chain.compromised: true
# Mistral AI PyPI
curl "https://api.attestd.io/v1/check?product=mistralai&version=2.4.6" \
-H "Authorization: Bearer YOUR_API_KEY"
# → supply_chain.compromised: true
# Guardrails AI
curl "https://api.attestd.io/v1/check?product=guardrails-ai&version=0.10.1" \
-H "Authorization: Bearer YOUR_API_KEY"
# → supply_chain.compromised: true
Why SLSA failed here#
SLSA provenance tells you that a package was built by a specific workflow in a specific repository. It does not tell you whether that workflow was compromised at the moment of build.
The Shai-Hulud attack is the first documented case of a supply chain attack producing cryptographically valid SLSA Build Level 3 attestations for malicious packages. The packages were built by the legitimate TanStack workflow. The Sigstore attestation is genuine. The Rekor transparency log entry is real. Every automated check that relies on provenance alone returns clean.
Snyk's analysis put it directly: "the attack produces valid SLSA Build Level 3 attestations for malicious packages," making it necessary to add a behavioral analysis layer and a signature-based check for malicious packages on top of provenance verification.
The registry approach is the complement to provenance. Provenance tells you who built the package. A known-bad registry tells you whether this specific version has been confirmed malicious. Both signals are necessary. Neither alone is sufficient.
If you installed any of these versions#
Treat all credentials accessible from the affected environment as compromised. Rotate immediately:
GitHub tokens and npm publish tokens, AWS access keys, Kubernetes service account tokens, HashiCorp Vault tokens, SSH keys, all credentials in .env files, and Claude Code configurations.
Check IDE directories for persistence artifacts that survive package uninstall: router_runtime.js, setup.mjs, and modifications to Claude Code hooks or VS Code auto-run task configurations.
Block the known C2 infrastructure at DNS or proxy level: api.masscan.cloud, git-tanstack.com, and *.getsession.org.
Downgrade to the last clean version of any affected package. For @tanstack/react-router, version 1.169.4 returns supply_chain.compromised: false.
Attribution#
TeamPCP, also tracked as DeadCatx3, PCPcat, ShellForce, and CipherForce. Branch names in the attack used Dune universe terms: sietch, sardaukar, atreides, shai-hulud. Commits were spoofed to claude@users.noreply.github.com to impersonate the Claude Code bot.
This is the same threat group behind the LiteLLM attack in March, the Bitwarden CLI compromise in April, and the PyTorch Lightning ShaiWorm in April.
Sources#
- Socket Research
- TanStack post-mortem
- Aikido
- Snyk
- StepSecurity, Endor Labs, Microsoft Threat Intelligence Get an API key at api.attestd.io/portal/login. Free tier, 1,000 calls a month, no credit card required.