Logo
Back to Blog
Cloud & DevOpsMay 13, 202614 min read

TanStack npm Supply Chain Attack: How Cache Poisoning Compromised 42 Packages in 6 Minutes

On May 11, 2026, an attacker published 84 malicious versions across 42 @tanstack/* packages by chaining pull_request_target abuse, GitHub Actions cache poisoning, and OIDC token extraction from runner memory. Part of the Mini Shai-Hulud campaign that hit 170+ packages across npm and PyPI.

Lushbinary Team

Lushbinary Team

AI & Cloud Solutions

TanStack npm Supply Chain Attack: How Cache Poisoning Compromised 42 Packages in 6 Minutes

On May 11, 2026, an attacker published 84 malicious package versions across 42 @tanstack/* npm packages in under six minutes. The attack combined three chained vulnerabilities in GitHub Actions to mint legitimate OIDC tokens and publish credential-stealing malware through TanStack's own release pipeline. No npm tokens were stolen. The pipeline itself became the weapon.

This was not an isolated incident. It was part of the broader Mini Shai-Hulud campaign by threat actor TeamPCP, which simultaneously hit @mistralai/mistralai, @uipath/*, @opensearch-project/opensearch, and the Python guardrails-ai package - over 170 packages across npm and PyPI in a single coordinated wave. The TanStack compromise alone affected packages with approximately 12 million weekly downloads.

This article breaks down exactly how the attack worked, what the malware does, how to check if you're affected, and the concrete steps every engineering team should take to harden their CI/CD pipelines and dependency management against this class of supply chain attack.

1Attack Timeline: 6 Minutes That Shook npm

The attack unfolded in two distinct phases: a silent cache-poisoning phase on May 10-11, followed by a rapid detonation phase that published malware through TanStack's legitimate release workflow.

Phase 1: Cache Poisoning (May 10-11)

Time (UTC)Event
May 10, 17:16Attacker creates fork zblgg/configuration (renamed to evade fork-list searches)
May 10, 23:29Malicious commit authored under fabricated identity claude <claude@users.noreply.github.com>. Adds a ~30,000-line bundled JS payload
May 11, 10:49PR #7378 opened against TanStack/router#main. Triggers pull_request_target workflows automatically
May 11, 11:11Force-push lands the malicious commit. Workflow executes attacker code, poisoning the pnpm store cache
May 11, 11:29Poisoned 1.1 GB cache entry saved to GitHub Actions cache under a key matching the release workflow
May 11, 11:31PR force-pushed back to a 0-file no-op, closed, and branch deleted. Cache poison persists invisibly

Phase 2: Detonation (May 11, 19:15-19:26 UTC)

When a legitimate maintainer merged a PR to main, the release workflow restored the poisoned cache. Attacker binaries extracted OIDC tokens from the runner's process memory and published 84 malicious versions directly to the npm registry - all within 6 minutes.

Key Insight

The attacker chose a payload that broke tests, causing the workflow's normal publish step to skip. The malware published packages from within the test/cleanup phase instead. A more careful attacker who didn't break tests could have remained undetected for hours longer.

2The Three-Vulnerability Chain

This attack required chaining three separate vulnerabilities. No single flaw alone was sufficient - each bridged a trust boundary the others assumed was intact.

Vulnerability 1: pull_request_target "Pwn Request"

TanStack's bundle-size.yml workflow used the pull_request_target trigger, which runs in the context of the base repository (with access to secrets and write permissions) even when triggered by a fork PR. The workflow checked out the fork's code and executed it - giving the attacker code execution in the base repo's CI environment.

# The dangerous pattern (simplified)
on:
  pull_request_target:
    paths: ['packages/**']

jobs:
  benchmark-pr:
    steps:
      - uses: actions/checkout@v6.0.2
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge
          # ^ This checks out FORK code in the BASE repo context

      - run: pnpm nx run @benchmarks/bundle-size:build
        # ^ Executes attacker-controlled code with base repo privileges

Vulnerability 2: GitHub Actions Cache Poisoning

The actions/cache@v5 post-job save uses a runner-internal token, not the workflow GITHUB_TOKEN. Setting permissions: contents: read does not block cache writes. Cache scope is shared between pull_request_target runs (which use the base repo's scope) and pushes to main. The attacker wrote a poisoned pnpm store under the exact cache key the release workflow would later restore.

Vulnerability 3: OIDC Token Extraction from Runner Memory

The release workflow declared id-token: write for npm OIDC trusted publishing. When the poisoned cache was restored, attacker binaries read /proc/<pid>/mem of the GitHub Actions Runner.Worker process to extract the OIDC token from memory. This token authenticated directly to registry.npmjs.org - bypassing the workflow's defined publish step entirely.

1. Fork PR Openedpull_request_target fires2. Fork Code RunsIn base repo context3. Cache Poisonedpnpm store overwritten~8 hours pass - legitimate PR merged to main4. Release WorkflowTriggered by push to main5. Cache RestoredMalicious binaries on disk6. OIDC Extracted/proc/pid/mem dump7. Malicious npm Publish84 versions across 42 packagesCredential TheftSelf-PropagationWiper DaemonDetected by StepSecurity in ~20 minutes - all versions deprecated

3What the Malware Does

The payload is delivered through two vectors: an optionalDependencies entry pointing to an orphan commit that executes a prepare script, and an embedded ~2.3 MB obfuscated file router_init.js placed directly in the package tarball. When npm install, pnpm install, or yarn install runs against an affected version, the malware executes automatically via lifecycle scripts.

Credential Harvesting

The malware targets a comprehensive list of credential sources:

  • Cloud credentials: AWS IMDSv2 / Secrets Manager, GCP metadata service, Azure managed identity
  • CI/CD tokens: GitHub Actions OIDC, GitLab CI, CircleCI environment variables
  • Container orchestration: Kubernetes service-account tokens, HashiCorp Vault tokens
  • Package registry: ~/.npmrc tokens, npm publish tokens
  • Source control: GitHub tokens (env vars, gh CLI, .git-credentials), SSH private keys
  • Password vaults (Python variant): 1Password, Bitwarden local stores

Triple Exfiltration Channels

Stolen credentials are exfiltrated via three redundant channels, making takedown significantly harder:

Typosquat Domain

git-tanstack[.]com - attacker-controlled C2

Session Network

Decentralized, encrypted, takedown-resistant messenger

GitHub Dead Drops

Creates repos using stolen tokens with Dune-themed descriptions

Self-Propagation (Worm Behavior)

The malware enumerates other packages the victim maintains via the npm registry API and republishes them with the same injection. This is what makes it a worm - it spreads through the ecosystem without human intervention, using each compromised developer as a stepping stone to more packages.

Destructive Wiper Daemon

Critical: Remove Daemon Before Revoking Tokens

If the malware finds valid GitHub tokens, it installs a persistent gh-token-monitor daemon (macOS LaunchAgent or Linux systemd) that polls GitHub every 60 seconds. If the token is revoked, the daemon executes rm -rf ~/, wiping the home directory. The daemon auto-exits after 24 hours. Always remove the daemon first, then revoke tokens.

Persistence locations to check:

  • macOS: ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
  • Linux: ~/.config/systemd/user/gh-token-monitor.service

4Affected Packages and How to Check

42 @tanstack/* packages were affected, with 84 malicious versions total (two per package). The TanStack team confirmed these package families were not compromised:

@tanstack/query*
@tanstack/table*
@tanstack/form*
@tanstack/virtual*
@tanstack/store
@tanstack/start (meta-package only)

Key affected packages include @tanstack/react-router (versions 1.169.5 and 1.169.8), @tanstack/router-core, @tanstack/router-cli, @tanstack/react-start, and all @tanstack/solid-* and @tanstack/vue-* router packages. The full list is available in GitHub Security Advisory GHSA-g7cv-rxg3-hmpx.

How to Check Your Project

Search your lockfile for the IOC fingerprint - any affected package will contain this in its manifest:

# Check for the malicious optionalDependencies entry
grep -r "79ac49eedf774dd4b0cfa308722bc463cfe5885c" node_modules/

# Check for the malicious payload file
find node_modules/@tanstack -name "router_init.js" -size +2M

# Check for persistence artifacts
ls ~/Library/LaunchAgents/com.user.gh-token-monitor.plist 2>/dev/null
ls ~/.config/systemd/user/gh-token-monitor.service 2>/dev/null

# Check IDE directories for leftover artifacts
find . -path "*/.claude/router_runtime.js" -o -path "*/.vscode/setup.mjs"

