Critical alert: Mini Shai-Hulud campaign exfiltrates secrets and spreads via TanStack CI/CD pipelines

A supply chain attack campaign named Mini Shai-Hulud was recently reported. It is a worm-type malware that has compromised multiple npm packages, starting with the @tanstack/* ecosystem, publishing malicious versions through the projects’ own GitHub Actions pipelines using stolen OIDC tokens. These malicious versions include obfuscated payloads that steal CI/CD, cloud, repository, and development tool credentials, persist on developer machines, and self-propagate to other packages using valid npm and OIDC tokens.

npm is the main package registry for the JavaScript and TypeScript ecosystem and has become a critical piece of the modern software supply chain. Millions of applications and CI/CD pipelines automatically consume new versions of packages published on npm, which makes any compromise of maintainer accounts or their pipelines a vector of massive and cross-cutting impact. This structural dependency makes it essential to apply additional security controls (intermediate registries, version validation, publishing policies, and observability) instead of blindly trusting that everything coming from npm is reliable.

The severity is critical because the worm can read memory from the GitHub Actions runner and extract absolutely all secrets from a pipeline (including “masked” secrets), abuse OIDC to publish new malicious packages with valid SLSA Build Level 3, and also establishes persistence on developer workstations and IDEs. In practice, any environment that has installed compromised versions must assume total compromise of all secrets accessible from that machine or pipeline.

Compromised packages

PackageEcosystemCompromised versionsQuick comment
@tanstack/react-routernpm1.169.5, 1.169.8Very widely used React router in front-end.
@tanstack/solid-routernpm1.169.5, 1.169.8Router for SolidJS applications.
@tanstack/vue-routernpm1.169.5, 1.169.8Router for Vue applications.
@tanstack/react-startnpm1.167.68, 1.167.71Full-stack framework on React.
@tanstack/solid-startnpm1.167.65, 1.167.68Full-stack framework on Solid.
@tanstack/vue-startnpm1.167.61, 1.167.64Full-stack framework on Vue.
@mistralai/mistralainpm2.2.3, 2.2.4Official Mistral AI TypeScript SDK.
mistralaiPyPI2.4.6Mistral AI Python SDK.
guardrails-aiPyPI0.10.1Popular library for LLM guardrails.
@opensearch-project/opensearchnpm3.6.2OpenSearch client.
@uipath/clinpm1.0.1UiPath CLI used in automation/RPA.
@uipath/robotnpm1.3.4UiPath robot component.
@uipath/project-packagernpm1.1.16Key package in UiPath projects.
@draftlab/authnpm0.24.1, 0.24.2Auth for DraftLab applications.
@squawk/airport-datanpm0.7.4, 0.7.5, 0.7.7Aeronautical data, used in aviation solutions.

Technical details of the attack

Compromise vector and package publishing

The TeamPCP threat group launched a new wave of the Mini Shai-Hulud worm targeting TanStack and other high-value projects. To do this, the attacker created a malicious fork of the TanStack/router repository using the voicproducoes account and added two key artifacts: a fake @tanstack/setup package and a large, heavily obfuscated file called tanstack_runner.js. This @tanstack/setup package is declared as an optional dependency via a github: URL with a commit hash that appears to belong to the official TanStack repository, but actually points to that malicious fork.

Two characteristic changes were observed in each compromised package:

  • An optionalDependencies block was added in package.json pointing to @tanstack/setup via a github:tanstack/router# URL.
  • A large router_init.js file was added at the root of the package, which should not be present according to the files field (which only lists dist and src), evidencing tarball manipulation outside the normal build process.

The malicious packages were not published with credentials stolen from a maintainer, but by compromising the GitHub Actions pipeline itself, leveraging the runner’s OIDC token to publish directly to npm. As part of the campaign, the malicious packages include SLSA v1 level 3 attestations issued by npm’s signing infrastructure (Sigstore/Fulcio/Rekor), so that they appear legitimate despite having been generated by an already compromised pipeline.

Characteristics of the worm and payload

The file router_init.js:

(SHA-256 hash ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c) contains a payload of about 2.3 MB highly obfuscated that implements a multi-stage stealer with worm capabilities.

Main capabilities:

  • Advanced obfuscation: three layers of obfuscation, including obfuscated string table, a substitution cipher with PBKDF2-SHA256 and secondary payloads encrypted with AES-256-GCM that are decompressed with Bun.gunzipSync.
  • Use of the Bun runtime: the payload installs and uses Bun 1.3.13 instead of Node.js, evading security tools focused on Node hooks (–require, etc.).
  • GitHub Actions runner memory scraping: a Python payload reads /proc/<pid>/mem of the Runner.Worker process to extract internal JSON of the type {“value”:”…”,”isSecret”:true}, obtaining all secrets configured in the workflow, even if they are masked or never written to disk.
  • Cloud credential theft: invokes AWS metadata (169.254.169.254 IMDSv2), ECS (169.254.170.2), and local HashiCorp Vault sockets (127.0.0.1:8200) to obtain temporary credentials, tokens, and secrets.
  • Credential file collection: traverses more than 100 known paths to extract npm tokens, GitHub PATs, AWS, GCP, Azure keys, cryptocurrency wallet files, shell histories, and AI and messaging tool configurations.
  • Workstation persistence: creates files in .claude/ and .vscode/ to hook into session opening events in Claude Code and folder opening events in VS Code, and also installs gh-token-monitor type services via LaunchAgents on macOS or systemd user on Linux.

Self-propagation

Mini Shai-Hulud behaves as an npm worm, using stolen tokens to compromise more packages:

  • Searches for npm tokens with bypass_2fa true (publishing without 2FA) and, if found, enumerates all packages of the same maintainer using the npm search API.
  • In CI/CD environments, exchanges GitHub OIDC tokens for per-package npm publishing tokens via the oidc/token/exchange endpoint, bypassing the standard authentication flow.
  • Publishes infected artifacts for all packages with sufficient permissions, replicating the infection.

It also injects malicious workflows into .github/workflows/codeql_analysis.yml that use toJSON(secrets) to serialize all repository secrets and exfiltrate them directly to C2 infrastructure (via HTTP POST) or as artifacts uploaded in seemingly legitimate actions.

Exfiltration and ransom

Data exfiltration combines two main channels:

  • Session Protocol CDN: uploading encrypted blobs to filev2.getsession.org, part of the Session network infrastructure, using hybrid RSA-4096 + AES-256-GCM encryption.
  • Dead-drop on GitHub: uses the GitHub GraphQL API to create commits in attacker-controlled repositories, signed as claude@users.noreply.github.com, with messages chore: update dependencies and branches that mimic the Dependabot convention (dependabot/github_actions/format/<dune-word>).

The campaign also includes an extortion/ransom component: the malware creates npm tokens with the description IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner, indicating that revoking the token may trigger a destructive routine that wipes the owner’s computer.

Detection and key indicators

At the sysadmin/DevOps level, these are very practical indicators:

  • Presence of router_init.js in node_modules or at the root of @tanstack/* packages.
  • package.json with optionalDependencies pointing to github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c.
  • Unusual tarball size growth (packages ~900 KB versus ~190 KB for clean versions).
  • bun processes running during npm install of packages that don’t normally use Bun.
  • python3 processes reading /proc/*/mem on CI runners.
  • Network connections from npm install or CI jobs to filev2.getsession.org, api.masscan.cloud, or git-tanstack.com.
  • npm tokens with the description IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner.
  • Unexpected commits signed as claude@users.noreply.github.com with branches like dependabot/github_actions/format/.
