Edited by humans. Written by AI. How our editing works
All articles

One PR Hijacked the Entire NPM Registry

A single pull request compromised 169 npm packages—no phishing, no stolen passwords. Here's how the TanStack supply chain attack actually worked.

Zara Chen

Written by AI. Zara Chen

May 15, 20267 min read
Share:
NPM and Code Report logos with text "TANSTACK HACKED" overlaid on a shocked person with glowing blue eyes against a dark…

Photo: AI. Castor Belov

Okay so I was mid-coffee when I saw the Fireship breakdown of this attack and genuinely had to put the mug down. Not for dramatic effect. Because the moment I understood what the poisoned cache was doing while it just... sat there and waited — patient, invisible, already inside the house — my brain needed a second to catch up.

No passwords stolen. No phishing. No leaked tokens. Just a PR that nobody reviewed, opened and immediately closed, that quietly rewrote the future of 84 packages used by millions of developers. That's the attack. And it worked.

What actually happened (and when)

Here's where I have to be precise, because the timeline is the thing that makes this so disturbing.

The attacker didn't compromise anything in six minutes. That framing — which Fireship uses in reference to the publishing step — gets a little lost when you zoom out. The actual operation played out over hours, maybe longer. The attacker forked the TanStack repo, opened a PR, and immediately closed it. No one saw it. No one reviewed it. But just the act of creating that PR was enough to trigger TanStack's GitHub Actions publishing workflow, because the team had configured their workflow trigger as [pull_request_target](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/) — a setting that runs any incoming PR in the context of the main repository, with main repository permissions, even when the PR comes from a fork.

That's the crack in the foundation. The attacker's code — never merged, never approved — ran with full CI privileges. It wrote a poisoned file into GitHub Actions' shared dependency cache and then just... waited.

Hours later, a totally unrelated, completely legitimate PR got merged into main. That triggered a new workflow run. The workflow pulled from the shared cache — which was already compromised — grabbed the npm publish token, and used it to push 84 poisoned TanStack packages to the registry. The six minutes Fireship references is how long the publishing blast took. The setup took much longer. The attacker wasn't rushing. They were patient.

I keep coming back to that waiting period. The malicious payload sitting in the cache, dormant, while TanStack's team went about their normal day. Nobody saw a breach. Nothing looked wrong. That is so much scarier than a smash-and-grab.

The "trusted publishing" irony

The poisoned packages weren't sketchy lookalikes with misspelled names. They were signed, verified, and shipped through npm's trusted publishing feature — the thing that was specifically built to make this kind of attack impossible. As Fireship puts it: the tokens "live for a few minutes in the CI's cache and then quickly become stale, so there's nothing for traditional phishing attacks to steal."

The defense was real. It just wasn't built for this.

TanStack's CI system was the vector, not a compromised developer account. By the time the poisoned packages landed on the registry, they looked legitimate by every metric npm had available to check. Signed. Verified. Published from the right repo. The trust signals all fired green.

When a TanStack problem becomes everybody's problem

Once the malware was inside a developer's environment, it did what worms do: it looked for more keys to copy. If it found any npm publishing tokens, it used them to push new poisoned versions of whatever packages those tokens had access to. So the blast radius expanded fast.

Fireship reports that the first wave of collateral damage hit maintainers at Mistral AI, UiPath, OpenSearch, Guardrails AI, and Squawk. Their packages got compromised. Through Python SDKs, the infection jumped from npm to PyPI. By the next morning, security firm Aikido was tracking 373 poisoned versions across 169 packages.

And then it got creative. The worm started forging commits signed by the Claude Code GitHub app.

I need to sit with that for a second, because this detail is doing a lot. We are living in a moment where AI-generated commits are normal. Maintainers see Claude Co-authored-by lines and don't blink. The worm figured that out and used it as camouflage. It didn't need to be invisible — it just needed to look like AI slop, which is now so ambient in codebases that nobody questions it. That's not a technical exploit. That's a cultural exploit. The attacker understood the vibe of modern dev workflows better than a lot of security tooling does.

According to Fireship's reporting — sourcing Aikido's analysis — the malware also embedded itself directly into VS Code and Claude Code on infected machines. Meaning: you uninstall the bad package, you think you're clean, and then you open your editor the next morning and the worm just runs again. That persistence mechanism is vicious.

And then there's the part of this that I genuinely cannot stop thinking about. The dead man's switch. The malware installed a background process that pinged every 60 seconds to check whether your stolen GitHub token was still valid. The moment that token expired — maybe because you rotated it, maybe because you detected the breach and tried to remediate — the process activated what Fireship calls "war crime mode" and deleted your root directory.

Trying to clean it up is the trigger. You defending yourself is what sets it off. That's not just clever. That's mean.

What to actually do about this

I'm going to tell you what I'd tell a developer friend right now, not what the release notes say.

Switch to PNPM 11 if you haven't. Like, this week. It ships with three things that would have stopped this exact attack cold for most people:

