This article was originally published on LucidShark Blog.
What LucidShark Would Have Caught Before the TanStack Attack Landed
The Mini Shai-Hulud worm compromised 84 @tanstack packages in six minutes. Here is exactly what a developer running LucidShark would have seen in their editor before the malicious payload executed.
On May 11, 2026, between 19:20 and 19:26 UTC, a threat actor known as TeamPCP published 84 malicious versions across 42 @tanstack/* npm packages. The campaign, dubbed Mini Shai-Hulud, then expanded to 172 compromised packages across npm and PyPI within 48 hours. @tanstack/react-router alone has 12.7 million weekly downloads.
This story is on the front page of Hacker News for a reason: the attack succeeded against a project that did everything right. TanStack had 2FA on all maintainer accounts, OIDC trusted publishing instead of long-lived tokens, and signed provenance attestations on every release. The compromised packages still carry valid npm provenance. Standard advisory tooling did not flag them at install time.
So what would have caught it? Let us walk through the specific signals LucidShark surfaces and when a developer running it would have seen each one.
How the Attack Actually Worked
Understanding what LucidShark catches requires understanding the attack chain first.
The attacker created a fork of TanStack/router (renamed to zblgg/configuration to avoid appearing in fork lists), then opened a pull request that triggered a pull_request_target workflow. That workflow checked out and executed attacker-controlled code, poisoning the GitHub Actions cache with a malicious pnpm store. When legitimate maintainer PRs were later merged to main, the release workflow restored the poisoned cache. Attacker-controlled binaries then extracted OIDC tokens directly from the GitHub Actions runner process memory and used those tokens to publish the malicious package versions without ever touching npm credentials.
The malicious versions introduced two payloads:
- **router_init.js** (SHA-256: `ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c`): A 2.3 MB obfuscated file with spawn-based daemonization and a re-entrancy guard. It harvests GitHub Actions secrets, AWS/GCP/Azure credentials, Vault tokens, Kubernetes service account tokens, and SSH keys.
- **tanstack_runner.js** (SHA-256: `2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96`): Deployed via a malicious `optionalDependencies` entry pointing to a git commit hash: `github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c`.
Exfiltration routes through the Session decentralized messaging network (filev2.getsession.org) to disguise C2 traffic as encrypted messaging protocol activity. On developer machines, the malware installs a persistent daemon (gh-token-monitor) via macOS LaunchAgent or Linux systemd that polls GitHub every 60 seconds. Critically, the malware writes persistence files to .claude/ and .vscode/ directories that survive node_modules deletion.
What LucidShark's SCA Scanner Surfaces
LucidShark runs Software Composition Analysis locally, inside your editor, before you commit. Here is the sequence of signals a developer would have seen.
Signal 1: Git Reference in optionalDependencies
The first thing LucidShark's dependency graph analysis flags is a git commit reference in optionalDependencies. Legitimate packages do not ship git SHAs as runtime dependencies. The rule that fires is sca/git-dependency-in-production.
LucidShark SCA [HIGH] sca/git-dependency-in-production
File: node_modules/@tanstack/react-router/package.json
Field: optionalDependencies
Value: "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
A production dependency is pinned to a git commit reference rather
than a registry version. This bypasses registry provenance checks
and can introduce code that has not been reviewed by the package
maintainers.
Action: Remove or replace with a versioned registry dependency.
Docs: lucidshark.com/docs/rules/sca-git-dependency-in-production
This fires at npm install time, before any code runs. The developer sees it inline in their editor via the MCP integration.
Signal 2: Unregistered File in Package
LucidShark compares the files present in the installed package against the package's published file manifest and the registry's integrity hash. router_init.js is a 2.3 MB file that does not appear in the published file list for any prior version of @tanstack/react-router. The rule is sca/unexpected-file-in-package.
LucidShark SCA [HIGH] sca/unexpected-file-in-package
Package: @tanstack/[email protected]
File: router_init.js (2,347,521 bytes)
This file was not present in any prior version of this package and
is not declared in the package's "files" manifest. Large obfuscated
files that appear in new versions are a known indicator of supply
chain compromise.
SHA-256: ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c
Action: Do not run this package. File a security report with the
registry maintainers.
Docs: lucidshark.com/docs/rules/sca-unexpected-file-in-package
Signal 3: Lifecycle Hook Writing Outside node_modules
LucidShark's lifecycle hook analyzer does static analysis on prepare, postinstall, and preinstall scripts before they run. It detects filesystem writes outside the package directory. In this case, router_init.js writes to .claude/ and .vscode/ at the workspace root.
LucidShark SCA [CRITICAL] sca/lifecycle-hook-writes-outside-package
Package: @tanstack/[email protected]
Hook: prepare
Detected: fs.writeFileSync targeting paths outside node_modules/
Target paths identified in static analysis:
- {workspace}/.claude/
- {workspace}/.vscode/
Lifecycle hooks that write outside the package directory are a
primary persistence mechanism in supply chain attacks. This pattern
was observed in the axios compromise (April 2026) and SAP CAP
attack (April 2026).
Action: Block installation. Audit any existing .claude/ and
.vscode/ files for unexpected additions.
Docs: lucidshark.com/docs/rules/sca-lifecycle-hook-writes-outside-package
This is the signal that would have stopped execution entirely. A CRITICAL finding blocks the pre-commit hook by default in LucidShark's standard configuration.
Signal 4: Network Egress to Known Exfiltration Endpoint
LucidShark's static network analysis scans package code for known exfiltration patterns and domains. The Session messaging endpoint filev2.getsession.org is in LucidShark's threat intelligence feed, updated daily from Socket Research, Endor Labs, and the OpenSSF package analysis feeds.
LucidShark SCA [HIGH] sca/known-exfiltration-endpoint
Package: @tanstack/[email protected]
File: router_init.js
Detected: HTTP request to filev2.getsession.org
This domain is associated with credential exfiltration in the
Mini Shai-Hulud campaign (first observed 2025-09, active as of
2026-05-11). The domain is used to disguise C2 traffic as
Session decentralized messenger protocol activity.
Action: Block installation. Rotate all credentials on any machine
where this package was previously installed.
Docs: lucidshark.com/docs/rules/sca-known-exfiltration-endpoint
Signal 5: Version Diff Anomaly
LucidShark computes a diff between the installed version and the previous known-good version. A 2.3 MB obfuscated file appearing in a patch release of a routing library is a structural anomaly, regardless of whether the specific payload is known.
LucidShark SCA [MEDIUM] sca/version-diff-anomaly
Package: @tanstack/react-router
Previous version: 1.120.2 (known good)
Current version: 1.120.3
Bundle size delta: +2,347 KB (expected delta for patch: ~2 KB)
New files: router_init.js, tanstack_runner.js
Files with obfuscation score above threshold: 2/2
Patch-level version bumps that introduce large obfuscated files
are a strong indicator of supply chain tampering, even when
provenance attestations are valid.
Action: Review changes before proceeding.
Docs: lucidshark.com/docs/rules/sca-version-diff-anomaly
The Provenance Trap
This attack is important because it demonstrates the limits of provenance attestations as a sole defense. The compromised packages carry valid Sigstore signatures and SLSA provenance. From the registry's perspective, the release was legitimate: it came from the official TanStack CI pipeline, signed with a valid OIDC token, with a verifiable build graph. Every provenance check passes.
Provenance answers "was this built where it claims to be built?" It does not answer "is the build pipeline itself clean?" and it does not answer "does this package contain malicious code?"
LucidShark's SCA approach does not rely on provenance as a trust signal. It analyzes package behavior: what does the lifecycle hook do, what files does the package write, what network connections does the code attempt, how does this version differ from the previous one? Those questions have answers that provenance cannot provide.
The Timeline Difference
Here is how the timeline plays out with and without LucidShark.
Without LucidShark: Developer runs npm install or updates lockfile. Malicious package installs silently. router_init.js executes during the prepare hook. Credentials are harvested and exfiltrated. Persistence daemon is installed. Developer does not know until the security team gets an alert or the incident makes HN.
With LucidShark: Developer runs npm install. Before the lifecycle hook runs, LucidShark's pre-install analysis fires. The developer sees four findings in their editor: a git reference in optionalDependencies, an unexpected 2.3 MB file, a lifecycle hook writing outside the package directory, and a known exfiltration endpoint. The CRITICAL finding blocks the pre-commit hook. The developer files a report. The package does not run.
The critical difference is that LucidShark runs analysis before execution, not after. By the time a behavioral EDR solution would detect the persistence daemon, the credentials are already exfiltrated.
What to Do Right Now
If your team uses any @tanstack/* package and has not audited your lockfile since May 11, 2026:
- Check your `package-lock.json` or `pnpm-lock.yaml` for any `@tanstack/*` version published between 19:20 and 19:30 UTC on May 11, 2026.
- Search your `node_modules` for `router_init.js` and `tanstack_runner.js`.
- Audit your `.claude/` and `.vscode/` directories for files you did not put there.
- Rotate GitHub tokens, npm publish tokens, and any AWS/cloud credentials that were present on machines where affected packages were installed.
- Block `filev2.getsession.org` at your network perimeter if you have not already.
The full list of compromised package versions is available in the Endor Labs advisory and the Socket Research blog.
LucidShark's SCA in Practice
LucidShark is open-source and runs locally. It integrates with Claude Code via MCP, which means findings appear inline in your editor as you work, not in a separate dashboard you have to remember to check. The SCA scanner runs on every npm install, package.json change, and pre-commit hook.
The threat intelligence feed that powers the exfiltration endpoint detection is updated daily and pulls from Socket Research, the OpenSSF Package Analysis project, and Endor Labs advisories. The behavioral analysis rules (lifecycle hook writes, git references in production deps, version diff anomalies) are local rules that run entirely on your machine with no data leaving your environment.
The TanStack attack is the fifth Shai-Hulud wave in eight months. The attack surface is not shrinking. The question for every engineering team right now is whether their tooling catches supply chain attacks before execution or after. For the developers who had LucidShark running, the answer this week was before.
**Try LucidShark today:** [lucidshark.com](https://lucidshark.com) , open-source, local-first, works inside Claude Code via MCP.
Top comments (0)