JavaScript installs are a trust boundary: every npm install, pnpm install, or bun install can pull fresh tarballs and, on many clients, run lifecycle code that sees your environment—before your application code runs at all.
The May 2026 wave of Mini Shai-Hulud against the TanStack npm namespace was a sharp reminder that legitimate maintainer pipelines—not just stolen passwords—can ship malware. CI was tricked into minting publish credentials, and 84 malicious artifacts landed across 42 @tanstack/* packages in minutes [1]GitHub. “GHSA-g7cv-rxg3-hmpx — Malware in 42 @tanstack/* packages exfiltrates cloud credentials, GitHub tokens, and SSH keys.” GitHub Advisory Database, 2026. [1]Linsley, Tanner. “Postmortem: TanStack npm supply-chain compromise.” TanStack Blog, May 11, 2026 (updated May 15, 2026)..
For a Go-centric view of supply chain risks—build-time vectors like init and go generate—see Supply chain attacks and how to stay safe with Go.
TL;DR
- Lifecycle hooks (
preinstall,postinstall,prepare, and friends) let a dependency run arbitrary code during install; worms in the Mini Shai-Hulud family abuse that surface alongside stolen or hijacked publish credentials. - The May 11 incident published 84 malicious artifacts across 42
@tanstack/*packages in minutes via OIDC trusted publishing, after apull_request_targetworkflow and cache poisoning let attacker-controlled code run in CI. - pnpm and npm both support disabling install scripts; combine that with frozen lockfiles in CI, review of OIDC trust policies, and secret hygiene on runner hosts.
- Bun already skips dependency lifecycle scripts by default—still pair with
trustedDependenciesandminimumReleaseAgeinbunfig.toml. - Today: Turn on
ignore-scripts(or your client’s equivalent) where you can tolerate it, run installs in CI with--frozen-lockfile/npm ci, and read your trusted publishing / OIDC allowlists end to end.
Why installs are high risk
Package managers copy code onto laptops and CI agents that already hold Git credentials, cloud tokens, and registry tokens. A single compromised version can run during resolution—through install scripts, postinstall, prepare, or optionalDependencies paths—before your application code runs at all. TanStack’s postmortem describes exactly that pattern for the May 2026 tarball: the resolver pulled an optional payload and prepare executed a large obfuscated script that harvested cloud and Git credentials from the install host [1]Linsley, Tanner. “Postmortem: TanStack npm supply-chain compromise.” TanStack Blog, May 11, 2026 (updated May 15, 2026)..
That is separate from typosquatting: the namespace stayed @tanstack, and the publish came from the real release automation, not a guessed password [1]Snyk. “TanStack npm packages compromised: inside the Mini Shai-Hulud supply chain attack.” Snyk Blog, May 11, 2026..
Case study: Mini Shai-Hulud on npm (May 2026)
Independent reporting and TanStack’s own timeline converge on May 11, 2026, with malicious publishes concentrated in a short UTC window and detection within roughly 20–26 minutes of the first batch [1]Linsley, Tanner. “Postmortem: TanStack npm supply-chain compromise.” TanStack Blog, May 11, 2026 (updated May 15, 2026). [1]Snyk. “TanStack npm packages compromised: inside the Mini Shai-Hulud supply chain attack.” Snyk Blog, May 11, 2026.. StepSecurity attributes the activity to TeamPCP, documents spread beyond TanStack to other maintainers, and notes an escalation: compromised packages carried valid SLSA Build Level 3 provenance because the attacker hijacked the build identity—not because Sigstore was “wrong,” but because the pipeline that signs was made to build attacker code [1]Kurmi, Ashish. “TeamPCP’s Mini Shai-Hulud Is Back: A Self-Spreading Supply Chain Attack Compromises TanStack npm Packages.” StepSecurity Blog, May 11, 2026..
Akamai’s analysis of the same wave emphasizes GitHub Actions cache poisoning, memory scraping for tokens, npm’s OIDC exchange, and persistence via Claude Code and VS Code integration files—so cleanup is not only “bump the version” but also repo and workstation forensics [1]Akamai Security Intelligence Group. “Mini Shai-Hulud: The Worm Returns and Goes Public.” Akamai Security Research Blog, May 15, 2026..
A different Mini Shai-Hulud surge in late April 2026—documented by CSA Lab Space across npm, PyPI, and Composer—illustrates the same family’s use of preinstall hooks, a Bun-delivered stealer payload, and AI agent config persistence on affected repos; timeline and package list differ from the May TanStack case, so treat it as related lineage, not the same incident details [1]Cloud Security Alliance AI Safety Initiative. “Mini Shai-Hulud: Multi-Ecosystem Developer Supply Chain Attack.” CSA Lab Space, May 1, 2026..
Mitigations: pnpm
ignore-scripts: skips lifecycle scripts (including attacks that rely oninstall/preparechains). Enable via CLI or.npmrcfor CI where you can vet exceptions.onlyBuiltDependencies: restricts which packages may run postinstall-style build steps—use it so only packages that genuinely need native compilation execute code.- Frozen lockfile:
pnpm install --frozen-lockfilein CI fails whenpackage.jsonand the lockfile drift—your first line against surprise resolution. - No publish secrets on forked PR jobs: the TanStack chain started from
pull_request_targetrunning fork code [1]Snyk. “TanStack npm packages compromised: inside the Mini Shai-Hulud supply chain attack.” Snyk Blog, May 11, 2026.; that class of misconfiguration is older than this worm but still endemic.
See pnpm’s npmrc settings and onlyBuiltDependencies in package.json for the current flags.
Mitigations: npm
ignore-scripts(config ornpm config set ignore-scripts true) aligns npm with the same “no lifecycle hooks” posture where feasible npm CLI configuration:ignore-scripts.npm ciin CI: installs exactly frompackage-lock.json; pair with lockfile review on dependency bumps.- Advisories and OSV:
npm auditand the GitHub advisory database help for known vulnerable versions, but they do not replace compromise detection for freshly published malware—publishers can yank or deprecate faster than CVEs appear [1]Cloud Security Alliance AI Safety Initiative. “Mini Shai-Hulud: Multi-Ecosystem Developer Supply Chain Attack.” CSA Lab Space, May 1, 2026.. - Trusted publishing / OIDC: npm’s OIDC model is powerful, but over-broad trust (any workflow on any branch) is how attackers convert CI footholds into registry writes; scope subjects to named workflows on protected branches npm: Trusted publishers.
Mitigations: Bun
Bun skips dependency lifecycle scripts by default; you opt in per package via trustedDependencies in package.json Bun install documentation.
trustedDependencies: explicitly list packages allowed to run install scripts—everything else is silently skipped.minimumReleaseAge: set inbunfig.tomlto skip packages published less than N seconds ago, providing a cooling-off window before fresh (potentially malicious) versions enter your lockfile.--ignore-scripts: scripts in your top-levelpackage.jsonstill run by default; pass this flag to suppress them too.
[install]# e.g. 24 hours = 86400 seconds; tune to your risk appetiteminimumReleaseAge = 86400Putting it together
Across all three package managers, the same three controls do the most work: freeze resolution (lockfiles plus CI flags that fail on drift), disable scripts by default and opt in only where necessary, and narrow your OIDC trust policies so a CI foothold cannot become a registry publish. The May 2026 TanStack incident succeeded not because any one control was unavailable, but because several gaps lined up—a misconfigured pull_request_target, broad OIDC trust, and no script restrictions. Closing any one of those would have broken the chain.
References
Related
- Supply chain attacks and how to stay safe with Go —
govulncheck, vendoring, and whygo getdoes not mirror npm’s install-script model.