The TanStack npm supply chain attack
On May 11, 2026, attackers published poisoned versions of 42 packages in the official
@tanstack scope (84 malicious versions in total), part of a wider "Mini Shai-Hulud" campaign.
Only the TanStack Router/Start packages were affected; the compromise was tracked as
CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx.
What happened
Unusually, this was not a stolen npm token, phishing, or a typosquat. It was a chained attack on
TanStack's own GitHub Actions CI/CD. An attacker abused a pull_request_target workflow that ran
untrusted fork code, poisoned the project's Actions cache across the fork/base trust boundary, and, when
a legitimate maintainer merge later triggered the release workflow, extracted the npm trusted-publisher
(OIDC) token from the runner's memory and published the malicious versions directly to the registry. Because
publishing used the project's legitimate publisher identity, the bad versions even carried valid provenance
attestations.
On the consumer side, the poisoned packages pulled in their payload through an injected
optionalDependency that pointed at a git URL, whose package.json
declared a prepare lifecycle hook. That hook executed automatically on install (via the Bun
runtime) and ran a credential harvester targeting cloud keys (AWS, GCP), Kubernetes and Vault tokens, npm and
GitHub tokens, and SSH keys, with self-propagation and editor/agent persistence.
Detection and response
Automated scanners (StepSecurity, Socket) flagged the packages within minutes of publication: a tarball
roughly 3.7× its normal size and an unexpected file at the package root were the tells. TanStack
deprecated all 84 versions within about an hour and a half, and npm removed the tarballs registry-side within a
few hours. If you installed any affected @tanstack/* Router/Start version on May 11, rotate
credentials that were exposed to the install, pin to a pre-attack release, and reinstall from a clean lockfile.
The settings that reduce exposure
- Dependency cooldown (minimum release age), the control that would have stopped this. The malicious versions were detected and pulled within minutes to hours. Any cooldown that holds back brand-new versions for a few days would have served only the known-good prior releases during the dangerous window, so a cooldown-protected install would never have resolved the poisoned versions.
- Blocking exotic transitive deps (block-exotic-subdeps, pnpm). The payload was wired in through a transitive dependency pointing at a non-registry git source, exactly the class of dependency this pnpm setting restricts.
prepare hook on a git-URL dependency (rather than a classic postinstall on the
published package), a plain ignore-scripts setting would not have
fully prevented this particular infection. It remains worthwhile defense-in-depth, and for the other
packages in the same broader campaign that used a classic preinstall hook, it would have helped,
but here the decisive control is the cooldown.
Harden your setup
DepsGuard checks whether a cooldown and the other built-in defenses are enabled for the package managers you have installed, and turns on the missing ones, with a diff preview and a backup before any change.