TanStack Router and Start: 84 poisoned npm versions that steal credentials and spread ("Mini Shai-Hulud")
Identifiers: CVE-2026-45321 · GHSA-g7cv-rxg3-hmpx · CVSS 3.1 9.6 (scored by GitHub)
What it is
On 2026-05-11 attackers abused TanStack's own publishing pipeline to push malicious versions of 42 @tanstack packages (router, start, devtools and adapters). Installing one ran a hidden script that stole cloud, GitHub, npm, SSH, Kubernetes and Vault credentials, then republished the victim's own npm packages with the same malware. @tanstack/react-query was not affected. CISA lists it as exploited, including by ransomware groups.
On CISA's Known Exploited Vulnerabilities list since May 27, 2026.
Who is affected
Only these exact versions are malicious. The versions on the right are safe to move to.
| Registry | Package | Affected | Move to |
|---|---|---|---|
| npm | @tanstack/arktype-adapter | = 1.166.12 = 1.166.15 | 1.166.16 |
| npm | @tanstack/eslint-plugin-router | = 1.161.9 = 1.161.12 | 1.161.13 |
| npm | @tanstack/eslint-plugin-start | = 0.0.4 = 0.0.7 | 0.0.8 |
| npm | @tanstack/history | = 1.161.9 = 1.161.12 | 1.161.13 |
| npm | @tanstack/nitro-v2-vite-plugin | = 1.154.12 = 1.154.15 | 1.154.16 |
| npm | @tanstack/react-router | = 1.169.5 = 1.169.8 | 1.169.9 |
| npm | @tanstack/react-router-devtools | = 1.166.16 = 1.166.19 | 1.166.20 |
| npm | @tanstack/react-router-ssr-query | = 1.166.15 = 1.166.18 | 1.166.19 |
Show all 42 packages
| Registry | Package | Affected | Move to |
|---|---|---|---|
| npm | @tanstack/react-start | = 1.167.68 = 1.167.71 | 1.167.72 |
| npm | @tanstack/react-start-client | = 1.166.51 = 1.166.54 | 1.166.55 |
| npm | @tanstack/react-start-rsc | = 0.0.47 = 0.0.50 | 0.0.51 |
| npm | @tanstack/react-start-server | = 1.166.55 = 1.166.58 | 1.166.59 |
| npm | @tanstack/router-cli | = 1.166.46 = 1.166.49 | 1.166.50 |
| npm | @tanstack/router-core | = 1.169.5 = 1.169.8 | 1.169.9 |
| npm | @tanstack/router-devtools | = 1.166.16 = 1.166.19 | 1.166.20 |
| npm | @tanstack/router-devtools-core | = 1.167.6 = 1.167.9 | 1.167.10 |
| npm | @tanstack/router-generator | = 1.166.45 = 1.166.48 | 1.166.49 |
| npm | @tanstack/router-plugin | = 1.167.38 = 1.167.41 | 1.167.42 |
| npm | @tanstack/router-ssr-query-core | = 1.168.3 = 1.168.6 | 1.168.7 |
| npm | @tanstack/router-utils | = 1.161.11 = 1.161.14 | 1.161.15 |
| npm | @tanstack/router-vite-plugin | = 1.166.53 = 1.166.56 | 1.166.57 |
| npm | @tanstack/solid-router | = 1.169.5 = 1.169.8 | 1.169.9 |
| npm | @tanstack/solid-router-devtools | = 1.166.16 = 1.166.19 | 1.166.20 |
| npm | @tanstack/solid-router-ssr-query | = 1.166.15 = 1.166.18 | 1.166.19 |
| npm | @tanstack/solid-start | = 1.167.65 = 1.167.68 | 1.167.69 |
| npm | @tanstack/solid-start-client | = 1.166.50 = 1.166.53 | 1.166.54 |
| npm | @tanstack/solid-start-server | = 1.166.54 = 1.166.57 | 1.166.58 |
| npm | @tanstack/start-client-core | = 1.168.5 = 1.168.8 | 1.168.9 |
| npm | @tanstack/start-fn-stubs | = 1.161.9 = 1.161.12 | 1.161.13 |
| npm | @tanstack/start-plugin-core | = 1.169.23 = 1.169.26 | 1.169.27 |
| npm | @tanstack/start-server-core | = 1.167.33 = 1.167.36 | 1.167.37 |
| npm | @tanstack/start-static-server-functions | = 1.166.44 = 1.166.47 | 1.166.48 |
| npm | @tanstack/start-storage-context | = 1.166.38 = 1.166.41 | 1.166.42 |
| npm | @tanstack/valibot-adapter | = 1.166.12 = 1.166.15 | 1.166.16 |
| npm | @tanstack/virtual-file-routes | = 1.161.10 = 1.161.13 | 1.161.14 |
| npm | @tanstack/vue-router | = 1.169.5 = 1.169.8 | 1.169.9 |
| npm | @tanstack/vue-router-devtools | = 1.166.16 = 1.166.19 | 1.166.20 |
| npm | @tanstack/vue-router-ssr-query | = 1.166.15 = 1.166.18 | 1.166.19 |
| npm | @tanstack/vue-start | = 1.167.61 = 1.167.64 | 1.167.65 |
| npm | @tanstack/vue-start-client | = 1.166.46 = 1.166.49 | 1.166.50 |
| npm | @tanstack/vue-start-server | = 1.166.50 = 1.166.53 | 1.166.54 |
| npm | @tanstack/zod-adapter | = 1.166.12 = 1.166.15 | 1.166.16 |
How to check
Run one of these in your project's folder; the first two download the CLI and check its SHA-256 first:
d="$(mktemp -d)" && a="xsec-$(uname -m | sed s/arm64/aarch64/)-unknown-linux-musl.tar.gz" && curl -fsSL -o "$d/$a" "https://github.com/xtruct-col/xsec-action/releases/latest/download/$a" && curl -fsSL -o "$d/checksums.txt" "https://github.com/xtruct-col/xsec-action/releases/latest/download/checksums.txt" && (cd "$d" && grep " $a$" checksums.txt | sha256sum -c -) && tar -xzf "$d/$a" -C "$d" xsec && "$d/xsec" depsdocker run --rm -v "$PWD:/src" -w /src alpine:3.20 sh -c 'apk add -q curl && d="$(mktemp -d)" && a="xsec-$(uname -m | sed s/arm64/aarch64/)-unknown-linux-musl.tar.gz" && curl -fsSL -o "$d/$a" "https://github.com/xtruct-col/xsec-action/releases/latest/download/$a" && curl -fsSL -o "$d/checksums.txt" "https://github.com/xtruct-col/xsec-action/releases/latest/download/checksums.txt" && (cd "$d" && grep " $a$" checksums.txt | sha256sum -c -) && tar -xzf "$d/$a" -C "$d" xsec && "$d/xsec" deps'xsec depsOr look up @tanstack/arktype-adapter, @tanstack/eslint-plugin-router, @tanstack/eslint-plugin-start, … in your lockfile and compare the version with the ranges above.
How to fix it
Move to the fixed version shown for each package, delete node_modules and reinstall from a clean lockfile. Treat every machine and CI runner that installed a bad version as compromised: from a clean computer, rotate every secret it could reach (cloud, GitHub, npm, SSH, Vault, Kubernetes), check your cloud audit logs, and check that none of your own npm packages was republished.
Sources
- https://github.com/advisories/GHSA-g7cv-rxg3-hmpx
- https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx
- https://nvd.nist.gov/vuln/detail/CVE-2026-45321
- https://tanstack.com/blog/npm-supply-chain-compromise-postmortem
- https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-45321
This list is curated by hand and kept short on purpose: it is not every known vulnerability, and a clean xsec deps does not mean no dependency has a flaw. Use your package manager's audit (npm audit, pip-audit, bundle audit) or Dependabot for the rest.