TanStack npm Supply Chain Attack: Detect, Fix, and Recover
On May 11, 2026, attackers published 84 malicious versions across 42 @tanstack/* npm packages between approximately 19:20 and 19:26 UTC. The attack did not begin with a stolen npm password. Instead, the attacker abused a GitHub Actions chain involving a pull_request_target workflow, GitHub Actions cache poisoning, and extraction of an OIDC token from the Actions runner process. The malicious packages were published through TanStack’s legitimate trusted-publisher identity, which made the incident especially dangerous. (TanStack postmortem)
The payload executed during package installation and attempted to steal secrets from developer machines and CI environments, including cloud credentials, GitHub tokens, npm tokens, Kubernetes service-account tokens, Vault tokens, and SSH private keys. Any developer workstation or CI runner that installed an affected version on May 11, 2026 should be treated as potentially compromised. (TanStack postmortem)
Executive summary
This was a supply-chain malware incident, not a normal dependency vulnerability.
The important takeaways are:
- If you installed an affected package version, do not assume that removing the package is enough.
- The malware attempted credential theft and self-propagation.
- Some write-ups report persistence mechanisms on developer machines, including Claude Code hooks, VS Code task hooks, and a GitHub-token monitoring daemon.
- Rotate credentials after removing persistence, especially if the
gh-token-monitordaemon is present. - CI runners that installed affected versions should be rebuilt from clean images, not merely cleaned in place.
The official GitHub advisory tracks the issue as CVE-2026-45321 and GHSA-g7cv-rxg3-hmpx, with critical severity. (GitHub Advisory)
What happened?
The attacker created a fork of the TanStack router repository and used a malicious pull request to poison GitHub Actions cache data. A legitimate TanStack release workflow later restored that poisoned cache. From there, attacker-controlled code ran inside the release workflow and extracted the GitHub Actions OIDC token from runner memory. The attacker then used that token to publish malicious npm package versions. (TanStack postmortem)
TanStack’s own postmortem describes three linked weaknesses:
- A
pull_request_targetworkflow that checked out and executed fork-controlled code. - GitHub Actions cache poisoning across the fork/base trust boundary.
- Runtime extraction of a GitHub Actions OIDC token from the runner process. (TanStack postmortem)
As a result, the malicious packages appeared to come from the legitimate publishing pipeline. Security researchers reported that the broader “Mini Shai-Hulud” campaign produced packages with valid-looking SLSA provenance, showing that provenance alone is not enough when the trusted build pipeline itself has been hijacked. (StepSecurity)
What did the malware do?
When a developer or CI environment installed an affected version, the package could execute an install-time payload. The key indicator inside package.json was an unexpected optional dependency:
"optionalDependencies": {
"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}
Alongside it, the package shipped an unexpected file at its root:
router_init.js
The payload attempted to harvest credentials from locations such as:
- AWS metadata and Secrets Manager
- GCP metadata
- Kubernetes service-account tokens
- HashiCorp Vault tokens
~/.npmrc- GitHub tokens from environment variables,
ghCLI config, and.git-credentials - SSH private keys under
~/.ssh/
It also attempted to exfiltrate data through Session/Oxen infrastructure and to self-propagate by enumerating npm packages maintained by the compromised user or environment. (TanStack postmortem)
Security vendors also reported persistence behavior in developer tooling directories and a GitHub-token monitoring daemon on Linux and macOS. That daemon reportedly polled GitHub and could attempt destructive behavior if the monitored token was revoked, which is why the remediation order matters. (Snyk)
Affected TanStack packages
The official advisory lists 42 affected TanStack packages, each with two malicious versions and a patched follow-up version. Update to the patched version or later. (GitHub Advisory)
| Package | Affected versions | Patched version |
|---|---|---|
@tanstack/arktype-adapter | 1.166.12, 1.166.15 | 1.166.16 |
@tanstack/eslint-plugin-router | 1.161.9, 1.161.12 | 1.161.13 |
@tanstack/eslint-plugin-start | 0.0.4, 0.0.7 | 0.0.8 |
@tanstack/history | 1.161.9, 1.161.12 | 1.161.13 |
@tanstack/nitro-v2-vite-plugin | 1.154.12, 1.154.15 | 1.154.16 |
@tanstack/react-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/react-router-devtools | 1.166.16, 1.166.19 | 1.166.20 |
@tanstack/react-router-ssr-query | 1.166.15, 1.166.18 | 1.166.19 |
@tanstack/react-start | 1.167.68, 1.167.71 | 1.167.72 |
@tanstack/react-start-client | 1.166.51, 1.166.54 | 1.166.55 |
@tanstack/react-start-rsc | 0.0.47, 0.0.50 | 0.0.51 |
@tanstack/react-start-server | 1.166.55, 1.166.58 | 1.166.59 |
@tanstack/router-cli | 1.166.46, 1.166.49 | 1.166.50 |
@tanstack/router-core | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/router-devtools | 1.166.16, 1.166.19 | 1.166.20 |
@tanstack/router-devtools-core | 1.167.6, 1.167.9 | 1.167.10 |
@tanstack/router-generator | 1.166.45, 1.166.48 | 1.166.49 |
@tanstack/router-plugin | 1.167.38, 1.167.41 | 1.167.42 |
@tanstack/router-ssr-query-core | 1.168.3, 1.168.6 | 1.168.7 |
@tanstack/router-utils | 1.161.11, 1.161.14 | 1.161.15 |
@tanstack/router-vite-plugin | 1.166.53, 1.166.56 | 1.166.57 |
@tanstack/solid-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/solid-router-devtools | 1.166.16, 1.166.19 | 1.166.20 |
@tanstack/solid-router-ssr-query | 1.166.15, 1.166.18 | 1.166.19 |
@tanstack/solid-start | 1.167.65, 1.167.68 | 1.167.69 |
@tanstack/solid-start-client | 1.166.50, 1.166.53 | 1.166.54 |
@tanstack/solid-start-server | 1.166.54, 1.166.57 | 1.166.58 |
@tanstack/start-client-core | 1.168.5, 1.168.8 | 1.168.9 |
@tanstack/start-fn-stubs | 1.161.9, 1.161.12 | 1.161.13 |
@tanstack/start-plugin-core | 1.169.23, 1.169.26 | 1.169.27 |
@tanstack/start-server-core | 1.167.33, 1.167.36 | 1.167.37 |
@tanstack/start-static-server-functions | 1.166.44, 1.166.47 | 1.166.48 |
@tanstack/start-storage-context | 1.166.38, 1.166.41 | 1.166.42 |
@tanstack/valibot-adapter | 1.166.12, 1.166.15 | 1.166.16 |
@tanstack/virtual-file-routes | 1.161.10, 1.161.13 | 1.161.14 |
@tanstack/vue-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/vue-router-devtools | 1.166.16, 1.166.19 | 1.166.20 |
@tanstack/vue-router-ssr-query | 1.166.15, 1.166.18 | 1.166.19 |
@tanstack/vue-start | 1.167.61, 1.167.64 | 1.167.65 |
@tanstack/vue-start-client | 1.166.46, 1.166.49 | 1.166.50 |
@tanstack/vue-start-server | 1.166.50, 1.166.53 | 1.166.54 |
@tanstack/zod-adapter | 1.166.12, 1.166.15 | 1.166.16 |
TanStack reported the following families as confirmed clean: @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, @tanstack/store, and the @tanstack/start meta-package. Note that this does not include similarly named @tanstack/start-* packages, several of which were affected. (TanStack postmortem)
How to check whether you were exposed
The safest rule is this:
If a developer machine or CI runner installed an affected version on May 11, 2026, treat the environment as compromised.
Do not run npm install just to test exposure. Instead, inspect lockfiles, existing node_modules, package tarballs, and system persistence locations.
1. Search your project for known malicious markers
From the root of each repository:
grep -R --line-number -E \
'79ac49eedf774dd4b0cfa308722bc463cfe5885c|@tanstack/setup|router_init\.js|tanstack_runner\.js' \
package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json node_modules 2>/dev/null
Any hit for the malicious git ref, @tanstack/setup, or router_init.js should be treated as a serious indicator.
2. Search for the payload file
Linux/macOS:
find . -name "router_init.js" -o -name "tanstack_runner.js"
If you find router_init.js, calculate its hash:
shasum -a 256 path/to/router_init.js
Known reported SHA-256 for router_init.js:
ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c
Known reported SHA-256 for tanstack_runner.js:
2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96
These hashes and filenames were published by security researchers as indicators of compromise. (Snyk)
3. Inspect package tarballs without executing install scripts
To inspect a package version safely:
mkdir /tmp/tanstack-check
cd /tmp/tanstack-check
npm pack @tanstack/react-router@1.169.5
tar -xzf tanstack-react-router-*.tgz
grep -A5 "optionalDependencies" package/package.json
ls -la package/router_init.js
A malicious package contains the @tanstack/setup git dependency and the unexpected router_init.js payload file at the package root. The GitHub advisory specifically recommends npm pack inspection because it downloads the tarball without executing lifecycle scripts. (GitHub Advisory)
4. Check for local persistence
Search your home directory and project directories for persistence files reported in the campaign:
find "$HOME" \
-path "*/.claude/router_runtime.js" -o \
-path "*/.claude/setup.mjs" -o \
-path "*/.vscode/setup.mjs" -o \
-path "*/.config/systemd/user/gh-token-monitor.service" -o \
-path "*/.local/bin/gh-token-monitor.sh" -o \
-path "*/Library/LaunchAgents/com.user.gh-token-monitor.plist" \
2>/dev/null
Also inspect Claude Code and VS Code hook/task configuration:
grep -R --line-number -E 'router_runtime|setup\.mjs|gh-token-monitor' \
"$HOME/.claude" .claude .vscode 2>/dev/null
Snyk and Wiz reported persistence artifacts including .claude/router_runtime.js, .claude/setup.mjs, .vscode/setup.mjs, Linux gh-token-monitor systemd user services, and macOS LaunchAgents. (Snyk)
5. Check for suspicious GitHub workflow injection
Look for an unexpected workflow file:
ls -la .github/workflows/codeql_analysis.yml 2>/dev/null
If this file exists and your team did not add it intentionally, inspect it before deleting it:
sed -n '1,200p' .github/workflows/codeql_analysis.yml
Security researchers reported that the malware attempted to inject a GitHub Actions workflow named codeql_analysis.yml to exfiltrate secrets. (Snyk)
6. Check Git history for suspicious dead-drop commits
git log --all --author="claude@users.noreply.github.com" --oneline
git branch -a | grep -i "dependabout"
The attacker used a fabricated claude@users.noreply.github.com identity and Dependabot-style branch names such as dependabout/.../setup-formatter. (TanStack postmortem)
7. Check network and proxy logs
Search DNS, proxy, firewall, EDR, and CI network logs for:
filev2.getsession.org
seed1.getsession.org
seed2.getsession.org
seed3.getsession.org
git-tanstack.com
api.masscan.cloud
litter.catbox.moe/h8nc9u.js
litter.catbox.moe/7rrc6l.mjs
TanStack, GitHub, Snyk, and Wiz reported these as exfiltration, C2, or second-stage payload indicators. (TanStack postmortem)
How to fix an affected workstation
Step 1: Isolate the machine
Disconnect the machine from untrusted networks or place it in a restricted network segment. Preserve logs if this is a company device.
Do not immediately revoke GitHub tokens if you found gh-token-monitor. First remove the persistence mechanism. Researchers reported a token-monitoring daemon that could attempt destructive behavior when a monitored token was revoked. (Snyk)
Step 2: Stop and remove the token-monitor persistence
Linux:
systemctl --user stop gh-token-monitor.service 2>/dev/null
systemctl --user disable gh-token-monitor.service 2>/dev/null
rm -f "$HOME/.config/systemd/user/gh-token-monitor.service"
rm -f "$HOME/.local/bin/gh-token-monitor.sh"
systemctl --user daemon-reload 2>/dev/null
macOS:
launchctl unload "$HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist" 2>/dev/null
rm -f "$HOME/Library/LaunchAgents/com.user.gh-token-monitor.plist"
Step 3: Remove editor and AI-agent persistence hooks
Inspect before deleting, especially in shared repositories:
grep -R --line-number -E 'router_runtime|setup\.mjs|gh-token-monitor' \
"$HOME/.claude" .claude .vscode 2>/dev/null
Then remove suspicious files:
rm -f "$HOME/.claude/router_runtime.js" "$HOME/.claude/setup.mjs"
rm -f ".claude/router_runtime.js" ".claude/setup.mjs"
rm -f ".vscode/setup.mjs"
Open .claude/settings.json and .vscode/tasks.json manually and remove any task or hook that invokes router_runtime.js, setup.mjs, or gh-token-monitor.
Step 4: Remove affected dependencies and reinstall cleanly
From each affected repository:
rm -rf node_modules
rm -f package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json
npm cache clean --force
Then update affected TanStack packages to patched versions or later:
npm install @tanstack/react-router@1.169.9
Repeat for any affected packages your project uses, or update all affected TanStack packages to the patched versions in the table above (or later).
For pnpm:
pnpm store prune
pnpm install
For Yarn:
yarn cache clean
yarn install
Step 5: Rotate credentials after persistence removal
After persistence has been removed, rotate all credentials that were accessible from the machine or CI job.
Prioritize:
- GitHub personal access tokens and OAuth tokens.
- npm tokens and publishing credentials.
- AWS keys, role trusts, and any credentials reachable through IMDS.
- GCP service-account credentials.
- Azure credentials.
- Kubernetes service-account tokens.
- HashiCorp Vault tokens.
- SSH keys.
- Secrets stored in
.envfiles. - Any secrets that may have appeared in Claude Code session logs or other AI-agent history files.
The official advisory recommends rotating all credentials accessible to the install process and reviewing cloud audit logs for activity from affected hosts. (GitHub Advisory)
Step 6: Review account and cloud activity
Check for suspicious activity after the install window:
- GitHub repository creation, branch creation, workflow changes, Actions secret reads, new deploy keys, new PAT usage.
- npm package publishes, token creation, token use, maintainer changes.
- AWS CloudTrail activity from unusual IPs or regions.
- GCP Cloud Audit Logs for service-account use.
- Kubernetes API activity using service-account tokens.
- Vault audit logs for unusual secret reads.
- SSH logins from unusual locations.
Step 7: Rebuild if compromise is confirmed
If you found the payload, persistence files, suspicious workflow injection, or evidence of token exfiltration, the safest remediation is to rebuild the workstation from a known-good image and restore only reviewed project files.
Removing node_modules is not enough if persistence or credential theft occurred.
How to fix affected CI/CD environments
CI runners should be treated more strictly than local machines because they often have access to production secrets.
1. Rebuild runners
For hosted ephemeral CI, invalidate the affected job and ensure the next run starts from a clean image.
For self-hosted runners:
# Example only: adapt to your runner management process.
sudo systemctl stop actions.runner.* 2>/dev/null
Then rebuild the VM, container image, or host from a known-good baseline.
2. Rotate CI secrets
Rotate:
- GitHub Actions secrets.
- Repository and organization secrets.
- npm publish tokens.
- OIDC trusted-publisher grants.
- Cloud deployment credentials.
- Kubernetes deployment tokens.
- Vault tokens.
- SSH deploy keys.
3. Audit GitHub Actions workflows
Search for risky pull_request_target patterns:
grep -R --line-number "pull_request_target" .github/workflows
grep -R --line-number "actions/cache" .github/workflows
grep -R --line-number "id-token: write" .github/workflows
A dangerous pattern combines:
- using
pull_request_target - checking out fork-controlled code
- running build/install commands
- using cache restore/save
- sharing cache with trusted workflows
TanStack’s postmortem identified this trust-boundary crossing as a root cause. (TanStack postmortem)
4. Purge GitHub Actions caches
If your repository has risky pull_request_target workflows or you suspect cache poisoning:
gh api /repos/OWNER/REPO/actions/caches --jq '.actions_caches[].id' | \
xargs -I{} gh api -X DELETE /repos/OWNER/REPO/actions/caches/{}
5. Restrict OIDC publishing
Only grant id-token: write in the specific job that publishes packages:
permissions:
contents: read
id-token: none
jobs:
publish:
permissions:
contents: read
id-token: write
Also pin npm trusted publishing to the expected workflow and branch, not just the repository. Snyk called out branch and workflow pinning as a safer OIDC configuration. (Snyk)
6. Block known campaign infrastructure
At the DNS, proxy, or egress firewall level, block:
*.getsession.org
git-tanstack.com
api.masscan.cloud
litter.catbox.moe
Blocking by IP alone is weaker for the Session/Oxen path because that infrastructure is distributed. (Snyk)
Preventing similar npm supply-chain attacks
This incident shows why “the package had provenance” is not enough. The build pipeline itself can become the attacker’s execution environment.
Recommended hardening:
- Avoid
pull_request_targetfor workflows that build, install, test, or execute fork code. - Never combine untrusted PR code execution with base-repository cache writes.
- Use ephemeral CI runners for sensitive workflows.
- Grant
id-token: writeonly to the publish job. - Pin GitHub Actions to commit SHAs rather than floating tags.
- Require review for release workflows.
- Monitor for unexpected package publishes.
- Use dependency scanners that detect malicious package behavior, not only known CVEs.
- Consider temporary
ignore-scripts=trueduring incident response, keeping in mind that it can break legitimate packages. - Use lockfiles and avoid automatically consuming just-published versions in production builds.
- Block git-based package dependencies where your package manager supports it.
Indicators of compromise
| Indicator type | Value |
|---|---|
| CVE | CVE-2026-45321 |
| GHSA | GHSA-g7cv-rxg3-hmpx |
| Malicious optional dependency | @tanstack/setup |
| Malicious git ref | github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c |
| Payload file | router_init.js |
| Helper file | tanstack_runner.js |
router_init.js SHA-256 | ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c |
tanstack_runner.js SHA-256 | 2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96 |
| Exfiltration domains | filev2.getsession.org, seed1.getsession.org, seed2.getsession.org, seed3.getsession.org |
| Additional infrastructure | git-tanstack.com, api.masscan.cloud |
| Second-stage payload paths | litter.catbox.moe/h8nc9u.js, litter.catbox.moe/7rrc6l.mjs |
| Linux persistence | ~/.config/systemd/user/gh-token-monitor.service, ~/.local/bin/gh-token-monitor.sh |
| macOS persistence | ~/Library/LaunchAgents/com.user.gh-token-monitor.plist |
| Editor/agent persistence | .claude/router_runtime.js, .claude/setup.mjs, .vscode/setup.mjs |
| Suspicious commit author | claude@users.noreply.github.com |
| Suspicious branch pattern | dependabout/.../setup-formatter |
These indicators were reported across the official TanStack postmortem, GitHub advisory, and security vendor analyses. (TanStack postmortem)
Final recommendation
If you only see an affected version in a lockfile but it was never installed, update the dependency and regenerate the lockfile.
If the affected version was installed on a developer machine or CI runner, assume secrets accessible to that process may have been stolen. Remove persistence first, rebuild or clean the host, then rotate credentials and review audit logs.
Do not stop at npm uninstall.
Comments