Share
## https://sploitus.com/exploit?id=4D70655E-6858-5FC9-9F7B-DEDE5131B599
# TanStack Supply Chain Compromise - IOC Checker

```bash
curl -fsSL https://raw.githubusercontent.com/nkopylov/tanscript-exploit-check/main/check-tanstack-exploit.sh | bash
```

Or clone and run locally:

```bash
git clone https://github.com/nkopylov/tanscript-exploit-check.git
cd tanscript-exploit-check
./check-tanstack-exploit.sh [project_dir ...]
```

---

## What happened

On **May 11, 2026** (19:20-19:26 UTC), an attacker published **84 malicious versions across 42 `@tanstack/*` npm packages** in a 6-minute window. The attack was part of a broader campaign called **"Mini Shai-Hulud"** by the group **TeamPCP**, which also hit packages from Mistral AI, UiPath, OpenSearch, and others โ€” over 170 packages total across npm and PyPI.

**No maintainer credentials were stolen.** The attacker exploited the CI/CD trust chain itself via a 3-stage attack:

1. **`pull_request_target` exploit** โ€” A throwaway GitHub fork opened a PR that ran attacker code in the base repo's security context
2. **GitHub Actions cache poisoning** โ€” The fork's code poisoned the shared pnpm cache, which later infected legitimate release workflows
3. **OIDC token extraction from process memory** โ€” The malicious code extracted npm publish tokens directly from the GitHub Actions runner's memory, producing packages with **valid SLSA Build Level 3 provenance attestations**

### What the malicious code did

A 2.3 MB obfuscated payload (`router_init.js`) was injected into each compromised package. It performed:

- **Credential harvesting**: GitHub tokens, AWS keys (IMDSv2, Secrets Manager, SSM), GCP metadata, Vault tokens, K8s service-account tokens, SSH keys, `~/.npmrc` tokens, Claude Code session history
- **Persistence**: Injected hooks into `.claude/settings.json`, `.vscode/tasks.json`, and systemd services
- **Exfiltration**: Sent data via encrypted Session/Oxen P2P network and GitHub GraphQL dead-drop commits
- **Self-propagation (worm)**: Used stolen OIDC tokens to republish itself to other packages owned by the same maintainers
- **Dead-man's switch**: If the GitHub token was revoked while active, the payload attempted `rm -rf ~/`

The malicious versions were detected within **~20 minutes** by StepSecurity researcher **ashishkurmi** and deprecated by **21:03 UTC** the same day.

## What this script checks

| # | Check | Description |
|---|-------|-------------|
| 1 | Dead-man's switch | LaunchAgent/systemd persistence daemon (`gh-token-monitor`) |
| 2 | Malicious processes | Known attacker process names |
| 3 | Payload files | Known malicious files by name and SHA-256 hash |
| 4 | Claude Code hooks | Injected hooks in `.claude/settings.json` |
| 5 | VS Code tasks | Injected tasks in `.vscode/tasks.json` |
| 6 | GitHub Actions | Secrets-exfiltrating `codeql_analysis.yml` workflow |
| 7 | npm lockfiles | Compromised package versions in lockfiles |
| 8 | optionalDependencies | Malicious `@tanstack/setup` dependency marker |
| 9 | Network connections | Active connections to attacker C2 infrastructure |
| 10 | DNS cache | Prior resolution of attacker domains |
| 11 | Git branches | Dune-themed attacker branch naming pattern |

## Affected packages

Only `@tanstack/router*` and `@tanstack/start*` ecosystem packages were affected. **NOT affected:** `@tanstack/query*`, `@tanstack/table*`, `@tanstack/form*`, `@tanstack/virtual*`, `@tanstack/store`.

Key compromised versions (each package had 2 malicious releases):

| Package | Malicious versions | First safe version |
|---------|-------------------|--------------------|
| `@tanstack/react-router` | 1.169.5, 1.169.8 | 1.169.9 |
| `@tanstack/router-core` | 1.169.5, 1.169.8 | 1.169.9 |
| `@tanstack/vue-router` | 1.169.5, 1.169.8 | 1.169.9 |
| `@tanstack/solid-router` | 1.169.5, 1.169.8 | 1.169.9 |
| `@tanstack/react-start` | 1.167.68, 1.167.71 | 1.167.72 |
| `@tanstack/router-plugin` | 1.167.38, 1.167.41 | 1.167.42 |

See the [full advisory](https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx) for all 42 packages.

## Remediation (if compromised)

> **CRITICAL: Disable the dead-man's switch BEFORE revoking any tokens.** The malware wipes `$HOME` if tokens are revoked while it's active.

1. Kill `gh-token-monitor` process and remove LaunchAgent/systemd service
2. Remove persistence files (`.claude/router_runtime.js`, `.vscode/setup.mjs`, etc.)
3. Delete `node_modules` and lockfiles, reinstall with `--ignore-scripts`
4. Rotate **ALL** credentials (npm, GitHub, AWS, GCP, SSH keys, etc.)
5. Block attacker domains at DNS/firewall level (`api.masscan.cloud`, `filev2.getsession.org`, `git-tanstack.com`)
6. Audit cloud provider logs for May 11-12, 2026

## Official announcements and references

