On June 1, 2026, backdoored versions of at least 32 packages appeared in Red Hat's official npm scope, carrying a self-replicating credential stealer that came to be called Miasma. Microsoft's analysis traced the publish path; StepSecurity and Cloudsmith documented the worm mechanics; Semgrep covered the v2 wave that switched to a malicious binding.gyp and hit 57 more packages.
Two details make Miasma a step beyond the spring's other campaigns. The publish vector abused a gap in npm trusted publishing with GitHub Actions, letting the attacker mint provenance-attested releases from a temporary branch, so the packages looked more trustworthy by the usual signals. And the payload self-replicates: it uses stolen npm OIDC tokens to republish itself across other packages its victims maintain.
The chain, stage by stage
Strip the novelty and Miasma is a chain of behaviors, each individually detectable. This is how it maps to the behavioral rules in Aguara v0.23.0, which shipped on June 7 informed by exactly this campaign:
| Stage | Behavior | Detection |
|---|---|---|
| Entry | npm lifecycle hook runs local JavaScript the moment npm install executes | SUPPLY_026 |
| Second stage | Node shells out to an alternate runtime to run the next payload stage | JS_BUN_SECOND_STAGE_001 |
| Control channel | GitHub itself used as the command and payload channel: API writes a release bot has no business making | JS_GITHUB_C2_001 |
| Host tampering | Writes to sudoers, linker preload, CA stores, sshd config, resolvers | JS_SUDOERS_TAMPER_001, JS_HOST_TRUST_TAMPER_001 |
| Cleanup | Destructive wipe of credential stores, shell history, evidence | JS_WIPER_TRIPWIRE_001 |
Each rule is chain-gated: running Bun is not a finding, a GitHub API call is not a finding, deleting node_modules is not a finding. The rules fire on the combination a worm needs and a legitimate package does not: the API write paired with a credential read, the delete call that has the actual capability to destroy a credential store rather than a build cache.
Two-speed response
The response came in two releases, and the split is the point. v0.22.2 shipped on June 3 with the advisory data: the compromised package versions, sourced from OSV records, matched offline by aguara check. That is the fast lane, useful the same week.
v0.23.0 shipped on June 7 with the six behavioral detections above. That is the slow lane, useful for the next campaign, because Miasma v2 had already demonstrated the pattern: same chain, new packaging. IOC data answers "am I exposed to this incident"; behavior rules answer "would I notice the next one."
Honest limits
Static analysis of a chain is not complete worm detection, and we do not claim it is. A payload obfuscated past the lexical floor, or split across stages a single-file analysis cannot connect, can evade a static rule. The detections are bounded to high-confidence shapes because a noisy scanner gets ignored, and an ignored scanner catches nothing. Coverage is documented per rule with aguara explain <ID>, including what each rule does not see.
The structural lesson of Miasma is the same one pnpm v11 acted on: install-time execution is the chokepoint. Approval-gated build scripts, release-age windows, and pre-install lockfile checks attack the chain at its entry, where defense is cheapest.
Would your pipeline notice the next worm?
aguara audit . --ci composes advisory matching and behavioral scanning into one deterministic verdict.