A clean statement of the attack
TeamPCP stole no token. It hijacked the legitimate OIDC trust binding of the TanStack/router GitHub Actions workflow, injected its own pipeline logic into the workflow, and published the malicious versions under that identity. From npm’s perspective, the publication looked exactly like any legitimate TanStack release: same OIDC identity, same trusted-publisher binding, same workflow path, valid Sigstore provenance.
The attack chain combines three known vulnerability classes, each individually documented for years. First: pull_request_target is a GitHub Actions trigger that lets a workflow run in the base repository context with its secrets, even when the workflow definition originates from a fork. GitHub’s documentation has warned about this pattern (“Pwn Request”) explicitly since 2020. Second: GitHub Actions cache is shared in the same key space for fork and base. A fork PR can poison a cache key that a subsequent base workflow loads blindly. Third: the OIDC token a runner receives for npm trusted-publisher authentication lives in the memory of the runner process. Whoever runs code with the same privileges in that address space can extract the token from memory.
The chaining is the actual novelty. The individual bricks are old; their combination into a reproducible worm campaign with OIDC identity hijacking and provenance generation is not.
Who is affected
Three groups in descending severity. First: teams that ran npm install, npm ci, or pip install on an affected package between 11 May 2026 19:20 UTC and 19:46 UTC — in CI or on a developer laptop. Second: teams that have run an indirect dependency resolution on one of these packages with npm audit or the Snyk CLI in the last 30 days and that auto-merge Renovate/Dependabot updates without a 72-hour quarantine. Third: teams that use SLSA provenance as the sole entry gate into a curated artifact pipeline — their pipeline waved the malicious versions through as signed.
The affected package namespaces are @tanstack/* (42 packages, 84 versions), uipath-* and @uipath/* (65 packages), mistralai (npm and PyPI, mistralai==2.4.6 in quarantine), opensearch (npm, 1.3 million weekly downloads), and guardrails-ai on PyPI (guardrails-ai==0.10.1 in quarantine). Teams that consume only prebuilt container images whose build pipeline does not perform direct npm pulls are not directly affected.
Mitigation and immediate measures
The clean order is: first, search CI and developer logs for npm install events between 11 May 2026 19:00 UTC and 21:00 UTC (a one-hour safety margin on each side of the known exposure window); second, match the lockfiles of all active projects against the known bad-versions list; third, on a hit, rotate tokens in this order — OIDC-active identities first (GitHub Actions trusted publisher, AWS/Azure/GCP IAM credentials of the runner), then npm tokens, then SSH keys and .npmrc auth tokens, then .env contents. Then delete ~/.npm/_cacache, node_modules, and package-lock.json and reinstall against current, patched versions.
For CI pipelines with a pull_request_target trigger: review every workflow that uses this trigger. If the workflow reads secrets or holds write permissions, it is a potential Pwn Request vector. Recommended: switch to pull_request where operationally possible; otherwise install hard gates (label-based, branch protection, explicit-allow contributor list). For Renovate and Dependabot bots: close open PRs that touch the affected packages and regenerate them against current versions. For PyPI: pin explicitly to the last known clean versions beforemistralai==2.4.6 and guardrails-ai==0.10.1 until the maintainer ships a confirmed clean follow-up.
Detection and verification
Lockfile inventory: a complete inventory across all package-lock.json and yarn.lock files in the client base is the entry ticket. CI build log verification: every CI run in the exposure window is suspect; build logs are ground truth, because under cache poisoning the build may have installed a different version than the lockfile claims. Falco or Tetragon rule: outbound connections from processes started directly out of an npm, yarn, or pnpm subprocess are a hard detection signal for install-hook exfiltration. Provenance validation against a manifest baseline: for pipelines that already check SLSA provenance, the finding is more structural — pure cosign verification would have passed this delivery as legitimate. The structural answer is a manifest diff between the current and previous version: unexpected extensions of scripts.postinstall, new bin definitions, or changes to the file list outside the expected version drift become a build stop.
Operator recommendation
Mitigate immediately if you ran a CI build or local npm install / pip install on one of the affected packages between 11 May 2026 19:00 UTC and 21:00 UTC — full token rotation, treat secrets as compromised. A 48-hour patch window is acceptable if you have affected packages as indirect dependencies in your lockfile but cannot point to an install in the known exposure window — raise lockfiles to current patched versions, check auto-merges of the following days. A structural measure within the week if you use SLSA provenance as the entry gate into a curated pipeline — install a manifest diff stage as the second verification step.
Mid-market teams in the German Mittelstand typically have exactly one npm install step in the CI pipeline per client and know the direct top-level dependencies. TanStack packages usually arrive through React, Vue, or Solid frontend projects; Mistral and Guardrails through AI agent backends; OpenSearch through search and logging pipelines. Time per client repository: 20 to 45 minutes, plus 1 to 4 hours of token rotation depending on the number of bound cloud identities. Larger organizations with central npm mirrors (Artifactory, Verdaccio, Nexus) additionally need a mirror cache invalidation and differentiated per-client token rotation. Kubernetes clusters with image build pipelines that ran in the window are directly in the risk path. Declarative build hosts (NixOS, Talos, Flatcar) structurally close the initial compromise path, provided no compromised version sits in the pin.
What I actually did
On 11 May at 21:30 UTC I ran an inventory across all active client lockfiles. Hits in two projects: a @tanstack/react-query pin in a frontend project (on a clean version before the bad window) and a mistralai pin in an AI agent pipeline (also clean). I still rotated the cloud identities of both pipelines. In parallel I installed a manifest diff stage in my standard build pipeline that, between lockfile resolution and tarball extraction, checks every version diff against the immediately preceding version. False-positive rate in the first 24 hours of live operation: around 1 in 200 builds — almost always legitimate version transitions that require manual confirmation. On the detection side I put the Falco rule live on my build hosts.
I was not hit. That is not a feat, but a fortunate coincidence of restrictive Renovate rules (auto-merge only after a 72-hour quarantine) and the fact that my active frontend projects sat on versions that were not in the bad window at the time of the attack.
Technical deep dive: why valid SLSA provenance does not rescue the delivery
SLSA — Supply-Chain Levels for Software Artifacts — formalizes trust in software deliveries at four levels. Level 3 requires the build to take place on a hardened, isolated build platform and the provenance to be cryptographically signed. In the npm ecosystem, Sigstore Cosign generates that signature via the GitHub Actions trusted publisher binding: the runner receives an OIDC token that attests a specific repository identity, and Sigstore signs the provenance through that identity.
The break lies in the assumption that “signed by the right identity” implicitly also means “produced by the right build logic”. That assumption was historically tenable because the runner process counted as an isolated build environment. Mini Shai-Hulud shows that the isolation does not hold when the workflow itself runs code from an untrusted context (fork PR via pull_request_target, cache entry with key collision across forks) in the runner address space. Once that happens, the OIDC identity is no longer that of the “right build logic” but that of the “runner the attacker briefly controls”.
The decisive construct is the widespread configuration actions/checkout@v4 with ref: pull_request.head.sha inside a pull_request_target workflow. As soon as a postinstall script (or a build-time plugin that executes code in the runner address space) runs anywhere in the fork PR code, that script can read GITHUB_TOKEN, the Sigstore OIDC token endpoint, and every secret set in the workflow. Mini Shai-Hulud uses that path systematically.
Conclusion
Mini Shai-Hulud is not the largest npm incident of the year by raw package count — Shai-Hulud in summer 2025 was broader. What sets 11 May 2026 apart structurally from its predecessors is the valid SLSA provenance on the compromised delivery. That is the kind of break a DevSecOps architecture line cannot repair in its own layer; it requires an additional layer. “Also check the content” becomes a regular operational stage alongside “check the signature”, not an emergency measure.
The question is not whether SLSA is sound as a specification — it is, and what it specifies is specified correctly. The question is whether your pipeline installs the second trust step the 11 May finding makes necessary, or whether you continue to stand on the first step and hope the next worm does not also use the Pwn Request path. The structural answer is the second step, not the next patch.
Personal background and technical detail on hardening npm pipelines against compromised provenance: ole-hartwig.eu — Why a valid signature is no longer enough in 2026.