TypeValueDescription / suggested use
SHA-256ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266cHash of router_init.js (main payload in TanStack packages). Block/alert in EDR, AV, and forensic searches.
Processbunbun executions during npm install or CI jobs in repositories that don’t legitimately use Bun. Create detection/anomaly rule.
Local filerouter_init.jsMalicious file added at the root of @tanstack/* packages. Search on disk, containers, and artifacts.
Local file.claude/router_runtime.js, .claude/setup.mjsPersistence in development environments (Claude Code). Review and remove.
Local file.vscode/setup.mjsPersistence in VS Code via tasks/extensions. Review and remove.
Servicegh-token-monitorUser service (systemd / LaunchAgent) used to monitor tokens. Search and disable.
C2 Domainfilev2.getsession.orgSession network CDN used to exfiltrate encrypted data. Block or alert in proxy/firewall.
Domain/C2api.masscan.cloudRelated infrastructure used in previous campaigns of the same family. Monitor outbound connections.
GitHub InfraCommits with author claude@users.noreply.github.com on branches dependabot/github_actions/format/Dead-drop on victim repos. Create hunting query in GitHub / source code.
Internal networkhttp://169.254.169.254Access to AWS IMDS (credential theft). Alert if seen from containers/runners that shouldn’t touch metadata.
Internal networkhttp://169.254.170.2ECS/Task metadata endpoint. Same as above.
Internal networkhttp://127.0.0.1:8200Attempts to access local HashiCorp Vault. Monitor on CI runners.
npm TokenDescription IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwnerMalicious tokens created by the malware. Enumerate npm tokens and treat them as a critical compromise indicator.

Recommended mitigation

Assess exposure (dev and CI/CD)

  • Review package-lock.json / pnpm-lock.yaml / yarn.lock for affected versions of @tanstack/* and other listed packages (UiPath, DraftLab, etc.).
  • Search for router_init.js and references to @tanstack/setup in node_modules and local repos.
  • Review GitHub Actions (or CI) logs for:
    • mentions of @tanstack/setup,
    • use of bun run tanstack_runner.js or other Bun commands,
    • unusual network connections during dependency installation.

Containment and forensic analysis

  • Isolate any dev machine or CI runner that has run npm install with compromised versions of @tanstack/*.
  • Obtain a forensic image before cleaning, especially if there are npm tokens with the threat message.
  • Do not immediately revoke npm tokens with IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner until you isolate and analyze the machine.

Persistence removal:

On developer machines

  • Claude / VS Code:
    • Delete .claude/router_runtime.js and .claude/setup.mjs.
    • Review/restore .claude/settings.json.
    • Review .vscode/tasks.json and delete .vscode/setup.mjs.
  • Token monitoring services:
    • macOS: launchctl unload ~/Library/LaunchAgents/com.user.gh-token-monitor.plist and delete plist, ~/.local/bin/gh-token-monitor.sh, and ~/.config/gh-token-monitor.
    • Linux: systemctl –user stop/disable gh-token-monitor and delete ~/.config/systemd/user/gh-token-monitor.service.

In repositories

  • Audit .github/workflows/codeql_analysis.yml and recent workflows that use toJSON(secrets) or POST to unexpected domains, and remove or correct them.

Secret rotation

Assuming compromise where a malicious version was installed:

  • Rotate:
    • npm tokens (including CI ones),
    • PATs and GitHub tokens (user and Actions),
    • AWS, GCP, Azure, Vault and other cloud credentials,
    • SSH keys used on those machines or runners.
  • If there are cryptocurrency wallets, move funds to new wallets.

Solution and hardening recommendations

Dependency update / cleanup

  • Pin to safe versions of @tanstack/* according to the clean version tables published by TanStack and StepSecurity/Snyk reports; the operational rule is to revert to the last version prior to the attack dates (May 11, 2026 for TanStack) and remove versions published in that window.
  • After adjusting versions, run:
    • rm -rf node_modules
    • npm install (or pnpm/yarn equivalent) to regenerate the tree with non-compromised versions.

Harden CI/CD pipelines

Specific best practices for this case:

  • Restrict OIDC token permissions in workflows to the minimum (id-token: write only where essential) and use separate jobs with least privilege policies for publishing to registries.
  • Implement a cooldown for new package versions (e.g., 3-10 days) in an intermediate registry (Artifactory, Nexus, or solutions like StepSecurity Secure Registry), so that packages published minutes ago are not automatically consumed in CI.
  • Implement egress control on CI runners (whitelists of allowed domains), similar to solutions like Harden-Runner, which block connections to C2 domains in real time.
  • Automate package reputation and signature checks (SLSA, Sigstore) understanding that provenance does not guarantee absence of compromise, only identifies the pipeline that generated the artifact.

Additional development controls

  • Introduce continuous dependency scanning for malware and known campaigns (Mini Shai-Hulud, Shai-Hulud 2.0, etc.) using updated intelligence feeds from security providers.
  • Limit the use of github: dependencies in package.json, especially when pointing to fork commits, and establish manual reviews in PRs that introduce this pattern.

References

Annex – All compromised packages

In total, the Mini Shai-Hulud campaign affected 170 different packages and resulted in at least 348 malicious artifacts published in the npm and PyPI registries during this wave.

#Scope / packageCompromised versionsEcosystemRelevant notes
1@mistralai/mistralai2.2.3, 2.2.4npmOfficial Mistral AI TypeScript SDK
2@mistralai/mistralai-azure1.7.2, 1.7.3npmClient for Azure deployments
3@mistralai/mistralai-gcp1.7.2, 1.7.3npmClient for GCP deployments
4mistralai2.4.6PyPIMistral Python SDK; treat environments as compromised
5guardrails-ai0.10.1PyPILLM guardrails library affected in the campaign
6@opensearch-project/opensearch3.6.2npmOpenSearch-related client
7@tanstack/router-utils1.161.11, 1.161.14npmPart of the TanStack routing stack
8@tanstack/router-core1.169.5, 1.169.8npmTanStack router core
9@tanstack/history1.161.9, 1.161.12npmHistory management for the router
10@tanstack/react-router1.169.5, 1.169.8npmReact router, widely used in front-end
11@tanstack/react-router-devtools1.166.16, 1.166.19npmReact router devtools
12@tanstack/react-router-ssr-query1.166.15, 1.166.18npmSSR + queries integration
13@tanstack/react-start1.167.68, 1.167.71npmreact-start full-stack framework
14@tanstack/react-start-client1.166.51, 1.166.54npmreact-start client
15@tanstack/react-start-rsc0.0.47, 0.0.50npmReact Server Components support
16@tanstack/react-start-server1.166.55, 1.166.58npmreact-start server
17@tanstack/router-cli1.166.46, 1.166.49npmTanStack router CLI
18@tanstack/router-devtools1.166.16, 1.166.19npmRouter (core) devtools
19@tanstack/router-devtools-core1.167.6, 1.167.9npmRouter devtools core
20@tanstack/router-generator1.166.45, 1.166.48npmRoute generator
21@tanstack/router-plugin1.167.38, 1.167.41npmRouter plugin
22@tanstack/router-ssr-query-core1.168.3, 1.168.6npmCore for SSR + queries
23@tanstack/router-vite-plugin1.166.53, 1.166.56npmVite plugin for router
24@tanstack/solid-router1.169.5, 1.169.8npmRouter for SolidJS
25@tanstack/solid-router-devtools1.166.16, 1.166.19npmDevtools for Solid router
26@tanstack/solid-router-ssr-query1.166.15, 1.166.18npmSSR + queries for Solid
27@tanstack/solid-start1.167.65, 1.167.68npmSolid Start framework
28@tanstack/solid-start-client1.166.50, 1.166.53npmSolid Start client
29@tanstack/solid-start-server1.166.54, 1.166.57npmSolid Start server
30@tanstack/start-client-core1.168.5, 1.168.8npmTanStack Start client core
31@tanstack/start-fn-stubs1.161.9, 1.161.12npmStart function stubs
32@tanstack/start-plugin-core1.169.23, 1.169.26npmStart plugins core
33@tanstack/start-server-core1.167.33, 1.167.36npmStart server core
34@tanstack/start-static-server-functions1.166.44, 1.166.47npmStatic server functions
35@tanstack/start-storage-context1.166.38, 1.166.41npmStart storage context
36@tanstack/arktype-adapter1.166.12, 1.166.15npmArkType types adapter
37@tanstack/valibot-adapter1.166.12, 1.166.15npmValibot adapter
38@tanstack/virtual-file-routes1.161.10, 1.161.13npmVirtual file-based routes
39@tanstack/vue-router1.169.5, 1.169.8npmRouter for Vue
40@tanstack/vue-router-devtools1.166.16, 1.166.19npmVue router devtools
41@tanstack/vue-router-ssr-query1.166.15, 1.166.18npmSSR + queries for Vue
42@tanstack/vue-start1.167.61, 1.167.64npmVue Start framework
43@tanstack/vue-start-client1.166.46, 1.166.49npmVue Start client
44@tanstack/vue-start-server1.166.50, 1.166.53npmVue Start server
45@tanstack/zod-adapter1.166.12, 1.166.15npmZod adapter
46@tanstack/eslint-plugin-router1.161.9, 1.161.12npmESLint plugin for router
47@tanstack/eslint-plugin-start0.0.4, 0.0.7npmESLint plugin for Start
48@squawk/airways0.4.2, 0.4.3, 0.4.5npmPart of the Squawk aeronautical ecosystem
49@squawk/airport-data0.7.4, 0.7.5, 0.7.7npmAirport data
50@squawk/airports0.6.2, 0.6.3, 0.6.5npmAirports API
51@squawk/airspace0.8.1, 0.8.2, 0.8.4npmAirspace data
52@squawk/airspace-data0.5.3, 0.5.4, 0.5.6npmAirspace dataset
53@squawk/airway-data0.5.4, 0.5.5, 0.5.7npmAirways dataset
54@squawk/fix-data0.6.4, 0.6.5, 0.6.7npmNavigation fixes (points)
55@squawk/fixes0.3.2, 0.3.3, 0.3.5npmFixes API
56@squawk/flight-math0.5.4, 0.5.5, 0.5.7npmFlight calculation utilities
57@squawk/flightplan0.5.2, 0.5.3, 0.5.5npmFlight planning
58@squawk/geo0.4.4, 0.4.5, 0.4.7npmGeospatial functions
59@squawk/icao-registry0.5.2, 0.5.3, 0.5.5npmICAO registry
60@squawk/icao-registry-data0.8.4, 0.8.5, 0.8.7npmICAO dataset
61@squawk/mcp0.9.1, 0.9.2, 0.9.4npmMCP tools
62@squawk/navaid-data0.6.4, 0.6.5, 0.6.7npmRadio-aid data
63@squawk/navaids0.4.2, 0.4.3, 0.4.5npmNavaids API
64@squawk/notams0.3.6, 0.3.7, 0.3.9npmNOTAMs
65@squawk/procedure-data0.7.3, 0.7.4, 0.7.6npmProcedure data
66@squawk/procedures0.5.2, 0.5.3, 0.5.5npmProcedures API
67@squawk/types0.8.1, 0.8.2, 0.8.4npmShared types
68@squawk/units0.4.3, 0.4.4, 0.4.6npmUnits of measurement
69@squawk/weather0.5.6, 0.5.7, 0.5.9npmWeather data
70@uipath/* (multiple packages)Versions listed in your original textnpmMore than 50 packages in the @uipath scope affected
71@draftauth/client0.2.1, 0.2.2npmDraftAuth authentication client
72@draftauth/core0.13.1, 0.13.2npmDraftAuth core
73@draftlab/auth0.24.1, 0.24.2npmDraftLab auth
74@draftlab/auth-router0.5.1, 0.5.2npmAuthentication router
75@draftlab/db0.16.1, 0.16.2npmDraftLab data layer
76@beproduct/nestjs-auth0.1.2–0.1.17, 0.1.19npmMultiple compromised versions
77@dirigible-ai/sdk0.6.2, 0.6.3npmDirigible AI SDK
78@ml-toolkit-ts/preprocessing1.0.2, 1.0.3npmPart of ml-toolkit-ts
79@ml-toolkit-ts/xgboost1.0.3, 1.0.4npmXGBoost binding
80ml-toolkit-ts1.0.4, 1.0.5npmMain package
81@tallyui/* (components, core, etc.)Versions indicated in your listnpm@tallyui scope affected as a block
82@mesadev/rest0.28.3npmPart of the MesaDev ecosystem
83@mesadev/saguaro0.4.22npmMesaDev package
84@mesadev/sdk0.28.3npmMesaDev SDK
85safe-action0.8.3, 0.8.4npmSafe actions package
86@supersurkhet/cli0.0.2–0.0.7npmSupersurkhet CLI
87@supersurkhet/sdk0.0.2–0.0.7npmSupersurkhet SDK
88cmux-agent-mcp0.1.3–0.1.8npmMCP tool
89git-git-git1.0.8–1.0.10, 1.0.12npmgit utilities
90git-branch-selector1.3.3–1.3.5, 1.3.7npmgit branch selector
91nextmove-mcp0.1.3–0.1.5, 0.1.7npmMCP client
92agentwork-cli0.1.4, 0.1.5npmAgentWork CLI
93wot-api0.8.1, 0.8.2, 0.8.4npmWoT API
94cross-stitch1.1.3, 1.1.4, 1.1.6npmUtility library
95ts-dna3.0.1, 3.0.2, 3.0.4npmTypeScript package