- **CVE-2026-45321** (CVSS 9.6 Critical) โ€” [CVE Record](https://vulners.com/cve/CVE-2026-45321)
- **GHSA-g7cv-rxg3-hmpx** โ€” [GitHub Advisory](https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx)
- **TanStack Postmortem** โ€” [tanstack.com/blog/npm-supply-chain-compromise-postmortem](https://tanstack.com/blog/npm-supply-chain-compromise-postmortem)
- **TanStack Hardening Follow-up** โ€” [tanstack.com/blog/incident-followup](https://tanstack.com/blog/incident-followup)
- **GitHub Tracking Issue** โ€” [TanStack/router#7383](https://github.com/TanStack/router/issues/7383)
- **Tanner Linsley on X** โ€” [x.com/tannerlinsley/status/2053949930784645170](https://x.com/tannerlinsley/status/2053949930784645170)

### Security researcher write-ups

- [Socket.dev](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack)
- [Snyk](https://snyk.io/blog/tanstack-npm-packages-compromised/)
- [StepSecurity](https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem) (original discoverers)
- [Wiz](https://www.wiz.io/blog/mini-shai-hulud-strikes-again-tanstack-more-npm-packages-compromised)
- [Orca Security](https://orca.security/resources/blog/tanstack-npm-supply-chain-worm/)
- [SecurityWeek](https://www.securityweek.com/tanstack-mistral-ai-uipath-hit-in-fresh-supply-chain-attack/)

## Hardening prompts for AI coding agents

Copy-paste these prompts into your AI coding agent (Claude Code, Cursor, Cline/OpenClaw, Windsurf, Hermes, etc.) to harden your project against supply chain attacks like this one.

### Prompt 1: npm publish delay (quarantine new versions)

> Harden this project's npm configuration against supply chain attacks. Do the following:
>
> 1. **Pin exact versions**: Remove all `^` and `~` prefixes from every dependency in `package.json` so that `npm install` never silently pulls a newly published version.
>
> 2. **Disable postinstall scripts by default**: Add `ignore-scripts=true` to `.npmrc`. Then add an explicit `"preinstall"` script in `package.json` that runs only the known-safe lifecycle scripts this project actually needs (if any).
>
> 3. **Enforce lockfile-only installs in CI**: Make sure CI uses `npm ci` (not `npm install`). If there's a CI config file, verify this. If not, note it as a manual step.
>
> 4. **Add provenance verification**: Add `npm audit signatures` as a step in the CI pipeline and as a pre-push git hook.
>
> 5. **Create an update policy script**: Create a `scripts/safe-update.sh` that:
>    - Takes a package name as argument
>    - Checks when the latest version was published (`npm view  time --json`)
>    - Refuses to update if the version is less than 3 days old
>    - If older than 3 days, runs `npm install @latest --save-exact`
>    - Prints a summary of what changed
>
> Do not change any application code. Only touch config files, `.npmrc`, `package.json` scripts, and CI config.

### Prompt 2: Harden AI agent configuration

> Audit and harden this project's AI coding agent configuration against supply chain injection attacks (like the TanStack/Mini Shai-Hulud campaign that injected malicious hooks into `.claude/settings.json` and `.vscode/tasks.json`). Do the following:
>
> **Claude Code (`.claude/`)**:
> 1. Review `.claude/settings.json` and `.claude/settings.local.json` for any `hooks` entries that run shell commands. Flag anything that executes `.js`, `.mjs`, or `.sh` files โ€” especially from within `.claude/`, `.vscode/`, or `node_modules/`.
> 2. Remove any hooks you cannot trace to a legitimate, user-created purpose.
> 3. Add a `.gitignore` rule to prevent `.claude/settings.local.json` from being committed (it should stay local).
>
> **VS Code (`.vscode/`)**:
> 1. Review `.vscode/tasks.json` and `.vscode/launch.json` for tasks that execute unexpected scripts or binaries.
> 2. Remove any task entries that reference files like `setup.mjs`, `router_runtime.js`, or other names that don't belong to this project.
> 3. Check `.vscode/extensions.json` for extensions you don't recognize.
>
> **Cursor (`.cursor/`)**:
> 1. Review `.cursor/settings.json` and any rules files for injected commands or hooks.
> 2. Same checks as VS Code above โ€” Cursor inherits `.vscode/` config.
>
> **General**:
> 1. Check for any `preinstall`, `postinstall`, `prepare`, or `prestart` scripts in `package.json` that you didn't write. Flag suspicious ones.
> 2. Check `.github/workflows/` for any workflow using `pull_request_target` โ€” flag it as a security risk with a comment explaining why.
> 3. Ensure `.gitignore` excludes agent session files that could leak credentials (`.claude/projects/`, `.cursor/logs/`, etc.).
>
> Report what you found and what you changed. Do not modify application code.

### Prompt 3: GitHub Actions hardening

> Audit and harden the GitHub Actions configuration in this repository against CI/CD supply chain attacks. Do the following:
>
> 1. **Remove or refactor any `pull_request_target` triggers** โ€” these run workflow code in the context of the base repo with access to secrets, even when triggered by a fork. Replace with `pull_request` + a separate approval-gated workflow if needed.
>
> 2. **Pin all third-party actions to full commit SHAs** (not tags or branches). For example, replace `actions/checkout@v4` with `actions/checkout@`. Add a comment with the tag for readability.
>
> 3. **Apply least-privilege permissions** to every workflow. Add explicit `permissions:` blocks. Most workflows only need `contents: read`. Publish workflows need `id-token: write` โ€” and nothing else.
>
> 4. **Restrict cache scope**: If using `actions/cache`, ensure cache keys are branch-scoped to prevent cross-branch poisoning. Add `restore-keys` carefully โ€” don't restore caches from untrusted branches.
>
> 5. **Add StepSecurity Harden-Runner** as the first step in every job: `step-security/harden-runner@v2` with `egress-policy: audit` (or `block` if you know your allowed endpoints).
>
> 6. **Check for secrets in workflow logs**: Ensure no workflow step prints `${{ secrets.* }}` or `${{ toJSON(secrets) }}` to stdout.
>
> Report all changes. Do not modify application code.

## License

MIT