5The Broader Mini Shai-Hulud Campaign

The TanStack compromise was one node in a coordinated attack wave. TeamPCP, the threat actor behind the campaign, simultaneously targeted multiple high-value namespaces across npm and PyPI. The Mini Shai-Hulud campaign is a continuation of attacks that first targeted SAP CAP packages in late April 2026, then hit over 500 packages including CrowdStrike-maintained libraries in September 2025.

TargetEcosystemImpact
@tanstack/*npm84 versions, 42 packages, ~12M weekly downloads
@mistralai/mistralainpm + PyPIOfficial Mistral AI TypeScript and Python clients
@uipath/*npm60+ enterprise automation packages
@opensearch-project/opensearchnpmOfficial OpenSearch client
guardrails-aiPyPILLM guardrails framework

The Python variant operates differently - it downloads an unobfuscated modular credential stealer from the C2 domain. It only executes on Linux machines and exits if the system uses Russian language settings or has fewer than four CPUs. Notably, if the system locale is set to Israel or Iran, it invokes a random number generator and, on a 1-in-6 chance, plays an audio file at full volume and attempts to wipe the filesystem.

Attribution

Wiz assesses with high confidence that this is the work of TeamPCP, the same operators behind the SAP, Checkmarx, Bitwarden, Lightning, Intercom, and Trivy compromises. The attack reuses public tradecraft including the verbatim memory-dump script from the tj-actions compromise of March 2025.

6Incident Response Playbook

If you suspect exposure, follow this sequence carefully. The order matters - removing the persistence daemon before revoking tokens prevents the wiper from triggering.

1

Check for persistence daemon

Search for gh-token-monitor on all developer machines and CI runners. Remove it immediately if found.

launchctl list | grep gh-token-monitor
systemctl --user status gh-token-monitor
2

Remove the daemon

Unload and delete the persistence mechanism before touching any tokens.

# macOS
launchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
rm ~/Library/LaunchAgents/com.user.gh-token-monitor.plist

# Linux
systemctl --user stop gh-token-monitor
systemctl --user disable gh-token-monitor
rm ~/.config/systemd/user/gh-token-monitor.service
3

Rotate all credentials

Rotate GitHub tokens, npm tokens, AWS credentials, Vault tokens, Kubernetes service accounts, and SSH keys reachable from the install host.

# Revoke GitHub PATs
gh auth logout

# Rotate npm tokens
npm token revoke <token-id>

# AWS credential rotation
aws iam create-access-key --user-name <user>
aws iam delete-access-key --access-key-id <old-key>
4

Audit IDE and project directories

Check .claude/ and .vscode/ directories for router_runtime.js or setup.mjs artifacts that persist after npm uninstall.

find ~/Projects -name 'router_runtime.js' -o -name 'tanstack_runner.js'
find ~/Projects -path '*/.claude/setup.mjs'
5

