Mini Shai-Hulud npm supply chain attack analysis
SecurityMay 14, 2026·11 min read

Mini Shai-Hulud: the npm Supply Chain Attack That Defeated Every Trust Signal

Three small misconfigurations chained into 84 malicious package versions, published by the maintainer's own pipeline, signed with valid provenance, and undetectable by every trust signal the JavaScript ecosystem currently offers.

Table of Contents

TL;DR

On May 11, 2026, attackers published 84 poisoned versions of TanStack npm packages in a six-minute window. They never stole a token. They hijacked the maintainer's own CI pipeline and let it sign the malware for them. The packages had valid Sigstore provenance. The ecosystem has a new problem.

The Numbers

If you installed a TanStack package (Query, Router, Table, Form), there is a small chance you ran malware on May 11, 2026. The window was short and the damage was contained, but the technique behind it warrants close attention.

84
Malicious versions across 42 @tanstack/* packages
6 min
Publishing window: 19:20 to 19:26 UTC, May 11, 2026
12.7M
Weekly downloads of @tanstack/react-router alone
0
npm tokens stolen during the entire attack

Within 48 hours the campaign expanded to 172 packages and 403 malicious versions across npm and PyPI, including releases from Mistral AI, UiPath, OpenSearch, and Guardrails AI.[1] The cumulative download count of affected packages: 518 million.[4]

The campaign is called Mini Shai-Hulud. Attribution points to a group tracked as TeamPCP, also known as DeadCatx3, PCPcat, ShellForce, and CipherForce. These are the same operators behind the March 2026 Aqua Trivy compromise and the April 2026 Bitwarden CLI npm compromise.[5]

What Actually Happened, in Plain Terms

TanStack had implemented most of the controls the security community recommends:

  • Two-factor authentication on every maintainer account
  • OIDC trusted publishing instead of long-lived npm tokens
  • Signed Sigstore provenance on every release

None of it mattered. The attackers never tried to steal a credential. They went one layer deeper and compromised the build pipeline itself. When the legitimate pipeline ran, it published the attacker's code under TanStack's identity, signed it, attested it, and tagged it as authentic. From npm's point of view, every malicious release was a real release.

TanStack had locked the front door, the back door, and the windows. The attackers slipped a forged blueprint into the locksmith's toolbox.

The Attack Chain, Step by Step

The attackers chained three known vulnerability classes in GitHub Actions. None of them are new individually; together, they are devastating.

Attacker Side
TanStack CI
1

Fork & Rename

TanStack/router is forked and renamed to zblgg/configuration to evade discovery.

Evades fork-list discovery

2

Open PR → triggers pull_request_target

The PR triggers a workflow that runs fork code with the base repo's secrets and permissions.

Classic "Pwn Request" misconfiguration

3

Poison Actions cache

Instead of publishing directly, a malicious pnpm store is written to the shared GitHub Actions cache.

Cache is shared across branches

4

Maintainer merges a PR

The release workflow restores the poisoned cache. Attacker code now runs with publish privileges.

OIDC token extracted from /proc/pid/mem

OIDC Token Extraction

The poisoned code extracts the OIDC token from the runner's process memory and publishes 84 packages to npm, as if it were TanStack itself.

Valid Sigstore provenance on every version

Attack chain · TanStack/router → npm registry, May 11, 2026

Steps 1 and 2: The Pwn Request

The attacker forked the TanStack/router repository on GitHub, renamed the fork to zblgg/configuration to reduce searchability, then opened a pull request back to TanStack. That PR triggered a workflow using GitHub Actions's pull_request_target event.

Step 3: Cache Poisoning

The forked PR did not try to publish anything directly. Instead, it wrote a malicious pnpm store to the GitHub Actions cache. GitHub's Actions cache is shared across branches of the same repository, including caches written from fork pull requests. That shared scope is the trust boundary that should not exist but does.

Step 4: The Waiting Game

The attackers then waited. Days later, a TanStack maintainer merged a completely unrelated pull request to main. The release workflow ran, restored the poisoned cache, and the attacker's code began executing inside TanStack's trusted CI runner.

The Payload: OIDC Token Extraction

The poisoned install script read GitHub's OIDC token directly from the runner's process memory (/proc/<pid>/mem). With that token, the attacker authenticated to npm as the legitimate TanStack/router repository via npm's trusted-publisher binding and published 84 malicious versions across 42 packages in six minutes.

What the Malware Does to You

Every poisoned package contained a new file: router_init.js, a 2.3 MB obfuscated JavaScript bundle that runs at install time.[4] It behaves differently depending on where it runs.

On CI Runners and Developer Machines

  • Steals GitHub environment variables, npm tokens, SSH private keys, AWS / GCP / Azure credentials, and Kubernetes service-account tokens
  • Exfiltrates over three redundant channels: the typosquat domain git-tanstack[.]com, the Session messenger network, and GitHub API "dead drops"[3]
  • Worm propagation: uses any stolen npm publish tokens to publish poisoned versions of additional packages the victim has write access to

On Developer Workstations

The malware installs a persistent daemon called gh-token-monitor via a macOS LaunchAgent or a Linux systemd unit. The daemon polls GitHub every 60 seconds. When it receives a 40X response (indicating the stolen token has been revoked), it attempts to execute rm -rf ~/.[3]

Deep dive: the Russian language check and PyPI variant

The malware checks the system locale and terminates without exfiltrating data if it detects Russian. This is a known signature of certain Eastern European threat actors and the simplest way the security community infers rough geographic origin.

A separate variant targeted PyPI: mistralai@2.4.6 and guardrails-ai@0.10.1 were both published with a 13-line trojan that downloads and executes git-tanstack[.]com/tmp/transformers.pyz. The Python variant is Linux-only, has the same Russian-locale check, and exits if fewer than four CPUs are present, serving as a basic VM/sandbox evasion check.[3]

Why This Attack Matters Beyond TanStack

According to Snyk's analysis, this is the first npm supply chain attack to publish packages that are cryptographically indistinguishable from legitimate ones by provenance attestation.[5] Wiz and VentureBeat reach the same conclusion in their technical breakdowns.[3][9]

That threshold makes this campaign architecturally significant. It reveals several facts about the ecosystem:

  • Sigstore badges and SLSA provenance are necessary but insufficient trust signals on their own
  • OIDC trusted publishing protects against credential theft but does not protect against a compromised CI runner
  • 2FA and the absence of long-lived tokens do not constitute a complete defense

Supply chain attacks have moved one layer down the stack, from compromising package publishers to compromising the build infrastructure of package publishers. Defenders need behavioral visibility during installs and builds, beyond attestation checks at the registry.

What You Should Actually Do

Most effective defenses are configuration changes, requiring no new tools or vendors. Stacked together, they would have blocked every wave of this campaign.

1. Enable Release Cooldowns

Malicious npm packages are typically detected and removed within hours. The TanStack versions were detected publicly within 20 minutes.[1] A time-based delay before installing newly published versions eliminates the exposure window almost entirely.

.npmrc
# npm 11.10+; npm uses DAYS
min-release-age=7
pnpm-workspace.yaml
# pnpm 10.16+; pnpm uses MINUTES (10080 = 7 days)
minimumReleaseAge: 10080
minimumReleaseAgeExclude:
  - "@your-org/*"
.yarnrc.yml
# Yarn 4.10+; Yarn uses MINUTES
npmMinimalAgeGate: 10080
npmPreapprovedPackages:
  - "@your-org/*"
bunfig.toml
# Bun 1.3+; Bun uses SECONDS (604800 = 7 days)
[install]
minimumReleaseAge = 604800
minimumReleaseAgeExcludes = ["@your-org/*"]
.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: "/"
    schedule: { interval: daily }
    cooldown:
      default-days: 7

2. Disable Lifecycle Scripts in Dependencies

The TanStack payload also abused a prepare lifecycle hook on an optionalDependency (the orphan-committed @tanstack/setup package). pnpm v10+ disables postinstall scripts by default and requires explicit allow-listing via onlyBuiltDependencies in package.json. For npm and Yarn, use --ignore-scripts and selectively enable only trusted packages.

3. Harden Your Own CI/CD Pipelines

This is where the actual attack occurred, and where most teams have the weakest controls.

  • Never check out PR code inside a pull_request_target workflow. Use the plain pull_request event for anything that touches untrusted contributions.
  • Set permissions: id-token: none at the workflow level. Only grant id-token: write to the specific job that publishes.
  • Pin third-party GitHub Actions by commit SHA, never by tag.
  • Add repository-owner guards to workflows that run with elevated permissions.
  • Treat the GitHub Actions cache as untrusted across the fork-base boundary.

4. Reduce Uncached, Uncontrolled Installs

npx, pnpm dlx, and bunx perform live, uncached resolutions without cooldown enforcement on every invocation. Each run is an opportunity to fetch a compromised version. Pin frequently used tools as devDependencies instead.

5. Block Known Indicators at the Network Edge

At DNS or your egress proxy, block:

  • git-tanstack[.]com
  • *.getsession.org
  • 83.142.209[.]194

6. Lockfile Discipline

Commit package-lock.json or pnpm-lock.yaml. In CI, always use npm ci or pnpm install --frozen-lockfile. This eliminates surprise resolution between local development and production.

If You Installed an Affected Version

Any developer or CI environment that ran npm, pnpm, or yarn install against an affected @tanstack/* version on May 11, 2026 should be considered compromised. The response order matters.

  1. Isolate the host or runner first; do not revoke tokens yet
  2. Image the system for forensics
  3. Check .claude/ and .vscode/ directories for persisted payload files such as router_runtime.js or setup.mjs, which survive npm uninstall
  4. Check for gh-token-monitor persistence in ~/Library/LaunchAgents/ (macOS) or ~/.config/systemd/user/ (Linux)
  5. Rotate, in order: npm tokens, GitHub PATs and OIDC federations, cloud credentials (AWS / GCP / Azure), SSH keys, CI/CD secrets, Kubernetes service-account tokens
  6. Pin all @tanstack/* dependencies to versions published before 2026-05-11 19:00 UTC or to the patched releases listed in GitHub Security Advisory GHSA-g7cv-rxg3-hmpx[2]
  7. Reinstall from a clean lockfile

The Bigger Picture

Mini Shai-Hulud is the most architecturally significant npm supply chain attack of 2026. It demonstrates that an attacker who can execute code inside a target's GitHub Actions runner can produce malicious packages that are cryptographically indistinguishable from legitimate ones.

The highest-leverage defenses already exist in every major package manager: release cooldowns, lifecycle-script restrictions, locked-down CI workflows, disciplined lockfile use. Most teams have not turned them on.

Turn them on this week.

Sources

  1. [1]TanStack postmortem tanstack.com
  2. [2]GitHub Security Advisory GHSA-g7cv-rxg3-hmpx github.com
  3. [3]Wiz Research, "Mini Shai-Hulud Strikes Again" www.wiz.io
  4. [4]Socket, "TanStack npm Packages Compromised" socket.dev
  5. [5]Snyk, "TanStack npm Packages Hit by Mini Shai-Hulud" snyk.io
  6. [6]Endor Labs, "Shai-Hulud Compromises the @tanstack Ecosystem" www.endorlabs.com
  7. [7]The Hacker News, "Mini Shai-Hulud Worm Compromises TanStack" thehackernews.com
  8. [8]Orca Security, "Supply Chain Worm Attack Analysis" orca.security
  9. [9]VentureBeat, "Shai-Hulud Worm 172 npm/PyPI Packages" venturebeat.com
  10. [10]Phoenix Security, "TeamPCP Campaign Analysis" phoenix.security
  11. [11]Aikido, "Mini Shai-Hulud is Back" www.aikido.dev
  12. [12]Infosecurity Magazine, initial disclosure www.infosecurity-magazine.com
  13. [13]NHS England Digital Cyber Alert CC-4781 digital.nhs.uk
  14. [14]pnpm Supply Chain Security Documentation pnpm.io
  15. [15]Socket, "npm Introduces minimumReleaseAge" socket.dev

Ready for your next project?

Let's take your digital presence to the next level together.

Start a project

Listen to this article