When the seal is genuine and the contents are not: CVE-2026-45321, Mini Shai-Hulud and the first validly-signed compromised npm delivery
11 May 2026, 19:20–19:26 UTC. TeamPCP publishes 84 malicious versions across 42 @tanstack/* npm packages, with valid SLSA Build Level 3 provenance (CVE-2026-45321). The signature is genuine — what is compromised is the content it attests. As of 14 May: 373 versions across 169 packages, 518M cumulative downloads, plus PyPI and Packagist follow-up paths.

The 90-second summary
On 11 May 2026, 19:20–19:26 UTC, TeamPCP used the hijacked TanStack/router GitHub Actions workflow to publish 84 malicious versions across 42 @tanstack/* npm packages. In the following hours: 65 UiPath packages, mistralai on npm and PyPI, opensearch (npm), guardrails-ai on PyPI. Over 170 packages, 404 malicious versions, more than 518 million cumulative downloads in total. CVSS 9.6, critical.
Structurally new: for the first time, the compromised packages carry valid SLSA Build Level 3 provenance. The attack stole no token — it abused the legitimate pull_request_target configuration of the TanStack/router workflow, poisoned the GitHub Actions cache across the fork-base boundary, and extracted the OIDC token from the runner process memory. With that legitimate identity, the worm published the malicious versions. Sigstore signed correctly because, from the platform’s point of view, everything was in order.
Acute exposure: every CI and developer setup that ran npm install or pip install on one of the affected packages during the known 26-minute window (plus roughly 60 minutes of CDN/mirror replication). The malware exfiltrates local secrets (SSH keys, cloud tokens, GitHub PATs, npm tokens, .env files) and the active OIDC token of the CI runner. PyPI has put mistralai==2.4.6 and guardrails-ai==0.10.1 into quarantine; npm has removed the TanStack versions.
Recommendation in this order: lockfile inventory across the client base, CI build log verification for the exposure window, token rotation (OIDC-active identities first), npm install --ignore-scripts as the new default in standard pipelines, install a manifest diff stage between lockfile resolution and tarball extraction. A cosign verification alone does not detect this break.
What CVE-2026-45321 is technically — and why the SLSA provenance question matters
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 we actually did
On 11 May at 21:30 UTC we 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). We still rotated the cloud identities of both pipelines. In parallel we installed a manifest diff stage in our 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 we put the Falco rule live on our build hosts.
We were 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 our 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.
Frequently asked questions about Mini Shai-Hulud / CVE-2026-45321
Are we affected if @tanstack/react-router only appears as a transitive dependency in our lockfile?+
Possibly. Check the version actually resolved in the lockfile against the StepSecurity bad-versions list. If it falls inside the bad window and your CI build ran between 11 May 2026 19:00 UTC and 21:00 UTC, treat the pipeline identities as compromised and rotate OIDC-active tokens first.
What is the operational consequence of the SLSA provenance on CVE-2026-45321 being valid?+
A pure cosign verification or in-toto attestation check does not detect this incident. It needs a second stage: a manifest diff against a baseline version or a behavioural diff in the install hook. Sigstore validation alone is no longer sufficient in 2026 — not because validation is poor, but because in the Pwn Request path the compromised delivery receives a correctly signed provenance.
How do we verify after the fact whether our GitHub Actions OIDC token was misused between 19:00 and 21:00 UTC on 11 May?+
The OIDC tokens are short-lived and not easily reconstructed from logs. What is checkable: every npm publish, aws sts assume-role-with-web-identity, gcloud auth login, and Azure token exchange call in the audit logs of your cloud identities between 19:00 UTC and 25:00 UTC on 11 May. Unexpected identities or new trusted publisher bindings in that window are the indicator.
Do Kubernetes containers that run npm install in the build stage need to be rebuilt because of CVE-2026-45321?+
If the image build ran during the known exposure window (11 May 2026 19:00–21:00 UTC): yes. If it ran before or after: no, provided you have raised your lockfiles to clean versions. If you have no lockfile (which we do not recommend): you do not know; rebuild against current patched versions.
How do we respond if mistralai==2.4.6 or guardrails-ai==0.10.1 ran in a production AI agent pipeline?+
Rotate every LLM API key (Mistral, OpenAI, Anthropic — all readable from the .env of the affected process), repin to a confirmed clean predecessor version, run a full re-deploy. If the agent process holds persistent storage credentials (vector DB, object storage, SQL): rotate those too. With workload identity federation: rebind the FederatedIdentityCredential rather than just swapping a secret.
Is replacing pull_request_target with pull_request enough to close the Pwn Request path?+
Structurally yes, because pull_request does not expose base repository secrets to the fork context. Operationally it may mean certain preview build pipelines need to be redesigned. If pull_request_target remains necessary: keep a strict split — use pull_request_target only for steps that do not execute code from the PR, plus a separate workflow_run trigger with an approval gate for the code-executing steps.
We assess, rotate, and validate your npm/PyPI pipeline against Mini Shai-Hulud compromise — including OIDC token audit and manifest diff stage
Lockfile inventory across your client base, CI build log verification for the 11 May exposure window, token rotation along the OIDC bindings (trusted publisher, AWS/Azure/GCP federation, npm tokens, GitHub PATs), installing a manifest diff stage between lockfile resolution and tarball extraction, PoC validation of Falco or Tetragon detection on your build hosts — in this order, with a documented status at each step.
This is the operational routine from DevSecOps as a Service and our standard line for supply chain hardening. If you run Node.js, React, Vue, or Solid frontend or Python AI agent stacks in the German Mittelstand, operate platforms for multiple clients, or run a curated artifact pipeline with an SLSA provenance gate whose structural assessment you now need to revisit — let’s talk before the next production CI run.

![[Translate to English:] Hölzerner Setzkasten mit präzisem Raster aus Edelstahl-Würfeln auf glattem Beton; in drei Fächern stehen leicht abweichende Messing-Würfel gleicher Größe als stille Substitution. Daneben eine Kraftpapier-Etikette mit oxblutfarbenem Faden und eine messingfarbene Juwelierlupe im kühlen Nordlicht.](/fileadmin/_processed_/5/b/csm_0d49848511671c27dc01822451c27320dd11fa770ba1b43b9369bbf3178f8480_3232cf94e2.jpg)
![[Translate to English:] Ein altes Messing-Sprachrohr auf Beton, aus dem still ein dünner roter Faden über den Rand zu einem aufgeschlagenen ledernen Notizbuch zieht; drei Kraftpapier-Umschläge mit Sigeln und eine Messinglupe rahmen die Szene im kühlen Nordlicht.](/fileadmin/_processed_/5/6/csm_fe6852c689462a72ad8019d64c873316e6af6717ea48f58e7756bbede89f8cc6_30fcef2cdc.jpg)
![[Translate to English:] Drei identische Kraftpapier-Versandkartons in einer Reihe auf Beton, der mittlere mit gebrochenem dunkelrotem Wachssiegel, daneben ein Messingstempel und eine Lupe.](/fileadmin/_processed_/9/6/csm_002ea51bfedfb9a79176ea02ab055e8d88cfcb4e9cef4647c348ee0e3f81f78b_0a97675402.jpg)
![[Translate to English:] Sechs nahezu identische Kraftpapier-Umschläge mit Wachssiegeln auf Beton in präziser Anordnung; einer ist seitlich geöffnet, ein dünner roter Faden zieht still zu einem leeren ledernen Wallet; daneben Messinglupe und Messingschlüssel im kühlen Nordlicht.](/fileadmin/_processed_/4/b/csm_90d5b90398618e7ff838e23aa6871149fffb04222d5a5ddb7c9d3a97ce50c64c_4b1ffe84fd.jpg)