Block C2 infrastructure

Block the attacker's command-and-control domains at DNS or proxy level.

# Add to DNS blocklist or /etc/hosts
# git-tanstack.com
# *.getsession.org (if not using Session messenger)
6

Update affected packages

Update to the latest clean versions. All malicious versions have been deprecated.

# Remove node_modules and lockfile, reinstall
rm -rf node_modules package-lock.json
npm install

# Verify no affected versions remain
npm ls @tanstack/react-router

7Hardening GitHub Actions Against Cache Poisoning

The TanStack attack exploited a well-documented class of GitHub Actions vulnerability. Here are the concrete mitigations every team maintaining open-source packages should implement:

1. Never Execute Fork Code in pull_request_target

The pull_request_target event runs with base repository privileges. If you must use it (for labeling, commenting), never check out or execute the PR's code. Use pull_request instead for any workflow that builds or tests fork contributions.

# SAFE: pull_request_target for metadata-only operations
on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  label:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/labeler@v5  # Never checks out fork code

# SAFE: pull_request for build/test (runs in fork context)
on:
  pull_request:
    paths: ['packages/**']

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4  # Checks out fork code safely

2. Pin All Third-Party Actions to Full SHA Hashes

Floating refs like @v6.0.2 or @main can be retargeted. Pin to the full commit SHA to prevent supply chain attacks on the actions themselves.

