84 packages, 10 minutes, zero stolen tokens
On 2026-05-12, an attacker published 84 malicious npm package versions across the TanStack namespace in a 10-minute window, exploiting GitHub Actions OIDC trust to inject credential-stealing payloads into packages collectively downloaded millions of times per week. No npm tokens were stolen. No accounts were breached. The entire chain relied on three chained GitHub Actions abuses that turned trusted-publishing infrastructure into a supply-chain weapon. @tanstack/react-router alone receives over 12 million weekly downloads, making this one of the highest-blast-radius registry attacks in recent memory, according to CyberSecurityNews.
The 10-minute window
At approximately 19
UTC on 2026-05-12, the first batch of 42 malicious package versions appeared on npm. Six minutes later, at 19UTC, the second batch landed. In total, 84 artifacts across 42 distinct TanStack packages were poisoned, each receiving two malicious versions. The window was tight enough that automated monitoring barely caught it; broad enough that any CI runner executingnpm install during those minutes pulled compromised code.The attacker’s GitHub account, voicproducoes, submitted the malicious pull request that triggered the publishing pipeline. Repositories on that account include a project named “A Mini Shai-Hulud has Appeared,” a phrase linked to prior large-scale npm malware campaigns, suggesting this was not an isolated operation.
The attack chain: OIDC trust as a weapon
The technical mechanism is what makes this incident notable. The attacker did not steal npm credentials. Instead, they chained three GitHub Actions techniques:
pull_request_targetPwn Request pattern. The attacker opened a pull request against a TanStack repository. Thepull_request_targetevent runs workflows in the context of the base repository, not the fork, which means the workflow has access to the base repo’s secrets and OIDC tokens. This is a known footgun; the GitHub Actions documentation warns about it explicitly, and it keeps biting projects anyway.Actions cache poisoning. The attacker leveraged the fork-to-base trust boundary to inject malicious content into the repository’s Actions cache. Subsequent legitimate workflow runs consumed the poisoned cache, executing attacker-controlled code in a trusted context.
OIDC token extraction at runtime. The workflow’s memory was scraped for the OIDC token issued by GitHub, which npm’s trusted-publishing mechanism accepts as proof of identity for publishing. With that token, the attacker could publish under the TanStack namespace without ever touching an npm access token.
The result: trusted-publishing, the mechanism designed to replace long-lived npm tokens with short-lived OIDC claims, became the publication vector. The trust model worked as designed. The attacker simply operated within it.
The payload: what ran on your machine
The malicious package.json in each poisoned version registered a prepare lifecycle hook:
"prepare": "bun run tanstack_runner.js && exit 1"This means arbitrary code executed automatically during npm install on both developer workstations and CI runners. No post-install script. No manual step. Just npm install and the payload runs.
The actual payload lived in a 2.3 MB obfuscated file called router_init.js. It used spawn-based daemonization to persist in the background and exfiltrated:
- AWS, GCP, and Kubernetes credentials
- HashiCorp Vault tokens
- GitHub access tokens
- SSH private keys
.npmrcfile contents
According to CyberSecurityNews, the exfiltration targeted CI credentials specifically, which means any build pipeline that installed a TanStack package during the attack window potentially leaked its cloud-infrastructure credentials to the attacker.
TanStack’s response
TanStack deprecated all 84 affected versions with a SECURITY warning label and engaged npm security to pull the malicious tarballs from the registry. They also purged all GitHub Actions cache entries to eliminate the poisoned cache vector and merged hardening changes into their workflows, including repository-owner guards and pinned third-party action references, as reported by CyberSecurityNews.
The response was fast once detected, but the detection window is the concern. The attack completed its full publication cycle in under 10 minutes. Any consumer who ran npm install between 19
Why AI labs are exposed
OpenAI maintains an official TypeScript API library with 10.9k GitHub stars. Anthropic publishes a Python SDK and has been expanding its JavaScript tooling. Google’s Vertex AI and Gemini APIs ship with first-party npm packages. Meta’s Llama toolchain includes web-facing components. These organizations run CI pipelines that npm install hundreds of transitive dependencies, any one of which could carry a prepare hook payload.
The TanStack incident did not discriminate by consumer. Any build pipeline that resolved a TanStack dependency during the attack window was exposed, regardless of whether the downstream consumer was a startup’s landing page or a frontier lab’s inference-serving infrastructure. The JavaScript ecosystem’s transitive dependency graph means that a compromise to a popular utility package can reach build systems that never explicitly listed it as a dependency.
OpenAI is reportedly preparing a confidential IPO filing for Fall 2026 per Bloomberg, which puts its supply-chain hygiene under unusual public scrutiny. A compromised CI pipeline at an AI lab does not just leak AWS credentials; it potentially exposes model weights, training data paths, internal API endpoints, and the infrastructure topology of inference-serving clusters. The blast radius is qualitatively different from a typical web shop.
The transparency gap
When a popular open-source project is compromised, the downstream blast radius depends on how quickly consumers are informed. TanStack disclosed within hours and deprecated the affected versions. That is better than most. But the incident raises a question that no frontier lab has answered publicly: when a supply-chain attack reaches your build infrastructure, what do you disclose and when?
As of 2026-05-25, no major AI lab publishes a public dependency-incident log. OpenAI has status.openai.com for API uptime but not for supply-chain hygiene. Anthropic, Google DeepMind, and Meta have no equivalent. The industry norm for dependency incidents is silence unless a breach forces disclosure. The TanStack attack was broad enough that multiple large consumers were almost certainly exposed. Whether any of them were AI labs is unknown, and the current disclosure norms ensure it stays unknown.
What to do now
For any organization consuming npm packages, the TanStack incident reinforces three practices that have been best-effort recommendations for years and remain imperfectly adopted:
- Lockfile auditing. Pin exact versions in
package-lock.jsonorbun.lockb. Audit lockfile diffs in CI before merging. A new version of a dependency appearing in a lockfile diff is a signal, not noise. - Credential rotation on any CI runner that installed a TanStack package between 2026-05-12 19and 19UTC. Assume the payload ran. Rotate AWS keys, GCP service account keys, GitHub tokens, SSH keys, and Vault tokens. This is tedious and non-optional.
- OIDC workflow hygiene. If your project uses
pull_request_target, add arepository_ownercheck as the first step. Pin all third-party GitHub Actions to commit SHAs, not version tags. Restrict workflow permissions to the minimum required. The TanStack hardening changes are a usable reference pattern.
The uncomfortable truth is that the attacker’s technique was not novel. pull_request_target abuse, cache poisoning, and OIDC token extraction have all been documented in prior incidents. The TanStack attack worked because the defenses against these known patterns were not in place. The next target will be a different popular namespace with the same gap.
Frequently Asked Questions
Does deprecating an npm version actually prevent installs?
No. Deprecation adds a warning label to the version but does not block npm install from resolving it. Removing the tarball entirely requires manual coordination with npm security, and CDN propagation delays mean cached copies remain fetchable for hours after takedown. Consumers pinned to an affected version will continue installing it silently until they explicitly bump.
How does the TanStack attack differ from the 2018 event-stream compromise?
Both exploited lifecycle hooks for RCE, but the vectors were fundamentally different. Event-stream required social engineering to gain maintainer access to a single package. TanStack exploited CI infrastructure trust—no human was compromised, no account was breached. The attacker never touched npm credentials, instead weaponizing GitHub’s OIDC publishing flow to hit 42 packages simultaneously. The attack surface shifted from social trust to infrastructure trust.
Does a lockfile protect against this class of supply-chain attack?
Only partially. A lockfile resolved before the attack window is safe, but any fresh npm install without a committed lockfile, or a CI job that re-resolves dependencies, would pull the latest malicious version. Lockfiles also cannot defend against cache poisoning downstream—once GitHub Actions cache is poisoned, even correctly pinned workflows execute attacker code from the cache layer.
What GitHub Actions permissions should projects set to block pull_request_target abuse?
Set permissions: contents: read at the workflow level and grant id-token: write only on the specific job that needs OIDC publishing. The repository_owner guard should reject any PR from a fork immediately. Pin every third-party action to a full commit SHA rather than a version tag—tag mutability has been exploited in prior cache-poisoning chains. Where possible, replace pull_request_target with workflow_dispatch gated by required reviewers.
Why didn’t npm provenance or Sigstore catch this before publication?
npm’s provenance feature, built on Sigstore, links a published package to the CI job and commit that produced it—but it is opt-in for publishers and not enforced by consumers. There is no registry-level gate that rejects packages lacking provenance, and no standard npm install flag that aborts on unsigned packages. The OIDC token the attacker extracted would have produced a valid provenance signature anyway, since the publishing workflow itself was legitimate from the registry’s perspective.