Skip to main content

On 11 May 2026, attackers published 84 malicious versions across 42 @tanstack/* npm packages in under seven minutes. The payload harvested AWS, GCP, Kubernetes, Vault, GitHub, and SSH credentials from every CI pipeline and developer machine that installed the tainted versions. By the time the community flagged it, the damage window had already opened.

This was not a case of typosquatting or a rogue maintainer. It was an elegant, three-stage chain attack that exploited trust boundaries baked into GitHub Actions itself. Every development team running open-source dependencies — which is every development team — needs to understand what happened and why their own pipelines may be just as vulnerable.

TL;DR

  • Attackers chained three GitHub Actions misconfigurations to publish 84 malicious @tanstack/* npm package versions in six minutes on 11 May 2026
  • The attack exploited pull_request_target, GitHub Actions cache poisoning, and OIDC token extraction — no stolen credentials required
  • The payload harvested AWS, GCP, Kubernetes, Vault, GitHub, npm, and SSH credentials, exfiltrating data over encrypted Session messenger channels
  • Detection was entirely external — the TanStack team had no internal monitoring for unexpected package publishes
  • Every team using pull_request_target or shared CI caches should audit their workflows immediately

Anatomy of a Three-Stage Chain Attack

What makes the TanStack compromise particularly instructive is that no single vulnerability was sufficient on its own. The attacker needed to chain three separate trust-boundary violations, each bridging a gap that GitHub Actions treats as safe by default.

Stage 1: pull_request_target Misconfiguration

TanStack’s bundle-size.yml workflow used the pull_request_target trigger, which runs workflow code from the base repository but checks out fork code. This is a well-documented anti-pattern, yet it remains shockingly common across major open-source projects. The attacker’s fork PR triggered the workflow, executing untrusted code within the base repository’s trusted context — no approval gates, no review required.

Stage 2: Cache Poisoning Across Trust Boundaries

Here is where the attack gets clever. The attacker’s malicious commit included vite_setup.mjs, a roughly 30,000-line obfuscated payload buried in what looked like a routine build file. When the benchmark job ran, it wrote poisoned entries into GitHub Actions’ shared cache. The critical detail: GitHub Actions does not isolate cache entries between workflows. A cache key written by a fork-triggered workflow is fully accessible to the release workflow.

Stage 3: OIDC Token Extraction

TanStack’s release.yml workflow declared id-token: write permissions for npm’s trusted publisher system. When this workflow later ran on a push to main, it restored the poisoned cache. The attacker’s binaries, now running inside the release workflow, extracted the OIDC token directly from the GitHub Actions runner’s memory and used it to publish packages to npm — no credentials needed, no secrets leaked. The trust chain itself was the vulnerability.

The Payload: Credential Harvesting at Scale

The injected router_init.js (a 2.3 MB obfuscated file conspicuously absent from the package’s files manifest) performed systematic credential harvesting:

  • AWS: IMDS metadata and Secrets Manager credentials
  • GCP: Metadata server tokens
  • Kubernetes: Service account tokens
  • Vault: Authentication tokens
  • GitHub: Tokens and SSH private keys from multiple sources
  • npm: Registry authentication tokens

Exfiltration was routed through encrypted Session/Oxen messenger channels rather than attacker-controlled infrastructure, making network-based detection near-impossible. The payload also included self-propagation logic that enumerated other packages maintained by compromised accounts and republished them with the same injection.

Why Detection Was External

Perhaps the most sobering detail: the TanStack team had no internal monitoring for unexpected package publishes. Detection came entirely from external security researchers at StepSecurity, who spotted anomalous optionalDependencies entries within 20 minutes of the malicious publishes.

Twenty minutes is fast — but 84 packages were already live on the registry. The only reason damage was limited is that the attacker’s payload broke the test suite, making the breach “loud.” A more careful attacker could have maintained persistence for days.

The Uncomfortable Truth About GitHub Actions Security

This attack did not require a zero-day. Every component exploited is working as designed:

  • pull_request_target is documented as dangerous but remains the default recommendation for certain workflows
  • GitHub Actions cache sharing across workflows is by design — there is no per-workflow isolation
  • OIDC trusted publishing is meant to eliminate long-lived secrets, but it creates a new attack surface when the runner environment is compromised

The fundamental issue is that GitHub Actions’ trust model assumes workflows within the same repository share a security boundary. The TanStack attack demonstrates that a single misconfigured workflow can compromise every other workflow in the repository.

What Your Team Should Do Right Now

1. Audit Every pull_request_target Workflow

Search your repositories for pull_request_target triggers. If any workflow using this trigger checks out or executes fork code, you have the same vulnerability TanStack had. The safe pattern is to use pull_request instead, or implement explicit repository_owner guards.

2. Pin Third-Party Actions to SHA Commits

Floating references like @v6 or @main are standing supply chain risks. Pin every third-party action to a specific commit SHA. Yes, it is more work to maintain. The alternative is what happened to TanStack.

3. Implement Cache Isolation

Until GitHub provides native per-workflow cache isolation, treat shared caches as untrusted input. Consider using repository-scoped cache keys that include the workflow name and trigger type. Alternatively, avoid caching build artefacts across trust boundaries entirely.

4. Monitor Package Publishes

If your team publishes npm packages, set up automated monitoring for unexpected publishes. Tools like Socket.dev, Snyk, and npm’s own audit features can alert on new versions that do not match expected release patterns. The TanStack team has since implemented this — you should too.

5. Scope OIDC Permissions Tightly

The id-token: write permission should only be declared in workflows that absolutely require it, and those workflows should have minimal other responsibilities. A workflow that both builds and publishes creates a larger blast radius than one that only publishes from pre-verified artefacts.

6. Rotate Credentials If Affected

If your project installed any @tanstack/router*, @tanstack/history*, or @tanstack/start* packages on 11 May 2026, rotate all credentials immediately: AWS, GCP, Kubernetes, Vault, GitHub, npm, and SSH keys. The affected versions have been deprecated on npm, but credential exposure may have already occurred.

The Bigger Picture

The TanStack compromise is not an isolated incident. It follows the same trajectory we have been tracking throughout 2026: supply chain attacks are becoming more sophisticated, targeting the CI/CD infrastructure rather than the code itself. Earlier this year, the React2Shell attack targeted npm typosquatting. The Bitwarden/Checkmarx breach targeted CI/CD pipelines directly. Now TanStack shows us that even trusted publisher mechanisms can be subverted.

The pattern is clear: attackers are moving upstream. They are not trying to exploit your application — they are trying to exploit the systems that build, test, and deploy your application. Your security posture needs to extend far beyond your own codebase.

At REPTILEHAUS, we help development teams audit and harden their CI/CD pipelines, implement supply chain security controls, and build DevSecOps practices that catch these threats before they reach production. If this post has made you question your own pipeline security — it should have. Get in touch and let us help you close the gaps.

📷 Photo by Steve A Johnson (@steve_j) on Unsplash