On May 11, 2026, a coordinated supply chain attack targeting the npm and PyPI ecosystems occurred. This attack simultaneously affected multiple well-known npm and PyPI packages.
This was not a run-of-the-mill malicious package injection event. Instead of simply relying on stolen npm credentials, the attackers leveraged GitHub Actions, cache poisoning, OIDC Token abuse, malicious dependencies, persistent daemon processes, and multi-channel C2 infrastructure to construct a complete supply chain intrusion vector.
Specific release times of the malicious TanStack-related versions: Beijing Time (UTC+8) May 12, between 3:20 AM and 3:26 AM.
1. Incident Overview
This attack affected several high-value packages across both the npm and PyPI ecosystems.
Mainly affected packages:
- Packages under
@tanstack, such as@tanstack/react-router—one of the most widely used routing libraries in the React ecosystem with approximately 12 million weekly downloads—and@tanstack/vue-routerwas also affected. - Packages under
@uipath, including tools, CLIs, and Agent SDKs related to the UiPath Enterprise Automation Platform. @mistralai/mistralai, the official Mistral AI TypeScript client.@opensearch-project/opensearchmistralai: 2.4.6 on PyPIguardrails-ai: 0.10.1 on PyPI
The npm security team has noticed the attack and quickly removed the malicious versions.
You can view the list of known affected packages at the end of this article.
2. TanStack Attack Chain: GitHub Actions Becomes the Breakthrough
The compromise of TanStack stemmed from a complex exploit chain targeting GitHub Actions (involving three vulnerabilities):
The attack chain was roughly as follows:
- The attacker forked the
TanStack/routerrepository. - The attacker renamed the forked repository to
zblgg/configurationto bypass fork list audits. - The attacker submitted a Pull Request, triggering the
pull_request_targetworkflow in the target repository. - This workflow incorrectly checked out and executed code from the attacker's fork.
- The attacker poisoned the GitHub Actions cache, writing a malicious pnpm store into the cache.
- When a subsequent legitimate maintainer's PR was merged into the main branch, the release workflow restored the poisoned cache.
- A binary controlled by the attacker read the OIDC Token from the memory space of the GitHub Actions Runner process.
- The attacker used these Tokens to publish malicious versions of the npm packages.
The most notable aspect of this vector is that the attacker did not need to steal npm credentials to publish malicious packages.
This demonstrates that in modern software supply chains, package registry accounts are no longer the only critical assets. CI/CD workflows, caches, OIDC Tokens, publishing permissions, and workflow boundaries are also high-value attack surfaces.
UiPath Infection Variant
Shortly after @tanstack was compromised, @uipath was also breached. These packages used a preinstall script (node setup.mjs) to download the Bun runtime environment and execute a malicious payload. This is identical to the delivery mechanism seen in the previous SAP compromise. The UiPath variant used a re-obfuscated payload and a different Campaign Key, but connected to the same C2 (Command and Control) infrastructure.
3. Malicious Payload: Credential Theft and Worm-like Propagation
This malicious payload has self-propagation capabilities, primarily targeting the theft of the following types of credentials and identity materials:
It mainly targets:
- CI/CD tokens (GitHub Actions OIDC, GitLab, CircleCI)
- Cloud service credentials (AWS IMDSv2, GCP, Azure)
- Kubernetes service accounts
- HashiCorp Vault
- Package registry tokens
Worm Behavior: The malware uses stolen npm tokens and GitHub Actions OIDC tokens to publish poisoned versions of other packages that the victim has write access to, thereby spreading rapidly like a worm throughout the npm ecosystem.
Destructive Local Daemon Process
If the malware discovers a valid GitHub Personal Access Token starting with ghp_ or an OAuth Token starting with gho_, it installs a persistent daemon process locally:
macOS:
1 | ~/Library/LaunchAgents/com.user.gh-token-monitor.plist |
Linux:
1 | ~/.config/systemd/user/gh-token-monitor.service |
If it receives a 40X error due to the GitHub token being revoked, the process will attempt to execute rm -rf ~/, wiping the user's home directory completely! However, this daemon process will automatically exit after running for 24 hours.
Note: Similar to previous Mini Shai-Hulud variants, the malware checks if the system's language is set to Russian. If so, it terminates immediately and does not steal any data.
4. Detection Guide
- Audit Packages: Search lockfiles and CI logs for affected package versions. Check if
router_init.jsorsetup.mjsexists in the package root directory. - Detect and Clean Persistent Threats: Search for the
gh-token-monitordaemon process on developer machines and delete it (refer to the paths in the indicators below). - Rotate All Credentials: If you suspect you have been affected, immediately rotate GitHub tokens, npm tokens, AWS credentials, Vault tokens, Kubernetes service accounts, and CI/CD secrets.
Warning: Before revoking GitHub tokens, make sure to inspect and remove the persistent daemon process to avoid triggering the malware's destructive command (
rm -rf ~/). - Inspect IDE Directories: Check for leftover
router_runtime.jsorsetup.mjsfiles in.claude/and.vscode/directories. These files might persist even after runningnpm uninstall. - Block C2 Target Domains: Block
git-tanstack.comand*.getsession.orgat the DNS/proxy level.
Files
| File Name | Details | SHA-256 / SHA-1 |
|---|---|---|
router_init.js |
2,341,681 bytes | SHA256: ``ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c` |
router_init.js |
2,339,346 bytes | SHA256:2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96SHA1: e7d582b98ca80690883175470e96f703ef6dc497 |
setup.mjs |
5,047 bytes | SHA256:2258284d65f63829bd67eaba01ef6f1ada2f593f9bbe41678b2df360bd90d3dfSHA1: 12f35b1081b17d21815b35feb57ab03d02482116 |
opensearch_init.js |
- | SHA1:820fa07a7328b6cf2b417078e103721d4d8f2e79 |
Network IOCs
| Type | Target |
|---|---|
| C2 Domain | git-tanstack.com |
| Session Seed Nodes | seed1.getsession.org, seed2.getsession.org, seed3.getsession.org |
| Session File Server | filev2.getsession.org |
| C2 IP Address | 83.142.209.194 |
| PyPI Payload URL | git-tanstack.com/tmp/transformers.pyz |
Other Indicators
| Category | Indicator Description |
|---|---|
| Service Name | gh-token-monitor |
| macOS Path | ~/Library/LaunchAgents/com.user.gh-token-monitor.plist |
| Linux Path | ~/.config/systemd/user/gh-token-monitor.service |
| Runtime Artifacts | router_runtime.js , tanstack_runner.js |
| Hook Execution Command | preinstall: node setup.mjs |
| Malicious Git Dependency | github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c |
| Specific Execution Env | Bun Version 1.3.13 |
5. Affected Packages
PyPI
NPM
| Package | Affected Versions |
|---|---|
| @beproduct/nestjs-auth | 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.1.9, 0.1.10, 0.1.11, 0.1.12, 0.1.13, 0.1.14, 0.1.15, 0.1.16, 0.1.17, 0.1.18, 0.1.19 |
| @cap-js/db-service | 2.10.1 |
| @cap-js/postgres | 2.2.2 |
| @cap-js/sqlite | 2.2.2 |
| @dirigible-ai/sdk | 0.6.2, 0.6.3 |
| @draftauth/client | 0.2.1, 0.2.2 |
| @draftauth/core | 0.13.1, 0.13.2 |
| @draftlab/auth | 0.24.1, 0.24.2 |
| @draftlab/auth-router | 0.5.1, 0.5.2 |
| @draftlab/db | 0.16.1, 0.16.2 |
| @mesadev/rest | 0.28.3 |
| @mesadev/saguaro | 0.4.22 |
| @mesadev/sdk | 0.28.3 |
| @mistralai/mistralai | 2.2.2, 2.2.3, 2.2.4 |
| @mistralai/mistralai-azure | 1.7.1, 1.7.2, 1.7.3 |
| @mistralai/mistralai-gcp | 1.7.1, 1.7.2, 1.7.3 |
| @ml-toolkit-ts/preprocessing | 1.0.2, 1.0.3 |
| @ml-toolkit-ts/xgboost | 1.0.3, 1.0.4 |
| @opensearch-project/opensearch | 3.5.3, 3.6.2, 3.7.0, 3.8.0 |
| @squawk/airport-data | 0.7.4, 0.7.5, 0.7.6, 0.7.7, 0.7.8 |
| @squawk/airports | 0.6.2, 0.6.3, 0.6.4, 0.6.5, 0.6.6 |
| @squawk/airspace | 0.8.1, 0.8.2, 0.8.3, 0.8.4, 0.8.5 |
| @squawk/airspace-data | 0.5.3, 0.5.4, 0.5.5, 0.5.6, 0.5.7 |
| @squawk/airway-data | 0.5.4, 0.5.5, 0.5.6, 0.5.7, 0.5.8 |
| @squawk/airways | 0.4.2, 0.4.3, 0.4.4, 0.4.5, 0.4.6 |
| @squawk/fix-data | 0.6.4, 0.6.5, 0.6.6, 0.6.7, 0.6.8 |
| @squawk/fixes | 0.3.2, 0.3.3, 0.3.4, 0.3.5, 0.3.6 |
| @squawk/flight-math | 0.5.4, 0.5.5, 0.5.6, 0.5.7, 0.5.8 |
| @squawk/flightplan | 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.5.6 |
| @squawk/geo | 0.4.4, 0.4.5, 0.4.6, 0.4.7, 0.4.8 |
| @squawk/icao-registry | 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.5.6 |
| @squawk/icao-registry-data | 0.8.4, 0.8.5, 0.8.6, 0.8.7, 0.8.8 |
| @squawk/mcp | 0.9.1, 0.9.2, 0.9.3, 0.9.4, 0.9.5 |
| @squawk/navaid-data | 0.6.4, 0.6.5, 0.6.6, 0.6.7, 0.6.8 |
| @squawk/navaids | 0.4.2, 0.4.3, 0.4.4, 0.4.5, 0.4.6 |
| @squawk/notams | 0.3.6, 0.3.7, 0.3.8, 0.3.9, 0.3.10 |
| @squawk/procedure-data | 0.7.3, 0.7.4, 0.7.5, 0.7.6, 0.7.7 |
| @squawk/procedures | 0.5.2, 0.5.3, 0.5.4, 0.5.5, 0.5.6 |
| @squawk/types | 0.8.1, 0.8.2, 0.8.3, 0.8.4, 0.8.5 |
| @squawk/units | 0.4.3, 0.4.4, 0.4.5, 0.4.6, 0.4.7 |
| @squawk/weather | 0.5.6, 0.5.7, 0.5.8, 0.5.9, 0.5.10 |
| @supersurkhet/cli | 0.0.2, 0.0.3, 0.0.4, 0.0.5, 0.0.6, 0.0.7 |
| @supersurkhet/sdk | 0.0.2, 0.0.3, 0.0.4, 0.0.5, 0.0.6, 0.0.7 |
| @tallyui/components | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/connector-medusa | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/connector-shopify | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/connector-vendure | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/connector-woocommerce | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/core | 0.2.1, 0.2.2, 0.2.3 |
| @tallyui/database | 1.0.1, 1.0.2, 1.0.3 |
| @tallyui/pos | 0.1.1, 0.1.2, 0.1.3 |
| @tallyui/storage-sqlite | 0.2.1, 0.2.2, 0.2.3 |
| @tallyui/theme | 0.2.1, 0.2.2, 0.2.3 |
| @tanstack/arktype-adapter | 1.166.12, 1.166.15 |
| @tanstack/eslint-plugin-router | 1.161.9, 1.161.12 |
| @tanstack/eslint-plugin-start | 0.0.4, 0.0.7 |
| @tanstack/history | 1.161.9, 1.161.12 |
| @tanstack/nitro-v2-vite-plugin | 1.154.12, 1.154.15 |
| @tanstack/react-router | 1.169.5, 1.169.8 |
| @tanstack/react-router-devtools | 1.166.16, 1.166.19 |
| @tanstack/react-router-ssr-query | 1.166.15, 1.166.18 |
| @tanstack/react-start | 1.167.68, 1.167.71 |
| @tanstack/react-start-client | 1.166.51, 1.166.54 |
| @tanstack/react-start-rsc | 0.0.47, 0.0.50 |
| @tanstack/react-start-server | 1.166.55, 1.166.58 |
| @tanstack/router-cli | 1.166.46, 1.166.49 |
| @tanstack/router-core | 1.169.5, 1.169.8 |
| @tanstack/router-devtools | 1.166.16, 1.166.19 |
| @tanstack/router-devtools-core | 1.167.6, 1.167.9 |
| @tanstack/router-generator | 1.166.45, 1.166.48 |
| @tanstack/router-plugin | 1.167.38, 1.167.41 |
| @tanstack/router-ssr-query-core | 1.168.3, 1.168.6 |
| @tanstack/router-utils | 1.161.11, 1.161.14 |
| @tanstack/router-vite-plugin | 1.166.53, 1.166.56 |
| @tanstack/solid-router | 1.169.5, 1.169.8 |
| @tanstack/solid-router-devtools | 1.166.16, 1.166.19 |
| @tanstack/solid-router-ssr-query | 1.166.15, 1.166.18 |
| @tanstack/solid-start | 1.167.65, 1.167.68 |
| @tanstack/solid-start-client | 1.166.50, 1.166.53 |
| @tanstack/solid-start-server | 1.166.54, 1.166.57 |
| @tanstack/start-client-core | 1.168.5, 1.168.8 |
| @tanstack/start-fn-stubs | 1.161.9, 1.161.12 |
| @tanstack/start-plugin-core | 1.169.23, 1.169.26 |
| @tanstack/start-server-core | 1.167.33, 1.167.36 |
| @tanstack/start-static-server-functions | 1.166.44, 1.166.47 |
| @tanstack/start-storage-context | 1.166.38, 1.166.41 |
| @tanstack/valibot-adapter | 1.166.12, 1.166.15 |
| @tanstack/virtual-file-routes | 1.161.10, 1.161.13 |
| @tanstack/vue-router | 1.169.5, 1.169.8 |
| @tanstack/vue-router-devtools | 1.166.16, 1.166.19 |
| @tanstack/vue-router-ssr-query | 1.166.15, 1.166.18 |
| @tanstack/vue-start | 1.167.61, 1.167.64 |
| @tanstack/vue-start-client | 1.166.46, 1.166.49 |
| @tanstack/vue-start-server | 1.166.50, 1.166.53 |
| @tanstack/zod-adapter | 1.166.12, 1.166.15 |
| @taskflow-corp/cli | 0.1.24, 0.1.25, 0.1.26, 0.1.27, 0.1.28, 0.1.29 |
| @tolka/cli | 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6 |
| @uipath/access-policy-sdk | 0.3.1 |
| @uipath/access-policy-tool | 0.3.1 |
| @uipath/admin-tool | 0.1.1 |
| @uipath/agent-sdk | 1.0.2 |
| @uipath/agent-tool | 1.0.1 |
| @uipath/agent.sdk | 0.0.18 |
| @uipath/aops-policy-tool | 0.3.1 |
| @uipath/ap-chat | 1.5.7 |
| @uipath/api-workflow-tool | 1.0.1 |
| @uipath/apollo-core | 5.9.2 |
| @uipath/apollo-react | 4.24.5 |
| @uipath/apollo-wind | 2.16.2 |
| @uipath/auth | 1.0.1 |
| @uipath/case-tool | 1.0.1 |
| @uipath/cli | 1.0.1 |
| @uipath/codedagent-tool | 1.0.1 |
| @uipath/codedagents-tool | 0.1.12 |
| @uipath/codedapp-tool | 1.0.1 |
| @uipath/common | 1.0.1 |
| @uipath/context-grounding-tool | 0.1.1 |
| @uipath/data-fabric-tool | 1.0.2 |
| @uipath/docsai-tool | 1.0.1 |
| @uipath/filesystem | 1.0.1 |
| @uipath/flow-tool | 1.0.2 |
| @uipath/functions-tool | 1.0.1 |
| @uipath/gov-tool | 0.3.1 |
| @uipath/identity-tool | 0.1.1 |
| @uipath/insights-sdk | 1.0.1 |
| @uipath/insights-tool | 1.0.1 |
| @uipath/integrationservice-sdk | 1.0.2 |
| @uipath/integrationservice-tool | 1.0.2 |
| @uipath/llmgw-tool | 1.0.1 |
| @uipath/maestro-sdk | 1.0.1 |
| @uipath/maestro-tool | 1.0.1 |
| @uipath/orchestrator-tool | 1.0.1 |
| @uipath/packager-tool-apiworkflow | 0.0.19 |
| @uipath/packager-tool-bpmn | 0.0.9 |
| @uipath/packager-tool-case | 0.0.9 |
| @uipath/packager-tool-connector | 0.0.19 |
| @uipath/packager-tool-flow | 0.0.19 |
| @uipath/packager-tool-functions | 0.1.1 |
| @uipath/packager-tool-webapp | 1.0.6 |
| @uipath/packager-tool-workflowcompiler | 0.0.16 |
| @uipath/packager-tool-workflowcompiler-browser | 0.0.34 |
| @uipath/platform-tool | 1.0.1 |
| @uipath/project-packager | 1.1.16 |
| @uipath/resource-tool | 1.0.1 |
| @uipath/resourcecatalog-tool | 0.1.1 |
| @uipath/resources-tool | 0.1.11 |
| @uipath/robot | 1.3.4 |
| @uipath/rpa-legacy-tool | 1.0.1 |
| @uipath/rpa-tool | 0.9.5 |
| @uipath/solution-packager | 0.0.35 |
| @uipath/solution-tool | 1.0.1 |
| @uipath/solutionpackager-sdk | 1.0.11 |
| @uipath/solutionpackager-tool-core | 0.0.34 |
| @uipath/tasks-tool | 1.0.1 |
| @uipath/telemetry | 0.0.7 |
| @uipath/test-manager-tool | 1.0.2 |
| @uipath/tool-workflowcompiler | 0.0.12 |
| @uipath/traces-tool | 1.0.1 |
| @uipath/ui-widgets-multi-file-upload | 1.0.1 |
| @uipath/uipath-python-bridge | 1.0.1 |
| @uipath/vertical-solutions-tool | 1.0.1 |
| @uipath/vss | 0.1.6 |
| @uipath/widget.sdk | 1.2.3 |
| agentwork-cli | 0.1.4, 0.1.5 |
| cmux-agent-mcp | 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7, 0.1.8 |
| cross-stitch | 1.1.3, 1.1.4, 1.1.5, 1.1.6, 1.1.7 |
| git-branch-selector | 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7 |
| git-git-git | 1.0.8, 1.0.9, 1.0.10, 1.0.11, 1.0.12 |
| guardrails-ai | 0.10.1 |
| intercom-client | 7.0.4 |
| lightning | 2.6.2, 2.6.3 |
| mbt | 1.2.48 |
| mistralai | 2.4.6 |
| ml-toolkit-ts | 1.0.4, 1.0.5 |
| nextmove-mcp | 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7 |
| safe-action | 0.8.3, 0.8.4 |
| ts-dna | 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.0.5 |
| wot-api | 0.8.1, 0.8.2, 0.8.3, 0.8.4 |
Conclusion
This attack illustrates a new trend in supply chain threats: attackers no longer limit their focus to package registry accounts. Instead, they chain together GitHub Actions, caching, OIDC, developer terminals, CI/CD secrets, and open-source release workflows into a cohesive exploit path.
For security teams, simply "upgrading dependencies" is no longer sufficient. Effective protection must cover the entire pipeline—from code commits, CI execution, credential issuing, and package publishing, down to the developer's local environment.
This incident serves as a critical reminder: CI/CD is part of the production environment, dependencies are part of the executable codebase, and developer machines are part of the supply chain boundary.
Comments