News

An npm Loophole Left 1,500 Packages Open to Hijack Through Dead Repo Names, Then GitHub Went Quiet for Months

Bloom Security found npm Trusted Publishing trusted GitHub repository names instead of permanent IDs, letting anyone who claimed an abandoned name publish to more than 1,500 packages. GitHub has now fixed it after months without replying.

An npm Loophole Left 1,500 Packages Open to Hijack Through Dead Repo Names, Then GitHub Went Quiet for Months

For more than a year, npm's credential-free publishing system had a quiet flaw: it trusted the name of a GitHub repository, not the repository itself. When a maintainer renamed or deleted a repo, anyone who grabbed the old name could push new versions of their package. No stolen password, no leaked token.

Researchers at Bloom Security, who call the work SlugFest, counted more than 1,500 exposed npm packages with more than 10 million downloads in total and over a million a month. GitHub, which owns npm, says it has shipped fixes. Nobody knows whether anyone abused the hole before that.

How the lock worked

Trusted Publishing, generally available since July 2025, was meant to kill long-lived publishing tokens. A maintainer tells npm which GitHub repository and workflow may publish. GitHub hands that workflow a signed identity token, and npm trades it for a credential that expires within minutes.

The problem was what npm wrote down. It stored the repository as an owner/repo string, which GitHub calls a slug. GitHub also gives every repo and owner a permanent numeric ID that survives renames and transfers, and it puts those IDs in the token. npm just did not check them until the first successful publish from that repo.

So once a bound repo was renamed, transferred or deleted, or its owner account renamed or deleted, the slug was up for grabs. An attacker could create a repo with that name, add a workflow with the matching filename and receive a publish credential for a package they never owned. A malicious version could ship a postinstall script, code npm runs automatically on every machine that installs the update.

"It is like a lock that checks the address on the door rather than who owns the house: when the owner moves out, whoever moves in gets the key," said Bloom researcher Golan Myers.

Bloom proved it on its own package, publishing from a second organization that had claimed the freed slug. The rogue release even carried a valid provenance attestation, naming the attacker's repository. Some systems only check that an attestation exists, not what it says.

There was a partial safety net. GitHub retires repo names that saw more than 100 clones in the week before an owner account is renamed or deleted. "Everything we considered vulnerable was pertaining to claimable slugs," Myers said. The same weakness also affected npm's GitLab integration. PyPI already stores immutable IDs.

Months without an answer

Bloom reported the flaw through GitHub's HackerOne bug bounty on June 6, and it was validated on June 9. According to Bloom, the last reply came July 8. Six follow-ups after that, including a request for HackerOne to mediate, went unanswered.

Before going public, Bloom retested and found the hole closed. npm then confirmed the fix and apologized for the slow communication. GitHub announced the change on October 2.

"Unvalidated Trusted Publishing configurations now expire 48 hours after creation and can no longer authorize publishing," GitHub said in a statement, adding that this "limits the risk of trusting a repository or project name that changes ownership." A config's first successful publish now binds it to the repo's "immutable identity," and npm rejects trusted publishing tokens from GitHub Actions issue_comment events. GitHub did not say whether configurations that published before the change were bound retroactively. Bloom is not releasing the names of affected packages.

Agents make it worse

npm is already a favorite target, as the Trinitite worm's hit on a TanStack Query codegen package showed. AI coding agents widen the blast radius. From telemetry across dozens of organizations, Bloom estimates agents on a typical endpoint install about 70 packages a month, and close to 350 on developer machines, not counting dependencies and usually without review.

Bloom says it caught and blocked one customer's agent trying to install an affected package. Myers warned of a cascade, where an agent on a popular maintainer's laptop pulls one compromised package and that machine then pushes malicious code into the popular one.

Bloom tells maintainers to run npm trust list and confirm every bound repo still resolves to one they control, to delete and re-create bindings that point at renamed or transferred repos, and to remove a binding before deleting a repo. Users can check that a package's provenance attestation names the repository the project is actually supposed to come from.

Some offers on this page may be paid placements or contain affiliate links.

Cyberpresso: daily cyber & AI brief

Free daily newsletter, read in 5 minutes.

Subscribe free