@emstack/tanstack-supply-chain-checker
v1.2.0
Published
Detect and fix the mini-shai-hulud TanStack supply-chain attack (socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack)
Maintainers
Readme
@emstack/tanstack-supply-chain-checker
Detect and remediate the mini-shai-hulud TanStack supply-chain attack (disclosed May 11, 2026).
Reference: socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack
Quick start
npx @emstack/tanstack-supply-chain-checkerNo arguments → interactive mode. Guides through path input, project selection, and options step by step.
┌ tanstack-supply-chain-checker mini-shai-hulud attack detector
│
◆ Root path to scan
│ ./my-monorepo
│
◆ Select projects to scan
│ ■ my-app packages/app
│ ■ my-lib packages/lib
│ □ legacy packages/legacy
│
◆ Auto-fix detected issues?
│ No / Yes
│
◆ Include node_modules in scan?
│ No / Yes
│
◇ Scanning my-app 2 critical, 1 high
│ [CRITICAL] package.json
│ injected-dependency: Injected malicious package "@tanstack/setup" found in dependencies
│ [HIGH] package.json
│ compromised-version: Package "@opensearch-project/[email protected]" matches known compromised version
│
────────────────────────────────────────────────────────
SECURITY SUMMARY
Compromised packages (2)
● @tanstack/setup package.json
● @opensearch-project/[email protected] package.json
────────────────────────────────────────────────────────
◇ Scanning my-lib ✓ clean
│
SCAN SUMMARY
my-app 2 critical, 1 high packages/app
my-lib ✓ Clean packages/libUsage
Interactive (default)
npx @emstack/tanstack-supply-chain-checker # auto-launches interactive
npx @emstack/tanstack-supply-chain-checker -i # explicit flagDiscovers all package.json projects under the given root, lets you select which to scan, and walks through options with prompts.
Non-interactive (CI / scripting)
npx @emstack/tanstack-supply-chain-checker ./my-project
npx @emstack/tanstack-supply-chain-checker ./my-project --fix
npx @emstack/tanstack-supply-chain-checker . --include-node-modules
npx @emstack/tanstack-supply-chain-checker . --jsonFlags
| Flag | Description |
|---|---|
| -i, --interactive | Interactive mode (default when no args) |
| --fix | Auto-remove malicious files, clean package.json |
| --include-node-modules | Also scan inside node_modules (slow) |
| --json | Machine-readable output, exit 1 on findings |
| --help | Show help |
Report output
After scanning, a SECURITY SUMMARY block groups findings by threat type so you can triage at a glance:
────────────────────────────────────────────────────────
SECURITY SUMMARY
Compromised packages (3)
● @tanstack/setup package.json
● @opensearch-project/[email protected] package.json
● @tanstack/router (router_init.js) node_modules/...
Malicious files (1)
● router_init.js router_init.js
Backdoor hooks / tasks (1)
● scripts.prepare package.json
Network IOCs in source (1)
● filev2.getsession.org src/setup.ts
Unauthorized git commits (1)
● a3f1bc9d2e44 .git
────────────────────────────────────────────────────────Groups shown only when findings exist in that category. Bold label = package name, script key, or IOC string. Dim path = location in repo. Same summary printed per-project in interactive mode and aggregated across all projects at the end.
What it detects
| Category | Detail | Auto-fixable |
|---|---|---|
| Malicious payload files | router_init.js, router_runtime.js, tanstack_runner.js — case-insensitive, anywhere in tree | Yes |
| Known SHA256 hashes | 2 known payload hashes, checked against .js/.mjs/.cjs files | Yes |
| Persistence — Claude Code | .claude/router_runtime.js, .claude/setup.mjs | Yes |
| Persistence — VS Code | .vscode/setup.mjs | Yes |
| Injected dependency | @tanstack/setup in any dep section | Yes |
| Malicious lifecycle hook | prepare/postinstall referencing worm files (string and array form) | Yes |
| Compromised @tanstack package | router_init.js inside installed package | Yes |
| Compromised version — other packages | @opensearch-project/[email protected]–3.8.0, @squawk/[email protected], @squawk/[email protected], @squawk/[email protected] | Manual |
| Installed compromised package | package.json inside node_modules matches known bad name + version | Manual |
| Network IOC in source | .js/.ts files referencing filev2.getsession.org, git-tanstack.com | Manual |
| Malicious Claude hooks | .claude/settings.json hooks referencing worm files | Manual |
| VS Code auto-run tasks | .vscode/tasks.json with runOn: folderOpen | Manual |
| Unauthorized git commits | Commits from [email protected] | Manual |
What --fix does
- Deletes all confirmed malicious files
- Strips
@tanstack/setupfrom allpackage.jsondependency sections - Removes malicious lifecycle scripts (handles both string and array form)
- Writes
package.jsonatomically (temp file → rename — no corruption risk) - Prints secrets-rotation checklist covering npm, GitHub, AWS (incl. OIDC), GCP, Azure, Docker, PyPI, Ruby, Rust, Vault, GPG
- Prints git commands to audit unauthorized commits
What requires manual action
.claude/settings.jsonhooks — requires human judgment before editing.vscode/tasks.jsonauto-run tasks — inspect before deleting- Git history — revert specific commits after review
- Secrets rotation — cannot be automated
- OIDC federation revocation — GitHub repo settings
- Compromised third-party package versions — pin to a clean version and reinstall
- Network IOC references in source — inspect file, determine if injected
Affected packages
@tanstack — 84 compromised artifacts. Worm payload injected into router_init.js / router_runtime.js inside installed packages.
Other npm packages (loaded at runtime from data/compromised-packages.csv — sourced from Socket.dev disclosure):
| Namespace | Packages | Versions |
|---|---|---|
| @opensearch-project | opensearch | 3.5.3, 3.6.2, 3.7.0, 3.8.0 |
| @squawk | airport-data, airports, airspace, airspace-data, airway-data, airways, fix-data, fixes, flight-math, flightplan, geo, icao-registry, icao-registry-data, mcp, navaid-data, navaids, notams, procedure-data, procedures, types, units, weather | multiple (0.3.x – 0.9.x) |
| @mistralai | mistralai, mistralai-azure, mistralai-gcp | 1.7.1–1.7.3, 2.2.2–2.2.4 |
| @uipath | 45 packages (apollo-react, agent-sdk, robot, cli, …) | single versions per package |
| @tallyui | components, connector-*, core, database, pos, storage-sqlite, theme | 0.2.1–1.0.3 |
| @beproduct | nestjs-auth | 0.1.2–0.1.19 |
| @draftlab / @draftauth | auth, auth-router, db, client, core | 0.13.x–0.24.x |
| @cap-js | db-service, postgres, sqlite | 2.2.2, 2.10.1 |
| @mesadev | rest, saguaro, sdk | 0.28.3, 0.4.22 |
| @ml-toolkit-ts | preprocessing, xgboost | 1.0.2–1.0.4 |
| @supersurkhet | cli, sdk | 0.0.2–0.0.7 |
| @taskflow-corp | cli | 0.1.24–0.1.29 |
| @tolka | cli | 1.0.2–1.0.6 |
| @dirigible-ai | sdk | 0.6.2, 0.6.3 |
| (unscoped) | cross-stitch, git-branch-selector, git-git-git, ts-dna, wot-api, nextmove-mcp, cmux-agent-mcp, safe-action, ml-toolkit-ts, agentwork-cli, intercom-client, mbt | various |
PyPI packages [email protected] and [email protected] were also compromised but are outside the scope of this npm scanner.
Updating the IOC package list
Compromised package versions are loaded at runtime from data/compromised-packages.csv. To add new packages:
- Append rows to
data/compromised-packages.csvusing the same column format:Ecosystem,Namespace,Name,Version,Published,Detected npm,@example,package-name,1.2.3,, - Run
bun run build— no code changes needed.
The scanner filters Ecosystem = npm and skips @tanstack (handled separately via file-based detection).
Build & test
bun install
bun run build # outputs dist/cli.js
bun run validate # integration tests (runs automatically on publish)
bash test/smoke.sh # end-to-end smoke test with harmless fixturesContributing
See CONTRIBUTING.md — especially the section on adding new IOCs.
