The Shai-Hulud Family: A Taxonomy of Modern Supply Chain Worms 🪱
Seven supply chain worm campaigns, from Shai-Hulud to malware with valid provenance, and what each one teaches us about software trust.
From Shai-Hulud to Malware with Valid Provenance
Software supply chain security used to be relatively straightforward. You scan your dependencies. You generate an SBOM. You pin versions. You sign your artifacts. You generate provenance. You harden your CI/CD pipeline.
Then the worms arrived.
Since September 2025, supply chain worms have evolved at an alarming pace. More importantly, they have evolved in response to the defenses we put in front of them.
That makes them interesting to classify.

This is not a taxonomy based on malware families or strict threat-actor attribution. Researchers sometimes group these campaigns differently, and attribution is not always definitive.
Instead, I want to classify them the same way I approach them in my Beyond SBOMs and Defense Against the Dark Arts talks:
Introduce a defense. Then introduce the worm that teaches us why that defense is not enough.
Welcome to the wormhole.
Key Takeaways
- An SBOM can accurately inventory a package without telling you that the package itself is malicious.
- Disabling lifecycle scripts blocks important attack paths, but package installation has other execution surfaces.
- Developer workstations, IDEs, AI agents, CI caches, and publishing workflows are all supply chain trust boundaries.
- Signatures and provenance provide valuable evidence; policy determines whether the path that produced an artifact was legitimate.
Before the Worms: Scan Your Dependencies
Let's begin with a perfectly reasonable security strategy.
You don't want vulnerable software in production, so you:
- scan dependencies;
- scan container images;
- generate an SBOM;
- monitor newly discovered CVEs;
- patch vulnerable components.
Tools such as Docker Scout, Trivy, Snyk, and others are excellent at this.
An SBOM makes the contents of an artifact visible. A scanner tells us whether one of those components has a known vulnerability.
This is so standard and baseline that I have written a whole chapter on it.
But neither answers a different question:
What if the dependency itself is intentionally malicious?
That brings us to September 2025.

1. Shai-Hulud I
In September 2025, Shai-Hulud I became the first successful self-propagating worm observed in the npm ecosystem.
The model was beautifully horrible.
A compromised npm package executed a malicious postinstall script when a developer installed it. The payload searched the developer or CI environment for credentials, including npm tokens, GitHub tokens, and cloud credentials.
If it found an npm publishing token, it didn't stop at stealing it.
It used it.
The worm automatically modified other packages that the victim was allowed to publish and released infected versions of them to npm. Those packages were then installed by other developers, whose credentials could be stolen in turn.
The software supply chain had acquired something it had previously been missing:
Replication.
Wiz described Shai-Hulud I as the first successful self-propagating attack in the npm ecosystem. The campaign spread from compromised packages such as @ctrl/tinycolor into hundreds of packages across multiple maintainers.
What failed?
Not the SBOM.
An SBOM was never supposed to work alone.
It tells you what is there. You still have to check those entries, compare them with vulnerability intelligence, and keep checking as new information appears.
And zero-days were not new either.
Log4Shell had already shown us that a package could sit quietly in dependency trees until a newly discovered vulnerability made it dangerous.
Shai-Hulud I changed where the trouble started.
In the classic vulnerability story, the package became exploitable after the application reached production and exposed the vulnerable code path.
This package did not wait for production.
The moment a developer or CI job installed it, its malicious postinstall script came alive. And it came alive in an environment that might already contain npm tokens, GitHub tokens, cloud credentials, SSH keys, and access to other repositories.

2. Shai-Hulud II
The second coming arrived in November 2025.
Shai-Hulud II changed the execution mechanism from postinstall to preinstall and introduced a separate runtime.
The package dropped setup_bun.js, looked for the Bun JavaScript runtime, downloaded it when necessary, and then used Bun to execute the malicious payload. Microsoft and Datadog both documented the change to preinstall and the use of Bun.
Why Bun?
Because defenders had become very good at watching Node.js.
Detecting suspicious Node processes is considerably less useful when your attacker brings another JavaScript runtime with them.
Shai-Hulud II also became more aggressive. Researchers documented credential harvesting, propagation across packages, persistence mechanisms, and destructive behavior if the worm could neither spread nor successfully exfiltrate its data.
The defense
This led to a stronger recommendation:
npm install --ignore-scripts
Don't merely watch postinstall.
Disable lifecycle-script execution unless the package genuinely needs it.

