The npm Worm That Harvested Developer Secrets: Five Controls for Coding Agent Teams
By Declan Osei · October 21, 2026
Category: red-teaming-offensive-research
A registry worm turned one stolen publishing token into hundreds of compromised packages. Coding agents run installs with a developer's credentials, which makes the response worth copying.
Key takeaways
The problem A registry worm used one stolen publishing credential to republish itself across hundreds of packages, and coding agents run installs holding whatever secrets the inherited shell contained.
Core insight The campaign needed three things to function: a credential with publishing rights, a place to put collected data, and secrets readable from the environment. Removing any one breaks the chain.
Practical outcome Make the lockfile a human approval gate, run installs without credentials, keep publishing rights away from agents, allowlist egress, and stop agents creating public repositories.
Most malicious packages steal something and stop there. One campaign in September 2025 did something more awkward: it used what it stole to publish itself into further packages, then repeated. CISA's alert on the campaign, which tracks it under the name Shai-Hulud, describes a compromise reaching more than 500 packages.
If your team ships AI coding agents, treat this as a story about your environment rather than a story about JavaScript. An agent runs installs, executes build tooling and invokes scripts, and it does all of that holding whatever credentials happen to be present in the shell it inherited. Below: what the campaign did at a defender's altitude, what the registry's owner changed afterwards, and five controls for the seam where agents meet package managers. No payload details appear here.
What the Campaign Actually Did
CISA's alert characterises a self-replicating worm inside the npm ecosystem, and the summary breaks into three mechanics that matter for defence.
- It collected credentials. The malware searched infected environments for secrets, including personal access tokens for GitHub alongside cloud keys for AWS, for GCP and for Azure. Anything the process could read was in scope.
- It moved them out through two routes. Stolen material went to an endpoint the attacker controlled, and was additionally uploaded into a public GitHub repository carrying the campaign's name. Two exits, one of which looked like ordinary developer activity.
- It republished itself. Propagation worked by presenting the stolen credential to the registry as though it were the developer, then writing itself into further packages wherever those publishing rights extended.
That is as far into the mechanism as this article goes, and the defensive content needs no more. The campaign lived entirely off secrets already resident on workstations and continuous integration hosts, and the credential carrying publishing rights is what converted theft into propagation.
A later wave followed. The Register reported a return of the campaign under a second version number, with secrets exposed across a large number of public repositories, and relayed a warning from Wiz researchers that execution during a package's pre-install phase widens exposure across build and runtime environments. Those are press accounts of vendor research, so verify counts against primary advisories before repeating any of them in a report of your own.
Why a Package Registry Is Such Good Ground
Three properties of a package ecosystem combine badly. Installing a package can execute arbitrary scripts on the machine doing the installing. A maintainer's single credential frequently covers publishing rights across many packages at once. And continuous integration hosts hold cloud keys, because deployment is what they exist to do. Combine the three and a single theft reaches a very long way downstream.
Coding agents sharpen both edges. They install and execute at a pace that outruns per-dependency human review, which is largely the point of adopting them. And they operate inside environments dense with secrets: exported variables, credential helper caches, configuration files, cloud SDK profiles. Our article on the quiet ways a coding agent leaks a key covers that second property in detail, and the structural view sits in where trust breaks down in agentic pipelines.
What the Registry's Owner Changed
GitHub, which operates the registry, announced a set of changes afterwards. The reported plan covers a second authentication factor when publishing from a workstation, granular credentials that lapse after a week, wider adoption of trusted publishing, an end to both the older classic credentials and one-time-password second factors, a default configuration in which publishing refuses credentials outright, and trusted publishing support across additional providers.
On why it would not wait for voluntary adoption, the company is reported as saying that "attackers have shown us that they are not waiting". That is a secondary account of a company post, so read the original before quoting it anywhere consequential.
Read that list as a template rather than as somebody else's problem. Every item on it reduces the value of a stolen credential, and every item has an analogue inside your own estate.
Control 1: Make the Lockfile the Approval Gate
The advisory asked teams to review their dependency tree down through nested entries using lockfiles, and to hold versions at known-good points. For agent work, promote that file to the artefact carrying a human signature. An agent may propose a new dependency and make its case in the pull request; a person approves the resulting change to the file. Disable unattended installation of anything new, because an install nobody watched is a decision nobody made.
Control 2: Starve the Install Step of Credentials
Resolving and compiling dependencies does not require access to your cloud account. Run installs inside a container holding no credentials and reaching no network destination outside the registry itself. Keep tokens out of exported variables where the tooling permits an alternative, since an exported variable is readable by every process in the tree. Our guide to isolating agent tool calls sets out the pattern.
Control 3: Keep Publishing Rights Away From Agents Entirely
Among the advisory's recommendations were credential rotation for developers and phishing-resistant second factors on both the registry and the code host. Add a rule of your own on top: no agent holds a durable publishing credential, ever. Publication belongs to a person or to a pipeline, through trusted publishing or a credential minted for that one job and discarded afterwards.
Control 4: Close the Exit, Because It Is the Last Link
The advisory named the specific webhook host seen in this campaign as a block candidate, and suggested a pass over firewall records for anything unusual. Generalise rather than implementing that literally, because the next campaign uses a different host. Keep an allowlist of destinations reachable from build and agent environments, then raise an alert the moment an unlisted one appears. Without a route outward a worm is just a failed install, and this is the cheapest intervention point in the whole sequence.
Control 5: Harden the Account Behind the Token
The advisory also covered removing unneeded applications and integrations, auditing stored secrets and webhook configurations, and switching on protected branches, secret scanning and automated dependency updates. Each of those narrows what a stolen credential is able to change, and each produces signal as a side effect. A pipeline identity opening a new public repository should reach a human within minutes, because that capability was one of this campaign's two exfiltration routes.
Three Rules to Add to Your Coding Agent Policy
These three take an afternoon to implement and map directly onto what the campaign required to function.
- No agent publishes packages. Publication belongs to a person or to a pipeline identity scoped to that one job.
- No agent creates a public repository under a company or developer account without approval. A public repository was one of the two exfiltration routes here.
- No agent session inherits the whole environment. Pass an explicit list of the variables a task requires, rather than filtering out the ones you remembered to exclude.
Three ingredients made this work: publishing rights on a stolen credential, somewhere to deposit what was collected, and secrets left readable where the process ran. Take away any single one and completing the sequence becomes substantially harder. Take away two and the thing stops replicating altogether.
What Happened on the Python Side
Nothing above is specific to JavaScript, and a team running coding agents against Python repositories has the same seam with a different registry behind it. PyPI took a route worth understanding, because it produced a detection signal rather than only a hardening measure. Its own year in review, published on 31 December 2025, reports more than 50,000 projects publishing through trusted publishing, more than 20% of all file uploads arriving that way, and 17% of uploads over the year carrying a provenance attestation. The same post records more than 2,000 malware reports processed during the year.
The useful part is what that provenance did during an actual compromise. In the Ultralytics incident of December 2024, attackers reached the project's build workflow and later an unrevoked publishing token, and PyPI's own write-up explains how the trail was read: sigstore transparency logs and publish attestations showed the first bad releases coming through the existing workflow, while there were no corresponding source repository activity or PyPI publish attestations for the second round of releases. The absence of provenance is what made the second wave visible.
That is a control an agent team can build, and it belongs beside the five above. For every dependency your agents are allowed to add, record whether its releases arrive with provenance and from which workflow. Then alert on the change: a project that has been publishing with attestations and suddenly ships a release without one is showing you the exact signal the Ultralytics analysis describes. The check costs a field in your dependency record and a comparison in your pipeline, and it needs no cooperation from anybody.
Do Not Gate on Attestations Yet
The obvious next step is the one we would not take. Requiring provenance on every dependency before an agent may install it sounds like the clean version of this control, and plenty of supply-chain tooling will happily enforce it. At current adoption that rule does not improve your security posture, it just removes most of the ecosystem from your build. PyPI's own figure of 17% of uploads carrying an attestation, from its December 2025 review, is the thing to weigh here: the large majority of what you depend on has no provenance to check, so a presence requirement fails open in the only way that matters, by pushing your team to turn it off.
Our call is to gate on change rather than on presence. A per-package comparison works today at any adoption level, because it only asks whether this project is behaving like itself. A presence requirement needs the ecosystem to have moved first. The same reasoning applies on the npm side: the registry's announced direction, short-lived granular credentials and trusted publishing by default, is the right direction, and it is a plan rather than a current state, so build your controls against what is published today.
You can measure when this stops being true, which is the best kind of test. Take your own dependency list rather than the registry average, and count how many of those projects publish with provenance. When the answer for new additions is most of them, invert the rule: require provenance for anything an agent adds from that point, and treat the unattested long tail as a documented exception list with owners against it. Until then a presence gate is a policy you will suspend inside a fortnight, and a suspended control is worse than an honest absence of one, because somebody has already written it down as done.
When to Escalate
Where a workstation or a pipeline host installed a compromised package, consider every credential readable from that machine to be exposed. Rotate the lot, examine recent publications and newly created repositories under the related accounts, and open an incident rather than a cleanup ticket. Past a small project that means qualified responders. Our guide to incident response for agentic systems sets out which steps differ when an agent is involved.
The campaign itself was a registry story. The part worth carrying into your own architecture is narrower and more durable: know where secrets sit, and know exactly which processes can read them.
Questions about registry worms and coding agents
What was this campaign, briefly?
Self-replicating malware inside the npm registry. The CISA advisory sets out credential harvesting plus automatic spread, and puts the compromise at more than 500 packages.
Why does a package worm matter to an AI agent team?
An agent executes installs and build scripts while holding developer credentials, and typically reviews each dependency less closely than a person would. A hostile package reads whatever that environment leaves readable.
Is pinning versions sufficient on its own?
No. Pinning removes surprise updates, which is valuable and insufficient. You also need credential-free installs, publishing rights kept away from agents, and an egress allowlist.
Which sources should a team trust for current status?
Primary advisories from CISA, the code host and the registry itself. Press summaries lag, and the counts between them diverge, so cite the advisory rather than the coverage.
Frequently Asked Questions
What was this campaign, briefly?
Self-replicating malware inside the npm registry. The CISA advisory sets out credential harvesting plus automatic spread, and puts the compromise at more than 500 packages.
Why does a package worm matter to an AI agent team?
An agent executes installs and build scripts while holding developer credentials, and typically reviews each dependency less closely than a person would. A hostile package reads whatever that environment leaves readable.
Is pinning versions sufficient on its own?
No. Pinning removes surprise updates, which is valuable and insufficient. You also need credential-free installs, publishing rights kept away from agents, an egress allowlist, and a check for whether a dependency has stopped publishing with the provenance it used to carry.
Which sources should a team trust for current status?
Primary advisories from CISA, the code host and the registry itself. Press summaries lag and their counts diverge, so cite the advisory rather than the coverage.