# Bad: floating tag
- uses: actions/checkout@v4

# Good: pinned to full SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

3. Separate Build and Publish Workflows

Never grant id-token: write to a workflow that also runs tests or builds. Separate the publish step into its own workflow with minimal permissions, triggered only after tests pass in a separate job.

4. Disable Caching in Sensitive Workflows

If your release workflow uses OIDC publishing, consider disabling cache restoration entirely. The performance cost of a fresh install is far less than the risk of cache poisoning.

5. Add Repository Owner Guards

Add an if condition to ensure workflows only run when triggered from the expected repository, not from forks:

jobs:
  publish:
    if: github.repository == 'YourOrg/your-repo'
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

8Dependency Defense: Lockfiles, Provenance, and Script Policies

Even if your CI/CD pipeline is hardened, your project can still be affected as a downstream consumer. Here are the layers of defense for dependency management:

Lockfile Discipline

  • Always commit your lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock)
  • Use npm ci (not npm install) in CI to ensure exact lockfile versions are installed
  • Review lockfile diffs in PRs - unexpected version bumps or new dependencies are red flags
  • Pin exact versions in package.json for critical dependencies (no ^ or ~ ranges)

Disable Lifecycle Scripts

The Shai-Hulud worm relies on npm lifecycle hooks (preinstall, prepare) to execute. Disabling these eliminates the primary infection vector:

# .npmrc - disable scripts globally
ignore-scripts=true

# Or selectively allow scripts for trusted packages
# pnpm: use .pnpmfile.cjs to control which packages can run scripts
# npm: use --ignore-scripts flag in CI

npm Provenance Verification

npm provenance attestation links a published package to its source commit and build workflow. While it wouldn't have prevented the TanStack attack (the malware published through the legitimate workflow), it provides an audit trail and enables detection of publishes from unexpected workflow steps.

# Verify provenance of installed packages
npm audit signatures

# Publish with provenance (for package maintainers)
npm publish --provenance

Supply Chain Security Tools

Layer automated scanning into your workflow:

  • Socket.dev: Detects suspicious package behavior (network calls, filesystem access, obfuscated code) before installation
  • StepSecurity Harden-Runner: Monitors GitHub Actions runner network and file activity in real-time - this is what detected the TanStack attack within 20 minutes
  • npm audit: Checks for known vulnerabilities in your dependency tree
  • Snyk / Mend: Continuous monitoring with automated PR fixes for vulnerable dependencies
  • GitHub Dependabot: Automated security updates with lockfile-aware PRs

9Lessons for the JavaScript Ecosystem

The TanStack compromise exposes systemic issues in how the JavaScript ecosystem handles trust, publishing, and CI/CD security. These are not TanStack-specific problems - they affect every open-source project using GitHub Actions and npm.

What Went Right

  • External researchers at StepSecurity detected and reported the compromise within 20 minutes of the first malicious publish
  • The TanStack team coordinated immediately across timezones and began deprecating packages within an hour
  • The detection community had clear IOC patterns published within hours
  • npm security engaged quickly to pull tarballs from the registry

What Needs to Change

  • OIDC trusted publishing needs per-publish review. Once configured, any code path in the workflow can mint a publish-capable token. There is no mechanism to restrict which workflow step can publish.
  • npm's unpublish policy creates dangerous delays. The "no unpublish if dependents exist" policy means malicious tarballs remain installable for hours while waiting for npm security to pull them server-side.
  • GitHub Actions cache scope crosses trust boundaries. Cache entries from pull_request_target runs share scope with push workflows on the same branch. This is a known design issue that requires conscious mitigation.
  • No internal publish monitoring. TanStack learned about the compromise from a third party. Projects need automated alerting on unexpected publishes.
  • Maintainer count equals blast radius. Seven maintainers on the npm scope means seven credential-theft targets for the same blast radius.

The Bigger Picture

In 2025, Sonatype identified over 454,600 new malicious open-source packages, with over 99% on npm. The Shai-Hulud campaign represents a new class of attack: self-propagating worms that spread through legitimate CI/CD pipelines without stealing credentials directly. The attack surface is not just your code - it's your entire build infrastructure. Every repository with a workflow file is a potential target.