3. Mini Shai-Hulud
In January 2026 I wrote an article titled The Whispering JAR. It was about the first two Shai-Hulud attacks, and how to defend against them. I suggested, spoiler alert, using a sandbox for development!
You know it's a spoiler if you have read Dune.
The article was published in JAVAPRO's blog on April 23rd, and has been featured in two of their magazine issues since then.

The Whispering JAR: Java Security Lessons Hidden in a Fantasy Tale
A fantasy tale about Gord, Rothütle, and a malicious JAR reveals practical lessons for defending the Java software supply chain.
containersecurity.dev
On April 29, 2026—incidentally my 35th birthday—Mini Shai-Hulud appeared.
Thank you, universe.
Wiz reported that TeamPCP compromised packages associated with SAP's Cloud Application Programming Model, including @cap-js/sqlite, @cap-js/postgres, @cap-js/db-service, and mbt.
Once again, malicious preinstall execution and Bun appeared in the attack chain. The malware harvested GitHub, npm, cloud, Kubernetes, CI/CD, and other credentials.
But Mini Shai-Hulud introduced something more interesting.
It didn't see the developer workstation merely as somewhere from which credentials could be stolen.
It saw it as somewhere it could live.
The campaign planted persistence into the development context, including VS Code configuration. Subsequent analysis of this lineage documented AI-assisted development environments as another persistence and execution surface.
This matters because our development environment had changed.
Developers were no longer the only entities opening repositories, reading configuration files, modifying source code, and executing commands.
AI coding agents were doing it too.
The defense
So the boundary moved again.
If arbitrary repositories and packages can influence the tools operating on your workstation, then the workstation itself needs isolation.
Don't give an AI coding agent unrestricted access to:
- your home directory;
- SSH keys;
- npm tokens;
.envfiles;- cloud credentials;
- your Docker socket;
- every repository you can access.
Put the agent—and preferably untrusted development workloads—in a sandbox.
The developer workstation is part of the software supply chain now.
Then we tried to solve another old problem: static publishing credentials.
Instead of keeping long-lived npm tokens in CI, we could use short-lived identities through OIDC and trusted publishing.
Even better, we could generate provenance proving exactly where an artifact came from.
Surely that would help.
And it does.
Until May 11.

