Signed Is Not Safe: When SLSA Provenance Ships Malware
Mini Shai-Hulud pushed npm packages carrying valid SLSA Build Level 3 provenance and Sigstore signatures. Supply-chain trust just broke a layer deeper — and runtime traffic is the last line that still sees it.
The signature was valid. The package was malware.
On 2026-05-11 UTC, a new wave of the Shai-Hulud supply-chain worm hit npm. By the time BleepingComputer published its writeup the following day, researchers had counted 84 malicious versions across 42 @tanstack/* packages, plus @mistralai/*, Guardrails AI, UiPath, OpenSearch, Bitwarden CLI and several SAP-published modules. Endor Labs put the total at 160+ compromised packages on npm; Socket counted 416 malicious artifacts across npm and PyPI together. The threat actor is tracked as TeamPCP.
What sets this incident apart from every prior npm worm is not the volume. It is what the packages carried in their metadata: valid SLSA Build Level 3 provenance attestations and Sigstore signatures, generated by the legitimate upstream workflows running on hijacked GitHub Actions runners. The cryptographic chain verified. Downstream consumers — npm install, organisation-wide provenance gates, the Sigstore CLI, anything reading the in-toto attestation — saw a "trusted publisher" badge and let the bytes through. The dropper executed on first install.
If the past three years of supply-chain investment landed on the position that "verify the signature, trust the package", this is the incident that retires that position.
Why this breaks the defensive model
Software supply-chain controls — SLSA, in-toto, Sigstore, GitHub's own attestation feature — were designed around one threat model: an attacker steals a maintainer's publish token, or sneaks unsigned malware past a gate. The countermeasure is to bind the build process itself to a verifiable signing identity, so downstream consumers can refuse anything not signed by that identity.
That model assumes the signer is honest. In a Pwn Request / pull_request_target attack, the signer is honest — but the signer is also a build runner executing code submitted by an external contributor, with full access to the publisher's OIDC token and signing key material. The attacker never has to steal anything. They get the legitimate signer to sign their code.
The downstream effect is uncomfortable but clear:
- Verifiable provenance still has value — but for attribution, not trust. After the fact, you can prove which workflow produced which artifact. You cannot prove the workflow was not coerced into producing malware.
- A signed package is no longer a stronger safety signal than an unsigned one against this attack class. It just looks like one.
- Provenance-only gates (block anything without SLSA L3) provide no additional protection against signed malware. The package has SLSA L3.
"We're not paying because we don't negotiate with criminals." — Grafana, on declining the CoinbaseCartel extortion attempt after the same OIDC/Action abuse class led to full codebase theft, disclosed 2026-05-16.
Grafana's incident is not directly linked to TeamPCP, but it is the same operational pattern abused by a different crew within the same week. When two unrelated campaigns are exploiting the same GitHub Actions abuse class against high-profile victims inside one calendar week, the pattern — not the individual project — is the story.
The OIDC / Pwn Request pattern
The technical chain inside Mini Shai-Hulud combined three known-but-rarely-chained vulnerability classes on GitHub Actions runners. Snyk's reverse engineering and Wiz's analysis line up on the same three steps:
- Risky
pull_request_targetworkflow. Unlikepull_request,pull_request_targetruns the workflow in the context of the target repo — with that repo's secrets and OIDC permissions — even when the triggering PR comes from an untrusted fork. Many projects accept this for legitimate reasons (auto-labelling, CLA checks). Many also forget that the PR can modify the very scripts the workflow executes. - GitHub Actions cache poisoning. The attacker plants attacker-controlled artifacts into the Actions cache via the forked PR. On the next privileged workflow run, those artifacts are restored into the runner and executed.
- OIDC token theft from runner memory. With code execution inside the privileged runner, the attacker scrapes the runner's environment for the short-lived OIDC token used to authenticate to npm's trusted publisher endpoint. The token is valid. The publish call is signed. SLSA provenance is generated. Sigstore signs the result.
Nothing in this chain requires stolen long-lived secrets. The maintainer's npm credentials, the SSH keys, the Sigstore root — all untouched. The attacker exploited the runner-to-publisher trust boundary, which is exactly the boundary the modern supply-chain stack was designed to make trustworthy.
What the post-install actually did
Once a malicious @tanstack/[email protected] made it onto a developer machine, the payload executed inside the npm install lifecycle. From the TanStack postmortem and corroborating vendor analysis, the dropper:
- Targeted macOS and Linux developer workstations (Windows paths absent from the loader)
- Harvested SSH keys from
~/.ssh, cloud credentials from~/.aws,~/.config/gcloud, and Azure CLI cache - Scraped browser session cookies for GitHub and npm
- Beaconed over the Session P2P encrypted messenger network, deliberately chosen so the C2 traffic blends into normal Session client traffic and is hard to block at firewall or DNS level
- Contained geofencing logic: destructive secondary payloads targeting endpoints in Russia, Israel and Iran
OpenAI publicly confirmed on 2026-05-14 that two employee macOS devices were compromised. The attackers conducted "credential-focused exfiltration activity" against internal repositories reachable from those machines. OpenAI rotated its macOS desktop-app signing certificate, set 2026-06-12 as the hard revocation date for the prior certificate, and disclosed that the affected devices were still in a phased rollout of new package-manager controls — minimumReleaseAge and provenance validation that would have stopped the install. The two impacted endpoints had not yet received that configuration.
That detail matters. The mitigations existed. The deployment had not finished. A nine-day window between "we know the control" and "every endpoint has the control" was enough to land a high-profile victim.
Detection moves to the network
Where in the lifecycle does this attack become visible?
| Defensive layer | Sees the attack? | Why / Why not |
|---|---|---|
| SLSA provenance gate | No | Provenance is valid, signed by the legitimate workflow |
| Sigstore verification | No | Signature chains to a trusted Fulcio identity |
npm audit |
No | Package is too new to be in any advisory database |
| EDR on dev workstation | Partial, late | Sees node spawn a child process. Reads "npm doing npm things" |
| DNS allow-listing | Partial | Defeats naive C2; misses Session P2P |
| Wire-speed traffic ML | Yes | Sees unfamiliar destinations + outbound burst + protocol shape |
The signal exists. It just isn't in the layers everyone has invested in. The package install kicks off, in a tightly clustered minute, a behaviour the workstation has never produced before: hundreds of small reads against ~/.ssh and ~/.aws, a process tree rooted in npm reaching out to Session bootstrap nodes that the corporate network has never seen, and an outbound payload size distribution that does not match any of the developer's normal traffic. None of these on their own is high-confidence malicious. Together, on a workstation that historically only ingests, they are an event.
This is the layer where a defender still has a fighting chance against signed malware. The cryptographic chain handed the attacker a free pass through the static verification gates. The runtime traffic did not.
What good response looks like
Concrete actions for security teams reviewing their exposure to this attack class:
- Audit every
pull_request_targetworkflow in your org. Treat any workflow that combinespull_request_targetwith checkout-of-PR-head as a critical issue. Either pin to a fixed SHA inside the workflow file or migrate topull_requestand a separate, gated publishing job. - Cap the blast radius of OIDC tokens. Use the tightest possible
id-token: writescope. If the workflow does not publish, do not grant the permission. - Enforce
minimumReleaseAgefor npm and pip. A 72-hour minimum age would have caught Mini Shai-Hulud before the affected versions reached most installs. This is the single highest-leverage control for organisations whose CI is already strong. - Pin lockfiles by integrity hash. Assume they will be defeated again — but the cost of bypassing them is non-zero and slows attackers down.
- Treat developer workstations as monitored egress points. They are no longer "trusted endpoints behind the proxy". The egress baseline for a developer machine is narrow and observable; deviations from it are the signal.
- Maintain a runtime allow-list of outbound destinations per workload. Anything else is an event. Session P2P bootstrap nodes are a particularly cheap thing to baseline — almost no enterprise traffic legitimately touches them.
None of these depend on knowing a specific package is malicious in advance. That is the point. The next signed-malware campaign will use a different package and a different exfil channel.
Where Zero Hunt sits in this picture
Most of this article is the problem statement. Where the platform fits in is narrower, and specific.
The pillar that matters here is AI Traffic Analysis. A signed npm package is invisible to provenance gates by design — the signature is the gate. What is not invisible is the dropper reaching out to Session bootstrap nodes, the SSH/cloud-credential harvest hitting the network as an outbound burst, and the lateral fan-out after the first beacon lands on a credential. Zero Hunt's deep-learning traffic model runs four parallel inference heads — suspicious traffic, malware classification, attack-type identification, application fingerprinting — over the live PCAP stream at 2.7+ Gbit/s baseline throughput on a single appliance. It is trained on billions of sequences and runs locally on the appliance GPU; nothing about that detection path depends on the cloud, on the package being on a known-bad list, or on the signature being broken. The behaviour is the signal.
The secondary pillar is AI Generative Pentest. The 10-agent swarm can model the post-compromise blast radius from a hijacked developer workstation — what an attacker reaches from a single foothold, which internal repositories and cloud roles are exposed via the harvested credentials, where the lateral movement terminates. The output is a finding chain, ECDSA-signed at write time, that survives the audit conversation that always follows an incident of this class.
The reason supply-chain incidents read so badly in postmortems is the gap between "we trusted the signature" and "we saw the C2 nine days later". The defensible position is not to trust less; it is to look at the layer the signature cannot vouch for. That layer is the wire.
Sources:
- BleepingComputer — Shai-Hulud attack ships signed malicious npm packages (2026-05-12)
- Snyk — TanStack npm packages compromised
- Wiz — Mini Shai-Hulud strikes again
- TanStack postmortem
- The Record — OpenAI asks macOS users to update (2026-05-14)
- The Hacker News — Grafana GitHub token breach (2026-05-16)