TanStack npm "Mini Shai-Hulud" Supply Chain Worm — TeamPCP Attack Chain, IOCs & Full Remediation
How threat actor TeamPCP deployed the Mini Shai-Hulud worm to poison TanStack packages and compromise developer build environments.
This post is part of the Week of June 12, 2026 Security Roundup.
Background: What Is TanStack?
TanStack is a collection of open-source JavaScript/TypeScript libraries with 12 million weekly downloads across packages including @tanstack/react-query, @tanstack/router, @tanstack/table, and @tanstack/virtual. These are widely used in production web applications at OpenAI, Vercel, Grafana, and thousands of enterprises.
The Attack: "Mini Shai-Hulud"
On May 11, 2026 between 19:20 and 19:26 UTC (a six-minute window), threat actor group TeamPCP published 84 malicious versions across 42 @tanstack/* packages. By end of day, the campaign had expanded to over 160 packages across npm and PyPI via a self-propagating worm mechanism, collectively called "Mini Shai-Hulud."
Attack Technique: pwn-request + Cache Poisoning + OIDC Hijack
The attack combined three publicly known GitHub Actions attack techniques with zero novel code:
- Pwn Request (pull_request_target): Attacker forked TanStack/router and submitted a PR. The repository's CI/CD used the
pull_request_targettrigger, which runs with write permissions even from fork PRs—a known dangerous pattern documented since 2021. - GitHub Actions Cache Poisoning: The malicious PR injected a poisoned pnpm cache entry. When TanStack's release workflow restored the cache, it loaded attacker-controlled packages instead of legitimate ones.
- OIDC Token Extraction: With code executing in the runner, the attacker extracted the GitHub Actions OIDC token directly from the runner process memory, allowing npm publish using TanStack's own trusted identity with valid SLSA provenance.
Malware Payload Analysis
The malicious packages contained a ~2.3 MB obfuscated file, router_init.js, injected as an optionalDependency with a prepare lifecycle script. When npm install / pnpm install ran against any affected version:
# What router_init.js collected and exfiltrated:
- GitHub tokens (GITHUB_TOKEN, PAT tokens from env)
- AWS credentials (~/.aws/credentials, AWS_* env vars)
- GCP service account keys (~/.config/gcloud/)
- SSH private keys (~/.ssh/id_*)
- Kubernetes configs (~/.kube/config)
- CI/CD tokens (CIRCLE_TOKEN, TRAVIS_TOKEN, etc.)
- Docker registry tokens (~/.docker/config.json)
- NPM tokens (~/.npmrc)
# Exfiltration method:
# HTTPS POST to C2 (see IOCs below)
# Runs as a persistent background daemon on developer machines
Indicators of Compromise
Malicious package indicator:
File: node_modules/.pnpm/*/node_modules/@tanstack/router/router_init.js
File size: ~2.3 MB (legitimate router files are much smaller)
Package versions: @tanstack/* published 2026-05-11T19:20:00Z to 19:26:00Z
# Check for affected versions in your lockfile:
grep -E "@tanstack/[^:]+:(2026|.*2026-05-11)" package-lock.json yarn.lock pnpm-lock.yaml
# Check for IOC file:
find node_modules -name "router_init.js" -size +1M 2>/dev/null
# Check for suspicious background processes:
ps aux | grep -i "router_init\|tanstack" | grep -v grep
C2 infrastructure (defanged):
teamPCP-associated infrastructure — see TeamPCP threat intel reports for full IOC list
Look for: HTTPS POST requests to non-CDN endpoints from npm lifecycle scripts
Confirmed Downstream Impact
- OpenAI: confirmed developer environment compromise; rotated affected credentials
- Vercel: confirmed CI/CD exposure; internal investigation completed
- Grafana Labs: confirmed npm install hit affected versions; credentials rotated
- 334+ individual developer environments confirmed compromised
Remediation Steps
# Step 1: Update all @tanstack packages to latest verified clean versions
npm update @tanstack/react-query @tanstack/router @tanstack/table @tanstack/virtual
# Or for pnpm:
pnpm update "@tanstack/*"
# Step 2: Delete and reinstall node_modules to clear any cached malicious content
rm -rf node_modules package-lock.json
npm install
# Step 3: If affected versions ran in your environment, rotate ALL secrets immediately:
# - GitHub Personal Access Tokens and repository deploy keys
# - AWS IAM access keys
# - GCP service account keys
# - Kubernetes service account tokens
# - Docker Hub / registry tokens
# - NPM publish tokens
# - Any CI/CD tokens (CircleCI, TravisCI, GitHub Actions secrets)
# - SSH private keys (generate new keypairs)
# Step 4: Harden your GitHub Actions workflows
# Pin actions to commit SHAs instead of tags:
# uses: actions/checkout@v4 → uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
# Step 5: Enable SLSA verification in your CI
# Use GitHub's artifact attestation (available since May 2024):
gh attestation verify path/to/artifact.tgz --owner your-org
# Step 6: Audit pull_request_target usage in your repos
grep -r "pull_request_target" .github/workflows/
# Any workflow triggered by pull_request_target that checks out PR code
# and has write permissions should be refactored to use pull_request instead