4. TanStack
The TanStack compromise is one of the most important events in this taxonomy because the attacker didn't simply steal a long-lived publishing token.
They attacked trust itself.
According to TanStack's postmortem, an attacker chained three weaknesses:
- a dangerous
pull_request_targetworkflow; - GitHub Actions cache poisoning across the fork/base trust boundary;
- extraction of an OIDC token from the release runner's process memory.
When the legitimate release workflow later restored the poisoned cache, attacker-controlled code executed inside a privileged environment.
The result was 84 malicious versions across 42 @tanstack/* packages published within minutes.
And here is the particularly interesting part:
The packages had valid npm provenance.
The provenance pointed back to the legitimate TanStack repository and publishing infrastructure.
Because that's exactly where they came from.
The attacker had compromised the process that was supposed to establish trust.
A small but important SLSA distinction
This incident is frequently described as malicious software carrying valid “SLSA Level 3” provenance.
The SLSA project's own analysis makes an important distinction.
The attestations were cryptographically valid, but the build environment did not actually satisfy SLSA Build L3's isolation requirements. Proper isolation would have prevented this specific cache-poisoning path.
That's important.
TanStack did not prove that SLSA was useless.
It proved something more general:
A signature proves who signed something. Provenance proves where and how something was produced. Neither magically proves that the thing is good.
The defense
CI caches are executable trust boundaries.
Treat them accordingly.
Separate untrusted pull-request workloads from privileged release workflows.
Restrict OIDC permissions.
Protect publishing jobs.
Isolate caches.
Don't let untrusted code reach a context that can mint a trusted identity.
Meanwhile, the worms kept changing.

5. Miasma Wave 1
On June 1, 2026, Miasma appeared in packages under Red Hat's @redhat-cloud-services namespace.
Wiz identified malicious releases across at least 32 packages. The underlying malware heavily reused Mini Shai-Hulud tradecraft but introduced additional capabilities and evasion.
One change was particularly unpleasant for defenders:
Each infection could produce a uniquely encrypted payload.
Hashes are wonderful indicators when malware stays the same.
If every infected artifact contains a different encrypted payload, hash-based detection becomes considerably less useful.
Miasma also expanded cloud-identity collection and continued using legitimate CI/CD mechanisms. Wiz observed malicious publishing through workflows capable of producing valid provenance attestations.
JFrog's analysis also documented broad credential collection, AI-tool persistence, and a destructive token monitor.
You could say the malware was threatening to uninstall the French language pack:
rm -fr ~/
The defense
Static detection alone cannot win this game.
Now you need to care about behavior:
- Why is
npm installspawning another runtime? - Why is a build reading cloud credentials?
- Why is a package talking to GitHub's API?
- Why is a build worker enumerating secrets?
- Why does installing a JavaScript dependency result in unexpected outbound network connections?
Runtime observation, restricted egress, ephemeral environments, and anomaly detection start becoming much more valuable.
There was still one comforting defense left.
At least we could disable npm lifecycle scripts.
Two days later:
Nope.

6. Miasma Wave 2: Phantom Gyp
Phantom Gyp is my favorite example of why security recommendations need threat models rather than slogans.
We told developers:
npm install --ignore-scripts
The attackers effectively answered:
Fine. We won't use npm scripts.
The June Miasma wave abused binding.gyp, the configuration mechanism used by node-gyp for native Node.js modules.
Instead of putting the malicious execution path in:
{
"scripts": {
"preinstall": "..."
}
}
the attack could trigger code through native build configuration.
JFrog and Wiz both documented the binding.gyp execution technique in later Miasma packages.
The important point isn't binding.gyp itself.
The important point is what it represents:
We blocked one execution mechanism, so the attacker moved one layer lower.
--ignore-scripts was not broken.
It did exactly what it promised.
The assumption that “no lifecycle scripts means no code executes during installation” was broken.
The defense
Don't treat package installation as a harmless file-copy operation.
It is execution of untrusted supply chain input.
Use:
- isolated and ephemeral build environments;
- network restrictions;
- package cool-down periods;
- behavioral detection;
- controlled native compilation;
- strict separation between build environments and valuable credentials.
And this brings us back to provenance.
Because in August, Mini Shai-Hulud had one more lesson.
7. Mini Shai-Hulud Strikes Back
On August 28, 2026, ten malicious versions of @7nohe/openapi-react-query-codegen were published to npm.
Socket linked the compromise to activity consistent with Mini Shai-Hulud, while noting that technical overlap alone does not prove common operators.
The malicious package targeted cloud credentials, package-registry credentials, GitHub Actions secrets, and AI-agent configuration, while retaining self-propagation functionality associated with Mini Shai-Hulud.
But we've seen credential stealing before.
We've seen propagation before.
We've seen attacks against AI development environments before.
The interesting part is this:
All ten malicious versions had valid npm provenance.
Again.
This time, Socket found a comment-triggered GitHub Actions publishing workflow.
An untrusted GitHub account could trigger the project's legitimate publishing mechanism against code originating from a pull-request fork.
The trusted publishing system then did precisely what it was designed to do.
It authenticated the legitimate GitHub Actions workflow.
It published the package.
It generated provenance.
And the result was malicious.
There was no forged attestation.
There was no fake publisher.
There was no need to break Sigstore's cryptography.
The attacker convinced the real trusted process to publish the wrong thing.
That distinction matters enormously.
Provenance is evidence, not trust
Provenance answers questions such as:
- Which repository produced this artifact?
- Which workflow built it?
- Which identity published it?
- What source revision was involved?
Those are extraordinarily valuable questions.
But provenance cannot answer:
Was this workflow supposed to publish this code?
That requires policy.
Branch protection.
Review requirements.
Trust-boundary isolation.
Restrictions on workflow triggers.
Controlled release branches.
Minimal permissions.
And humans deciding which paths through a build system are actually allowed to result in production artifacts.
The Taxonomy
Seven campaigns. Seven lessons.
| Campaign | What changed | Assumption it broke |
|---|---|---|
| Shai-Hulud I | Install-time execution and propagation through stolen credentials | Dependency risk begins when the application reaches production |
| Shai-Hulud II | preinstall plus the Bun runtime | Watching obvious Node/postinstall behavior is sufficient |
| Mini Shai-Hulud | Developer, IDE, and AI context becomes a persistence target | The developer workstation sits outside the supply chain perimeter |
| TanStack | Cache poisoning, OIDC abuse, and valid provenance | Trusted publishing automatically means trusted output |
| Miasma Wave 1 | Per-infection encryption and increasingly hostile behavior | Static signatures can reliably identify the payload |
| Miasma Wave 2: Phantom Gyp | binding.gyp execution | --ignore-scripts means installation cannot execute attacker code |
| Mini Shai-Hulud Strikes Back | A legitimate workflow produces malicious, attested releases | Valid provenance means the source-to-artifact decision was legitimate |
These are not just increasingly sophisticated pieces of malware.
They are trust-boundary attacks.
Each worm asks the same question:
What does the defender still trust?
SBOMs, scanning, sandboxing, OIDC, SLSA, and provenance are still valuable. The mistake is turning any one of them into a synonym for trust.
The next worm doesn't need to break your security control. It only needs to find the thing your security control still trusts.

SIP It Up
Seven worms. Seven broken assumptions.
So what should you actually do?
If You Do One Thing: Wait Five Days
Set a five-day cooldown on new dependency releases.
min-release-age=5
npm introduced min-release-age in version 11.10.0, and the value is measured in days. So 5 means five days.
That's it.
The worms in this taxonomy depended on a simple advantage: they were installed before defenders had time to identify the malicious releases.
Five days changes that equation.
And five days is not an arbitrary number.
Look at the incidents for which we have detailed response timelines:
| Campaign | Time to detection/action | What happened |
|---|---|---|
| Shai-Hulud II — Postman | ~3 hours to internal investigation; ~6 hours until final malicious versions were removed | Publishing token revoked; 13 packages unpublished directly; remaining four deprecated and later removed by npm |
| Mini Shai-Hulud — SAP CAP | ~7–8 minutes to detection | Clean versions superseded the malicious releases within roughly 1–6 hours |
| TanStack | 20–26 minutes to detection | First versions deprecated after ~59 minutes; all 84 deprecated after ~1h 43m; final tarball removed after ~4h 35m |
| Miasma Wave 1 | Same day | By 13:00 UTC, Wiz reported that most malicious versions had already been revoked |
| Mini Shai-Hulud Strikes Back | Within hours | latest was restored to the clean 3.0.2 after roughly 2h 50m; all ten malicious versions were deprecated and removal was requested |
Postman's infected packages were available for about six hours before the final version disappeared.
The SAP CAP packages were detected only around seven or eight minutes after publication, with clean versions replacing them within hours.
TanStack gives us the most detailed timeline: public detection within 20–26 minutes, all 84 malicious versions deprecated within 1 hour 43 minutes, and the final tarball removed by npm within 4 hours 35 minutes.
With Miasma, most affected releases had already been revoked by the time Wiz updated its report at 13:00 UTC on the day of the attack.
And even the August 28 openapi-react-query-codegen attack, the newest member of this family, had its latest tag restored to a known-good release at approximately 22:51 UTC, less than three hours after the malicious publishing began. The maintainer deprecated all ten affected versions and requested their removal.
The other two entries fit comfortably inside the same window. Aikido reconstructed Shai-Hulud I's first malicious release on September 14, and Wiz publicly documented the campaign on September 16. JFrog documented Phantom Gyp on June 4, three days after the first Miasma wave appeared.
Every campaign in this taxonomy was identified within the five-day window. Where detailed response timelines are available, remediation usually began within minutes or hours.
That is the point of the cooldown.
You are not betting that your security scanner will magically recognize an unknown malicious package the moment it is published.
You are deliberately refusing to consume it while researchers, maintainers, registries, vendors, and security databases catch up.
By the time your five-day window expires, these examples were no longer mysterious new releases. They had been detected, deprecated, removed, superseded, or formally recorded as malicious.
This is why Xygeni highlighted the five-day cooldown from my Security Immediate Plan as a particularly effective, low-effort defense against npm worms: it deliberately gives defenders more time than these campaigns have historically needed to react.
The five-day cooldown converts an unknown-malware problem into a known-malware problem.
A cooldown is not a universal malware defense. A patient attacker could deliberately leave a malicious package dormant beyond the waiting period.
But the worms covered in this taxonomy weren't patient.
They depended on rapid publication, installation, credential theft, and propagation.
Five days breaks that cycle.
If You Do Five Things: SIP
If you're willing to do more than one thing, do five.
That's why I created SIP: the Security Immediate Plan.

SIP: Five Immediate Software Supply Chain Controls
SIP is a five-step emergency plan for reducing software supply chain risk across AI agents, dependencies, containers, attestations, and releases.
containersecurity.dev
SIP adds four more controls around the cooldown:
- Sandbox local AI agents and untrusted development workloads.
Keep developer credentials, SSH keys, cloud tokens, and the Docker socket outside their reach. - Freeze unvetted dependencies and disable lifecycle scripts.
Use the five-day cooldown, committed lockfiles, andignore-scripts=trueto eliminate the easiest execution paths. - Build in hardened, isolated environments.
Use ephemeral builders, restricted egress, minimal credentials, and hardened runtime images. - Generate an SBOM and maximum-level provenance.
Know exactly what entered the build, which workflow produced it, and which digest came out. - Continuously scan the SBOM attached to that exact digest.
Once the malicious release is identified, your security tooling finally has something concrete to detect.
One control buys the ecosystem time.
Five controls make sure that even when something gets through, it has fewer credentials to steal, fewer places to persist, fewer systems to reach, and much better evidence left behind.
So if you remember one thing from this entire taxonomy:
Want to stop the worms? Wait five days.
And if you want the complete answer:
SIP it up.
Further Reading
- Shai-Hulud I: Ongoing Package Supply Chain Worm — Wiz Research
- The Shai-Hulud II npm Worm — Datadog Security Labs
- Root Cause Analysis: Shai-Hulud II — Postman Engineering
- Mini Shai-Hulud Targets SAP npm Packages — Wiz Research
- SAP CAP Mini Shai-Hulud Supply Chain Attack — Onapsis
- TanStack npm Supply Chain Compromise: Postmortem — TanStack
- Mini Shai-Hulud: Where SLSA's Boundaries Fall — SLSA
- Miasma Targets Red Hat npm Packages — Wiz Research
- Miasma: The Spreading Blight — JFrog Security Research
@7nohe/openapi-react-query-codegenCompromise (GHSA-9pvf-vcx3-x239) — GitHub Security Advisory- OpenAPI React Query Codegen Compromise — Socket Research
For a deeper look at developer-context defenses, read Mini Shai-Hulud: The Next Evolution of npm Supply Chain Worms and Beyond SLSA: How to Stop Zero-Click CI/CD Worms.
Characters in this entry

Rothütle
The Black Forest scholar and technical specialist who turns strange security concepts into battlefield solutions.

Mina
Mina, the immortal undead assassin whose empathy is as surprising as her reputation.

S-9
An A-Series service android whose hostile compromise turns a high-rise café into a software supply-chain incident.

Shadow
The classified Bravo Team special-operations operative who confronts the evolving Shai-Hulud family of software supply-chain threats.

Phantom Gyp
The opera-cloaked worm of the Shai-Hulud family whose grand performance draws Mina into a gothic confrontation.
