Mini Shai-Hulud Didn’t Need Your Maintainer’s Password
On May 11, 84 malicious @tanstack artifacts were published using TanStack’s own legitimate OIDC identity. No stolen credentials. No phished maintainer. The attacker extracted tokens from GitHub Actions runner memory after poisoning the build cache. This is wave four of the same campaign — and the attacker left behavioral traces in public repos the whole time.
On May 11, 2026, between 19:20 and 19:26 UTC, TeamPCP published 84 malicious artifacts across 42 packages
in the @tanstack namespace. @tanstack/react-router alone has 12.7 million weekly downloads.
The total affected install base across npm and PyPI exceeded 518 million cumulative downloads.
This is the fourth wave of the Mini Shai-Hulud campaign. Wave one targeted Aqua Security’s Trivy scanner in March.
Wave two hit @bitwarden/cli in April.
Wave three hit Mistral AI and Guardrails AI packages.
Wave four hit TanStack — and demonstrated something no prior supply chain attack had managed:
publishing malicious packages indistinguishable from legitimate ones by provenance attestation.
TanStack was not structurally fragile. This is the part worth sitting with.
Why TanStack scored high
If you had run proof-of-commitment on @tanstack/react-router before May 11, you would have seen a package with strong behavioral signals: organization-backed, consistent release cadence, multiple maintainers, years of sustained activity, no single point of credential failure.
That score was accurate. TanStack is a real project with real maintainers and a real release pipeline. That’s precisely the attack surface the attackers chose — not because it was weak, but because a high-credibility target makes the payload harder to reject.
The ua-parser-js credential attack in 2021 succeeded because one person held the publish key to a package with 8 million weekly downloads. The TanStack attack succeeded because TanStack’s build pipeline held the publish key — and that pipeline was reachable through a forked pull request.
The attack chain
TeamPCP created a fork of the TanStack/router repository, renamed to zblgg/configuration
to evade fork-list searches. They opened a pull request that triggered a
pull_request_target workflow — a GitHub Actions event that, unlike pull_request,
runs with write permissions to the base repository.
The workflow executed attacker-controlled code, which poisoned the GitHub Actions runner cache with a malicious pnpm store. The attack then waited.
When legitimate TanStack maintainers later merged unrelated PRs to main, the release workflow
restored the poisoned cache. Attacker-controlled binaries in that cache extracted OIDC tokens
directly from the GitHub Actions runner’s process memory — specifically from
/proc/<pid>/mem. Those tokens were used to publish the malicious package versions
to npm with TanStack’s own legitimate identity.
From the npm registry’s perspective: legitimate publisher, legitimate OIDC identity, valid provenance attestation. Nothing was wrong.
What provenance verification can’t see
This attack broke a property most security teams assumed was reliable: if a package was published by the real maintainer, from the real source repository, the provenance chain is valid.
The Mini Shai-Hulud attack shows that assumption fails when the build environment itself is compromised mid-run. The OIDC token was real. The publisher was real. The source repository was TanStack’s actual repo. The malicious code was injected between “build starts” and “artifact is published,” inside the runner’s own process space.
npm audit signatures verifies that the published artifact was signed by the expected identity.
It does not verify what happened inside the build environment that produced the artifact.
The behavioral trace the attacker left
Here’s what’s unusual about this campaign: the attacker was not invisible.
The GitHub account voicproducoes was the identity behind the fork that initiated the attack.
That account had public repositories including one named “A Mini Shai-Hulud has Appeared.”
The attacker named their project after the attack campaign, in public, on the platform they used to execute it.
The malicious commit injected into the build was authored under
claude <[email protected]> —
impersonating the Anthropic Claude GitHub App — and prefixed with [skip ci]
to suppress automated CI checks. The impersonation was deliberate camouflage, not sloppy execution.
What this tells you: the attacker had public GitHub activity that, in retrospect, was directly traceable
to the campaign. voicproducoes was not a brand-new throwaway account created the day of the attack.
It had a commit history, public repositories, behavioral fingerprints.
If you could profile who is submitting pull requests to your dependencies’ repositories the same way you profile the packages themselves, the behavioral signal was there before the attack landed.
What this means for the scoring model
Behavioral commitment scoring — as proof-of-commitment implements it — measures structural concentration risk at the package and publisher level. It caught what it was designed to catch: packages with sole maintainers and no organizational backing (ua-parser-js in 2021, the current axios profile). It did not flag TanStack, because TanStack was not structurally fragile. That was the correct output.
The TanStack attack represents a different threat model: not “the legitimate maintainer is a single point of failure,”
but “the build pipeline is reachable by external contributors via pull requests.”
That surface exists at any project that accepts external PRs and uses GitHub Actions with
pull_request_target. It’s widespread.
The scoring extension that this attack suggests: behavioral profiling of external contributors to a package’s repository — not just the maintainers. A PR from an account with zero meaningful history, or whose public repos signal campaign participation, is a different risk profile than a PR from a long-standing contributor. That’s checkable today, manually. It will be automatable.
The wave pattern matters
TeamPCP compromised Trivy in March, @bitwarden/cli in April, and TanStack in May.
Three waves in three months. The targets escalated in download volume and behavioral credibility
with each wave.
The campaign’s persistence is itself a behavioral signal. A threat actor that returns
monthly, escalates target selection, and refines the technical approach between waves is not
an opportunistic attacker. They have a program. The behavioral fingerprint of the campaign
— the specific exploitation of pull_request_target, the cache poisoning technique,
the OIDC extraction method — is consistent and attributable.
Supply chain defense that waits for CVEs to catch these is defending against the previous wave.
The defense stack after TanStack
The TanStack attack doesn’t invalidate any existing layer. It identifies which threat surfaces each layer covers:
- Behavioral scoring (proof-of-commitment) — catches concentration risk: sole maintainer + high download volume, organizational vacuum, stale-but-depended-on packages. Did not and should not have flagged TanStack.
- Provenance verification (
npm audit signatures) — catches builds where the artifact doesn’t match the signed source. Failed against TanStack because the OIDC token was legitimately obtained mid-build. - Real-time publish scanning (Socket, Snyk) — catches malicious code patterns in new releases as they land. This is what actually caught the TanStack artifacts in practice.
- Pull request contributor profiling — not yet automated at scale,
but the voicproducoes account was behaviorally flaggable before the attack executed.
Projects using
pull_request_targetshould treat external PR authors with the same scrutiny as external publishers.
# Check structural risk across your dependencies
npx proof-of-commitment --file package.json
# Check build provenance (won't catch TanStack-style attacks)
npm audit signatures
Lock your package-lock.json. Pin exact versions. Create the window between “new version published”
and “it enters your build” so that real-time scanning has time to flag it.
The malware payload, for completeness
On developer machines, the injected code installed a persistent gh-token-monitor daemon
via macOS LaunchAgent or Linux systemd. The daemon polled GitHub every 60 seconds.
On receiving a 40X response due to token revocation, it attempted to execute rm -rf ~/
and exited after 24 hours without triggering the destructive handler.
The payload is destructive but not subtle. The interesting technical work was getting it published in the first place — which required no stolen credentials, no phished maintainer, and produced provenance attestations that looked legitimate from every external checkpoint.
That’s the part that changes the threat model. Not the destructive payload. The fact that the attack published legitimately-attested malicious code.
proof-of-commitment is open-source and free to use. GitHub · Web audit · API docs