Mini Shai-Hulud Hits TanStack: How a GitHub Actions Cache Poisoning Bypassed SLSA Build Level 3
At 19:20 UTC on May 11, 2026, a supply chain attack started in the @tanstack/router repository on npm. By 19:26, 84 malicious package versions across 42 @tanstack namespaced packages had been published. Six minutes. The worm then continued spreading, eventually compromising 170+ packages across npm and PyPI, with a cumulative download count across affected packages of over 518 million.
This was TeamPCP's Mini Shai-Hulud campaign. What made it technically significant was not the scale. It was the provenance.
The malicious packages carried valid SLSA Build Level 3 provenance attestations. This is the first documented supply chain attack to produce validly-attested malicious packages at that tier.
The attack chain, step by step
The attacker forked the TanStack/router repository and renamed the fork to `zblgg/configuration` to avoid appearing in the visible fork list on GitHub. They then opened a pull request against the upstream TanStack/router repository.
The PR triggered a `pull_request_target` workflow. This workflow type, unlike `pull_request`, runs with the permissions of the base repository, not the fork. Critically, it checked out and executed the attacker's fork code. The attacker's code poisoned the GitHub Actions pnpm store cache with a malicious version.
When TanStack's legitimate release pipeline subsequently ran, it pulled from the poisoned cache. The poisoned code extracted TanStack's OIDC token directly from the GitHub Actions Runner.Worker process memory and used it to POST the malicious packages directly to registry.npmjs.org. The packages were signed with TanStack's own OIDC identity — the same identity that generates SLSA Build Level 3 attestations. From the attestation's perspective, the build was legitimate. The provenance was accurate. The pipeline was TanStack's.
That is how you bypass SLSA Build Level 3 without breaking it.
What the payload does
The obfuscated `router_init.js` file included in the compromised packages profiles the execution environment and launches a comprehensive credential stealer. It harvests credentials from more than 100 file paths spanning cloud providers (AWS, Azure, GCP), cryptocurrency wallets, AI tools, messaging applications, and CI systems including GitHub Actions. It reads GitHub Actions runner process memory to extract in-flight secrets.
The stealer also installs persistence hooks in Claude Code, VS Code, and OS-level services that survive reboots.
The credential harvest is then exfiltrated to attacker-controlled infrastructure. The payload also includes a complete disk wipe routine capable of wiping the infected host entirely — a destructive escalation that goes well beyond the credential-theft playbook and significantly raises the incident severity for any team that installed the compromised packages in a CI or developer environment.
Why SLSA attestation did not prevent this
SLSA Build Level 3 guarantees that the build was performed by a specific, isolated, ephemeral build environment and that the provenance accurately records the build inputs and outputs. It does not guarantee that the build inputs themselves were not compromised before the build started.
The attacker did not tamper with the build system. They tampered with the build's input, the pnpm cache, before the build ran. The build then faithfully attested to exactly what it did. The attestation was correct. The cache was poisoned.
This is a documented gap in the SLSA Level 3 threat model: cache poisoning as a pre-build input compromise. Organizations treating SLSA Level 3 attestation as a final-control security guarantee should revise that assumption.
The parallel RubyGems incident
On or around May 11-12, RubyGems suspended new account registrations after hundreds of malicious packages were uploaded in a parallel campaign. The two events may be related to TeamPCP's broader operational tempo, or may be independent actors operating the same general playbook. Attribution on the RubyGems incident is not yet confirmed.
What your team should do now
Pin every npm dependency to exact semver, not ranges. A semver range means any new version, including a maliciously published one, can be pulled during the next install.
Audit your GitHub Actions workflows for `pull_request_target` usage. Any workflow that checks out PR code and runs it with base-repository permissions is a potential cache-poisoning entry point. Apply `ref: ${{ github.event.pull_request.head.sha }}` patterns cautiously; prefer separating privileged workflows from code checkout entirely.
Isolate the pnpm store cache from sensitive write-back paths. Scope cache keys to include the lockfile hash and restrict cache restore to read-only steps where possible.
Add Harden-Runner or equivalent GitHub Actions runtime monitoring. StepSecurity detected this attack within 30 minutes via anomalous cache write patterns.
Review your attestation strategy. If SLSA attestation is a decision gate in your supply chain, add a cache provenance check alongside it. StepSecurity's analysis of this incident includes specific detection patterns.
TeamPCP has now compromised at minimum eight distribution channels in Q2 2026. The supply chain is their primary attack surface. Treat every package manager input as untrusted until proven otherwise.
Gigia Tsiklauri is a Security Architect and founder of Infosec.ge. Get in touch if supply chain security is a current priority for your organization.