AutoDoc Security Notes

Last week I told you to check provenance. Here’s the attack that breaks that advice.

In my last post, I argued that the Axios compromise was catchable because the malicious versions had no provenance — they were pushed by hand with a stolen token, while legitimate Axios releases ship through a verified pipeline. Check provenance, catch the attack.

I stand by that. But I deliberately left a thread hanging, because there’s a harder version of this problem, and it had already happened six weeks earlier. On May 11, 2026, someone published 84 malicious versions across 42 @tanstack/* npm packages — and every single one carried valid, signed SLSA provenance. To npm’s provenance system and every SLSA checker, these were indistinguishable from legitimate TanStack releases. 

This is the attack that should keep anyone building supply-chain defenses honest.

What happened

The numbers first. Between 19:20 and 19:26 UTC — six minutes — 84 malicious artifacts were published across 42 packages in the @tanstack namespace.  @tanstack/react-router alone sees somewhere around 12 million downloads a week. By the end of the day, over 170 packages were confirmed compromised across npm and PyPI, with more than 400 malicious versions and 518 million cumulative downloads in the blast radius. 

But the count isn’t the story. The method is. The packages weren’t published by an attacker who stole credentials — they were published by TanStack’s own legitimate release pipeline, using its trusted OIDC identity, after attacker-controlled code hijacked the runner mid-workflow. 

No stolen npm token. No phished maintainer. 2FA was irrelevant because no human credential was ever used.

How you forge something you can’t forge

SLSA provenance is supposed to be unforgeable — it’s a cryptographic attestation, generated inside the build pipeline, proving a package came from a specific repo and workflow. So how do you get a valid one on malware?

You don’t forge it. You get the real pipeline to sign for you. The attacker chained three separate GitHub Actions weaknesses, each one necessary, none sufficient on its own. 

The chain, in plain terms: First, they opened a pull request from a throwaway fork against a workflow that ran on pull_request_target — a trigger that runs fork code in the base repository’s security context. That let their fork code poison the GitHub Actions cache with a malicious store.  Then, when a legitimate maintainer’s PR was later merged and the release workflow ran, it restored the poisoned cache, and attacker-controlled binaries extracted the OIDC token directly out of the runner’s process memory by reading /proc//mem. 

The facts check out and they’re even richer than I remembered. Here’s your second post, written to publish on Bear Blog and link to from Reddit — the deliberate counterpoint to Post 1.

Last week I told you to check provenance. Here’s the attack that breaks that advice.

In my last post, I argued that the Axios compromise was catchable because the malicious versions had no provenance — they were pushed by hand with a stolen token, while legitimate Axios releases ship through a verified pipeline. Check provenance, catch the attack.

I stand by that. But I deliberately left a thread hanging, because there’s a harder version of this problem, and it had already happened six weeks earlier. On May 11, 2026, someone published 84 malicious versions across 42 @tanstack/* npm packages — and every single one carried valid, signed SLSA provenance. To npm’s provenance system and every SLSA checker, these were indistinguishable from legitimate TanStack releases. 

This is the attack that should keep anyone building supply-chain defenses honest.

What happened

The numbers first. Between 19:20 and 19:26 UTC — six minutes — 84 malicious artifacts were published across 42 packages in the @tanstack namespace.  @tanstack/react-router alone sees somewhere around 12 million downloads a week. By the end of the day, over 170 packages were confirmed compromised across npm and PyPI, with more than 400 malicious versions and 518 million cumulative downloads in the blast radius. 

But the count isn’t the story. The method is. The packages weren’t published by an attacker who stole credentials — they were published by TanStack’s own legitimate release pipeline, using its trusted OIDC identity, after attacker-controlled code hijacked the runner mid-workflow. 

No stolen npm token. No phished maintainer. 2FA was irrelevant because no human credential was ever used.

How you forge something you can’t forge

SLSA provenance is supposed to be unforgeable — it’s a cryptographic attestation, generated inside the build pipeline, proving a package came from a specific repo and workflow. So how do you get a valid one on malware?

You don’t forge it. You get the real pipeline to sign for you. The attacker chained three separate GitHub Actions weaknesses, each one necessary, none sufficient on its own. 

The chain, in plain terms: First, they opened a pull request from a throwaway fork against a workflow that ran on pull_request_target — a trigger that runs fork code in the base repository’s security context. That let their fork code poison the GitHub Actions cache with a malicious store.  Then, when a legitimate maintainer’s PR was later merged and the release workflow ran, it restored the poisoned cache, and attacker-controlled binaries extracted the OIDC token directly out of the runner’s process memory by reading /proc//mem. 

With that token, they sent POST requests straight to registry.npmjs.org, authenticated as the legitimate TanStack release workflow, bypassing the normal publish step entirely while still satisfying every security check. 

Here’s the line that should reframe how you think about provenance. SLSA provenance answers “did this artifact come from this build pipeline?” It does not answer “did this build pipeline only run code its maintainers wrote?”  The attack lives precisely in that gap.

Two details that make it worse

The payload wasn’t just a credential stealer. It harvested secrets — and for the first time in this campaign, that included password vaults like 1Password and Bitwarden — then exfiltrated them, with a destructive fallback: on certain systems it would attempt rm -rf to wipe files.  And the payload drops persistence files like .claude/setup.mjs and .vscode/setup.mjs that survive an npm uninstall.  Removing the package does not remove the infection.

The one piece of grim luck: the attacker chose a payload that broke the build’s tests, which caused the clean publish step to skip — making the attack loud enough that an external researcher caught it within about 20 minutes. A more careful attacker who didn’t break tests could have published silently for hours longer.

The facts check out and they’re even richer than I remembered. Here’s your second post, written to publish on Bear Blog and link to from Reddit — the deliberate counterpoint to Post 1.

Last week I told you to check provenance. Here’s the attack that breaks that advice.

In my last post, I argued that the Axios compromise was catchable because the malicious versions had no provenance — they were pushed by hand with a stolen token, while legitimate Axios releases ship through a verified pipeline. Check provenance, catch the attack.

I stand by that. But I deliberately left a thread hanging, because there’s a harder version of this problem, and it had already happened six weeks earlier. On May 11, 2026, someone published 84 malicious versions across 42 @tanstack/* npm packages — and every single one carried valid, signed SLSA provenance. To npm’s provenance system and every SLSA checker, these were indistinguishable from legitimate TanStack releases. 

This is the attack that should keep anyone building supply-chain defenses honest.

What happened

The numbers first. Between 19:20 and 19:26 UTC — six minutes — 84 malicious artifacts were published across 42 packages in the @tanstack namespace.  @tanstack/react-router alone sees somewhere around 12 million downloads a week. By the end of the day, over 170 packages were confirmed compromised across npm and PyPI, with more than 400 malicious versions and 518 million cumulative downloads in the blast radius. 

But the count isn’t the story. The method is. The packages weren’t published by an attacker who stole credentials — they were published by TanStack’s own legitimate release pipeline, using its trusted OIDC identity, after attacker-controlled code hijacked the runner mid-workflow. 

No stolen npm token. No phished maintainer. 2FA was irrelevant because no human credential was ever used.

How you forge something you can’t forge

SLSA provenance is supposed to be unforgeable — it’s a cryptographic attestation, generated inside the build pipeline, proving a package came from a specific repo and workflow. So how do you get a valid one on malware?

You don’t forge it. You get the real pipeline to sign for you. The attacker chained three separate GitHub Actions weaknesses, each one necessary, none sufficient on its own. 

The chain, in plain terms: First, they opened a pull request from a throwaway fork against a workflow that ran on pull_request_target — a trigger that runs fork code in the base repository’s security context. That let their fork code poison the GitHub Actions cache with a malicious store.  Then, when a legitimate maintainer’s PR was later merged and the release workflow ran, it restored the poisoned cache, and attacker-controlled binaries extracted the OIDC token directly out of the runner’s process memory by reading /proc//mem. 

With that token, they sent POST requests straight to registry.npmjs.org, authenticated as the legitimate TanStack release workflow, bypassing the normal publish step entirely while still satisfying every security check. 

Here’s the line that should reframe how you think about provenance. SLSA provenance answers “did this artifact come from this build pipeline?” It does not answer “did this build pipeline only run code its maintainers wrote?”  The attack lives precisely in that gap.

Two details that make it worse

The payload wasn’t just a credential stealer. It harvested secrets — and for the first time in this campaign, that included password vaults like 1Password and Bitwarden — then exfiltrated them, with a destructive fallback: on certain systems it would attempt rm -rf to wipe files.  And the payload drops persistence files like .claude/setup.mjs and .vscode/setup.mjs that survive an npm uninstall.  Removing the package does not remove the infection.

The one piece of grim luck: the attacker chose a payload that broke the build’s tests, which caused the clean publish step to skip — making the attack loud enough that an external researcher caught it within about 20 minutes. A more careful attacker who didn’t break tests could have published silently for hours longer. 

Sit with that. The thing that saved the ecosystem from a far worse outcome was the attacker being slightly sloppy.

So what does this mean for detection?

If you only took one lesson from my Axios post — “check provenance” — TanStack is the counterexample that breaks it. Provenance mismatch is a strong signal. Provenance presence is not proof of safety. Both things are true at once, and a serious scanner has to hold both.

The honest conclusion is the one the defenders themselves reached. OIDC publishing, SLSA provenance, and Sigstore attestations are genuinely valuable — but they were designed assuming the pipeline itself is trustworthy, and groups like TeamPCP specifically target that assumption.  The answer isn’t to throw provenance away. It’s to add the next layer — one that assumes the pipeline can be compromised too, and asks what other evidence would betray an attack when the cryptographic signature looks perfect.

That’s where behavioral signals come back in. Valid provenance or not, the TanStack payload still did things a routing library has no business doing: reading process memory, dropping files into .claude/ and .vscode/, spawning a credential stealer at install time. The compromise was earlier in the chain than provenance can see — so detection has to look at behavior, not just origin. 

No single check is sufficient. Axios proved provenance-absence catches the stolen-token class. TanStack proves you also need behavioral analysis for the pipeline-subversion class. Layer them, or you’re only ever covered against the last attack.

That’s the actual thesis. Not “check this one thing.” It’s that the trust model underneath npm assumes the pipeline is clean, attackers have stopped honoring that assumption, and defense now means watching what packages do, not just where they claim to come from.

This is the second in a series taking apart real npm supply-chain attacks. The first covered the Axios compromise and why its provenance gap was the tell. Next: what install-time behavioral detection actually looks like in practice.