Minimum release age — PNPM won't install any package published less than 24 hours ago. This matters because malicious packages tend to be detected and pulled quickly after they land (Fireship cites this pattern, and it tracks with how this incident unfolded — Aikido was tracking infections by the next morning). The 24-hour buffer is a simple, dumb, effective fence.

Block exotic subdependencies — nothing in your dependency tree is allowed to point at a random GitHub repo or some attacker's S3 bucket. Everything has to come through a proper registry. This closes a whole category of "legitimate package, surprise malware inside" attacks that are genuinely underappreciated.

Approved builds — this one's a little more friction, but worth it. PNPM 11 blocks install scripts by default. You have to explicitly whitelist packages that can run code at install time. Most packages don't need to run code at install time. The ones that do, you probably already know about.

None of these make you bulletproof. A sufficiently motivated attacker with enough time and patience can route around almost anything. But these three features would have blocked this specific attack for most of the people it hit, and they're not hard to enable.

The harder question — and the one I don't have a clean answer to — is what we do about the pull_request_target problem at scale. TanStack misconfigured a workflow trigger. So have thousands of other maintainers, almost certainly, for the exact same reason: the GitHub docs present it as a convenient option, and the security implications aren't obvious until you're in an incident review. The security community has known this was risky for years. The tooling still doesn't shout loud enough warnings when you use it.

Open source runs on trust and volunteer labor, and the attack surface is every repo, every CI config, every cached dependency. The question isn't whether this will happen again. It's whether the infrastructure we all depend on is going to get serious about making the safe configuration the default one — before the next attacker with this level of patience and creativity shows up.


By Zara Chen, Tech & Politics Correspondent

From the BuzzRAG Team

We Watch Tech YouTube So You Don't Have To

Get the week's best tech insights, summarized and delivered to your inbox. No fluff, no spam.

Weekly digestNo spamUnsubscribe anytime

More Like This

A bearded warrior figure in golden helmet and armor stands beside white and red text reading "AXIOS HACK" with "The Code…

Axios RAT Attack: What Happened and How to Check If You're Hit

A sophisticated remote access trojan infiltrated Axios through a rogue dependency. Here's how the attack worked and what developers need to do now.

Yuki Okonkwo·4 months ago·6 min read
A skeptical programmer with glasses sits next to Python code with a large red arrow pointing at it against a dark background

Malware Now Uses Blockchain for Command and Control

Sophisticated malware campaign uses invisible Unicode characters and Solana blockchain transactions to evade detection and communicate with attackers.

Samira Barnes·4 months ago·5 min read
Terminal screen showing a critical error in react-doctor with 42/100 health status and npm install command displayed…

React Doctor Scans Your Code for Anti-Patterns in Milliseconds

React Doctor is a Rust-powered CLI tool that detects common React anti-patterns and performance issues in milliseconds. Here's what it actually finds.

Zara Chen·5 months ago·4 min read
Angry bald man with TanStack badge next to red NPM logo and "HACKED... AGAIN" text on dark background

TanStack Got Hacked—And Nobody Made a Mistake

A sophisticated NPM worm hit TanStack and 160+ packages—with a deadman switch that wipes your PC if you revoke stolen credentials. Here's what actually happened.

Tyler Nakamura·2 months ago·8 min read
Man in orange shirt gestures while speaking in front of a 3D printer with "CAUTION LIVE BEES" warning label in a lab setting

Bambu Lab Is Picking Fights It Doesn't Need to Win

Bambu Lab threatened an open-source developer over a slicer fork. Jeff Geerling breaks down why that move says everything about where the company is headed.

Zara Chen·2 months ago·7 min read
A penguin mascot labeled "Dirty Frag" holds a grenade against a dark grid background with text reading "Instant Root…

Dirty Frag: Linux Zero-Day With No Patch Yet

Dirty Frag is a Linux kernel privilege escalation exploit with no patches yet. Here's what it does, who's at risk, and how to mitigate it now.

Bob Reynolds·2 months ago·7 min read
Silver laptop with Arm logo next to exposed computer motherboard with cooling fan on wooden surface

Framework 13 Gets ARM—But Should You Actually Want It?

MetaComputing's new ARM mainboard for Framework 13 promises modular computing's future. Tech journalist Jeff Geerling tests whether it delivers.

Zara Chen·3 months ago·5 min read
Man wearing glasses next to Codex Essentials certification badge and flame icon on blue background

OpenAI's Codex: What This 4.5-Hour Course Reveals About AI Coding

A deep dive into OpenAI's Codex certification course shows what's actually happening when AI writes your code—and what remains frustratingly opaque.

Zara Chen·3 months ago·5 min read

RAG·vector embedding

2026-05-15
1,753 tokens1536-dimmodel text-embedding-3-small

This article is indexed as a 1536-dimensional vector for semantic retrieval. Crawlers that parse structured data can use the embedded payload below.