Every supply chain compromise has a moment where trust quietly changes hands. Ours happened in a system most teams never look at: the link between a GitHub repository and an npm package.
Security researchers at Bloom Security named the flaw SlugFest, and the metaphor their lead researcher Golan Myers used is the one that stuck: it is like a lock that checks the address on the door instead of who owns the house. When the owner moves out, whoever moves in gets the key.
Here is the uncomfortable part. Nobody had to steal a credential. The only precondition was a repository rename.
What npm trusted publishing was supposed to fix
In July 2025 npm shipped trusted publishing, and it was a genuinely good idea. Instead of storing long-lived npm tokens on a build server (and leaking them through logs, caches, and compromised CI runners), a GitHub Actions workflow requests a short-lived OpenID Connect token, presents it to npm, and gets a publish credential in return. Releases come with provenance attestations, so anyone can trace where a package was built.
The design removes a whole class of secret-leak incidents. It also introduced a subtle failure mode, because of a single field.
npm bound the trust relationship to the repository's human-readable address: owner/repository. GitHub's OIDC tokens also carry the repository's permanent numeric ID, which survives renames and transfers. npm, until a package's first successful publish, ignored that number entirely and matched on the name.
Names move. They are freed when a maintainer renames, transfers, or deletes a repository, and once a name is free, anyone can claim it.
How SlugFest actually works
- A maintainer configures trusted publishing for a package but has not yet published a version from that repository, so the binding is still name-based.
- The maintainer renames, transfers, or deletes the repository. The old
owner/repositoryslug becomes available. - An attacker creates a new repository at exactly that freed slug and runs a publish workflow. GitHub mints an OIDC token carrying the reclaimed name with a brand-new repository ID.
- npm checks the name, finds it matches the stored configuration, and hands over a publish token for a package the attacker does not own.
From there the attacker ships a malicious version under the legitimate package name. Download counts, reputation, and every dependent project come along for the ride. Bloom's registry-wide scan found more than 1,500 vulnerable packages with over 10 million cumulative downloads and more than a million downloads a month.
Bloom reported it to GitHub on June 6, 2026. It was triaged on June 9. Then the trail went cold through July, and the fix only landed after the researchers prepared to publish, more than 120 days later. GitHub and npm have since confirmed the remediation and apologized for the slow communication.
The fix is exactly the right shape: newly created trusted publisher configurations now expire after 48 hours unless they complete a first successful publish, which validates the binding to the repository's immutable identity. Changing a repository or project identity requires a fresh trust relationship and a fresh 48-hour window. npm also stopped accepting trusted publishing tokens from issue_comment events, on top of the existing pull_request_target restriction.
Then the agents showed up
If SlugFest were only about human developers running npm install, it would already be serious. It is the second half of Bloom's research that should change how you think about your build pipeline.
Bloom studied millions of AI agents across dozens of organizations. The average endpoint receives roughly 70 packages installed by AI agents every month. Developer machines get close to 350. Individual agents install between 0.3 and 1.2 packages a day; on developer machines, direct installs run two to three a day, on top of everything those packages pull in. A typical endpoint runs three agents across command-line interfaces, IDEs, extensions, and assistants. Developer machines run five or more.
Now line that up with how an agent behaves. It resolves dependencies itself, it prefers the latest version, and it rarely asks where a package came from. A malicious release published through a reclaimed slug gets installed and its postinstall hook executes with no human in the loop at all.
Bloom caught this in production, blocking one customer's AI agent as it tried to install an affected package.
The attacker's payoff is a cascade. Compromise one package. An AI agent on a popular maintainer's machine pulls it in. That endpoint becomes the launchpad for a malicious version of the maintainer's own package, and every consumer of it is exposed. That is the Shai-Hulud pattern with an accelerant.
Provenance is not a checkbox
There is a second blind spot that deserves attention, because it is the sort of thing that survives a compliance audit.
A malicious release can carry a provenance attestation generated by the attacker's own repository. Some verification systems check only that an attestation exists, not that its source matches the maintainer the package was supposed to come from. The signature is real. The signer is wrong. This is where supply chain control has to move from "was it signed" to "was it signed by the identity I trust, and is that identity still the same object I trusted last month".
Worth noting that PyPI's Warehouse already stores immutable identifiers, which is what Bloom recommended for npm. The npm GitLab trusted publishing path had the same weakness.
What this means for aratech clients
Most organizations cannot fix a registry, and they are not going to stop using npm. What they can do is stop treating package arrival as an invisible event.
- Audit trusted publisher bindings today. Maintainers with publishing rights should run
npm trust listand confirm every configured slug still resolves to a repository they control. Any binding pointing at a renamed, transferred, or deleted repository needs to be deleted and recreated. - Treat repository hygiene as security work. Deleting a trust binding whenever a repo is renamed, transferred, or deleted should be a written step in the runbook, not a good intention.
- Put a gate in front of the install path. A supply chain firewall that decides what enters the environment is the control that actually scales, because it does not depend on a human reviewing a lockfile diff at 6pm.
- Extend the inventory past packages. Audit extensions, plugins, MCP servers, skills, and applications. Bloom found the same pattern in August in Open VSX and the VS Code Marketplace: 87 extension packs in one and 677 in the other referenced extensions that did not exist, free for anyone to claim.
- Write the agent policy. Which agents may install software, from which registries, with what approval, and how you would know afterwards.
The pattern worth remembering
Two years of AI-related security stories keep circling the same lesson, and SlugFest states it cleanly: the binding between a human-readable name and the thing it identifies is the most important invariant in any trust system. Break it with a routine rename and the guarantee collapses, silently, with no alert and no failed login.
Your agents are now infrastructure. They install code at machine speed, into environments you are accountable for. The name on the door stopped being proof of who is inside. Verify identity, not spelling, and do it automatically.