10Secure Your CI/CD Pipeline with Lushbinary

Supply chain attacks are not going away - they are accelerating. Lushbinary helps engineering teams audit and harden their CI/CD pipelines, implement defense-in-depth dependency management, and build incident response playbooks before the next compromise hits.

Our security engineering services include:

  • GitHub Actions workflow audit and hardening (pull_request_target elimination, SHA pinning, permission minimization)
  • npm/PyPI supply chain security implementation (lockfile policies, script blocking, provenance verification)
  • CI/CD pipeline architecture with least-privilege publishing and separated build/release workflows
  • Automated dependency scanning integration (Socket.dev, StepSecurity, Snyk)
  • Incident response planning and credential rotation automation

Free Security Assessment

Worried your GitHub Actions workflows are vulnerable to cache poisoning? Lushbinary offers a free CI/CD security assessment. We'll audit your workflows, identify pull_request_target risks, and deliver a hardening roadmap - no obligation.

Frequently Asked Questions

Was my project affected by the TanStack npm supply chain attack?

If you installed any @tanstack/* package between 19:20 and 21:00 UTC on May 11, 2026, check your lockfile for the 84 affected versions across 42 packages. Confirmed-safe families include @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, and @tanstack/store.

What credentials should I rotate after the TanStack compromise?

Rotate all AWS, GCP, Kubernetes, Vault, GitHub, npm, and SSH credentials reachable from any host that installed an affected version. Check for the gh-token-monitor persistence daemon before revoking GitHub tokens to avoid triggering the wiper payload.

How did the attacker publish packages without stealing npm tokens?

The attacker poisoned the GitHub Actions cache via a pull_request_target workflow, then extracted OIDC tokens from the runner process memory when the release workflow ran on main. These OIDC tokens authenticated directly to the npm registry without needing stored npm credentials.

What is the Mini Shai-Hulud worm and how does it spread?

Mini Shai-Hulud is a self-propagating npm worm by threat actor TeamPCP. It harvests credentials from CI/CD environments, uses stolen npm tokens and GitHub OIDC tokens to republish poisoned versions of other packages the victim maintains, spreading through the ecosystem without human intervention.

How can I protect my GitHub Actions workflows from cache poisoning attacks?

Never run pull_request_target workflows that execute fork code. Pin all third-party actions to full SHA hashes. Separate build and publish into distinct workflows with minimal permissions. Use npm provenance attestation and consider disabling lifecycle scripts with --ignore-scripts.

Sources

Content was rephrased for compliance with licensing restrictions. Technical details sourced from official postmortems and security advisories as of May 13, 2026. Attack details and IOCs may be updated as the investigation continues - always verify on the original sources.

Harden Your CI/CD Before the Next Attack

Get a free security assessment of your GitHub Actions workflows and dependency management. We'll identify cache poisoning risks, pull_request_target vulnerabilities, and deliver a hardening roadmap.

Ready to Build Something Great?

Get a free 30-minute strategy call. We'll map out your project, timeline, and tech stack - no strings attached.

Let's Talk About Your Project

Prefer email? Reach us directly:

Contact Us

Subscribe · Newsletter

Ship Better Engineering, Every Week

Practical writing on AI agents, cloud architecture, and product teardowns. Read by builders at startups and Fortune 500s.

  • New deep-dives on AI agents and cloud architecture
  • Engineering teardowns of shipped products
  • No spam, unsubscribe in one click

We respect your inbox. Read our privacy policy.

Exclusive Offer for Lushbinary Readers
WidelAI

One Subscription. Every Flagship AI Model.

Stop juggling multiple AI subscriptions. WidelAI gives you access to Claude, GPT, Gemini, and more - all under a single plan.

Claude Opus & SonnetGPT-5.5 & o3Gemini ProSingle DashboardAPI Access

Use code at checkout for 10% off your subscription:

Supply Chain Attacknpm SecurityTanStackGitHub ActionsCache PoisoningMini Shai-HuludOIDCCI/CD SecurityCredential TheftWorm MalwareDevSecOpsOpen Source Security

ContactUs