//nefariousplan

Self Propagating Supply Chain

Worm pattern in a package registry: infected package harvests credentials, publishes to the next.

Worms are a classical category of attack in operating systems. Code that replicates itself across machines, using vulnerabilities in network services to propagate. The classical worms, Morris, Blaster, Conficker, targeted OS-level services. Their payload was propagation. Their victim population was the network-reachable install base of the vulnerable service.

Self-propagating supply chain is the same shape inside a package registry. The vulnerability is not a network service. It is the publish mechanism. The replicator is not a binary. It is a package version. The victim population is not a network. It is the set of developers who install the infected package into their own environments, carrying their own publish tokens.

Mechanism

The propagation cycle has a single starting event and a repeating payload. A maintainer account is compromised by any means (phishing, credential theft, prior supply-chain breach). The attacker publishes a malicious version of a package the maintainer owns. Every developer who installs the package receives the payload. The payload's job is two-fold: exfiltrate whatever credentials it can find in the developer's environment, and then use those credentials to publish infected versions of any packages those credentials have publish authority over.

The growth dynamic is exponential in the branching factor of the dependency graph. A maintainer with one compromised account owns N packages. Each of those packages reaches M install environments. Each install environment harvests credentials from K maintainers. Each of those maintainers owns some more packages. The next round of infection is NMK times the previous round, bounded only by detection and revocation.

Classical worms worked because the network was full of similar services with the same vulnerability. Self-propagating supply chain works because the package ecosystem is full of similar maintainers with the same authority model. The vulnerability the worm exploits is not a bug in any package. It is the ecosystem's trust decision: an npm token stored on a developer's machine can, by default, publish to any of that developer's packages, and developers routinely run npm install in environments that have publish tokens accessible.

Exhibits

Shai-Hulud: The First npm Worm. The exemplar. An npm package compromised via maintainer-account takeover shipped a harvester that stole npm tokens from every installation environment it reached. The harvested tokens were used by the same payload to publish infected versions of packages those developers maintained. Each new infection became a new propagator. The takedown chased a branching infection graph. The registry's protocol had no concept of "this publish is part of a worm," so each new publish looked locally legitimate. The only evidence of the worm was the pattern in the publish stream, which registry side detection had to build after the fact.

Exhibits

Shai-Hulud: The First npm Worm. The exemplar. An npm package compromised via maintainer-account takeover shipped a harvester that stole npm tokens from every installation environment it reached. Harvested tokens were used by the same payload to publish infected versions of packages those developers maintained. Each new infection became a new propagator. The registry protocol had no concept of 'this publish is part of a worm'; each new publish looked locally legitimate. The worm's signature was the pattern in the publish stream.

Boundaries

Not every maintainer-account compromise is a worm. A one-off malicious publish that exfiltrates data without attempting to republish is a maintainer-account-compromise instance without the self-propagating property. The ingredient that makes this pattern specific is the "publish new infected versions" step. Without that step, the attack does not compound across maintainers.

Not every credential harvester. Many stealers extract credentials and stop there, sending them to a collection server for later use. Self-propagating supply chain requires that harvested credentials feed the next publish automatically, inside the same payload, while the environment is still warm. The harvester becomes the publisher, in the same process, on the same machine.

Not every registry incident. A registry that serves malicious packages because it was itself compromised is a different problem (registry-side breach). Self-propagating supply chain works with a perfectly functioning registry. The registry correctly delivers whatever each compromised maintainer published. The problem is that the publishing side was coopted.

Defender playbook

Rate-limit the publish graph. A single npm token publishing new versions to twenty packages in two hours is worm behavior whether or not you have caught this specific payload. Registry-side behavioral detection on publish-burst patterns catches a meaningful fraction of early propagation before the branching factor explodes.

Separate install environments from publish environments. Developers routinely run package installs during normal work. Publish tokens should not live in the same process space as install runs, because install runs execute arbitrary postinstall scripts with the environment's full read access. Machines or CI jobs that publish should not install untrusted packages. Machines that install should not hold publish tokens.

During a known campaign, rotate every token that ever touched an install during the window. The worm's damage is nested: the first compromise steals from N developers, each of whom steals from M more. Revocation has to follow the same graph, in reverse, and it has to happen fast enough that tokens stolen in hour one are not still accepted in hour ten.

Monitor your own publish activity at the maintainer level. If you are a package maintainer, the most useful detection you can run is on your own npm audit log. A publish you did not initiate should page you inside the hour. Most maintainers have never set this up. Attackers have.

Treat the worm's existence as inventory. Shai-Hulud established the pattern as a 2025 reality. Assume another worm will land on npm in 2026, then again in 2027. The specific package that carries the next one is unknown. The pattern's presence is knowable. Plan for it.

Kinship

Maintainer Account Compromise. The initiating event for every worm. A self-propagating supply chain incident is a maintainer-account-compromise that autocatalyzed. The first infection is the parent pattern. The subsequent rounds are the worm.

Trust Inversion. The propagation mechanism. Each new infected publish rides the trusted authorizer of the next compromised maintainer. The registry sees a legitimate maintainer publishing a legitimate package. The publish is legitimate from the protocol's perspective. The attack is in the choice of who is publishing.

Revocation Gap. The scaling enabler. Every compromised credential has its own revocation gap. The worm's blast radius is roughly the sum of those gaps. Fast revocation for any one credential helps. The worm's strategy is to propagate faster than revocation can catch up across the population.

A maintainer's npm token is not a credential. It is a write capability for everyone who runs that install.