On 2 October 2026, npm changed trusted publishing so that a configuration which has never been used to publish expires 48 hours after it is created. The same change stops npm from accepting trusted publishing tokens minted in GitHub Actions issue_comment workflows. On 13 October, Bloom Security published the research behind it, which it calls SlugFest: until a configuration's first publish, npm matched the trusted repository by its owner/repo name, so whoever claimed that name could publish your package.
What's affected
Trusted publishing lets a CI workflow exchange a short-lived OIDC token for permission to publish, with no long-lived npm token. Bloom's write-up explains that a trusted publisher binding stores the repository slug, the workflow filename and an optional environment. GitHub's OIDC token also carries the numeric repository_id and repository_owner_id, which survive renames and transfers. Per Bloom, npm only records those IDs after the first successful publish. Before that, it had nothing to compare them against.
That opens a window when all of these are true:
- A package has a trusted publisher configured.
- That configuration has never published a version.
- The named repository or owner was renamed, transferred or deleted, so the old name can be registered by someone else.
An attacker registers the freed name, adds a workflow with the matching filename, and exchanges the resulting token for a publish credential. No maintainer account is touched. Bloom says it found more than 1,500 packages in this state, with more than 10 million combined downloads, and is not publishing the list. Bloom also says the issue affected npm's GitLab trusted publishing. It reported the flaw to GitHub on 6 June 2026. Neither Bloom nor GitHub reported exploitation in the wild.
GitHub's 2 October changelog describes the fix:
- An unvalidated configuration expires 48 hours after creation and can no longer authorize publishing.
- A configuration becomes validated, and exempt from expiry, after its first successful publish.
- Changing the repository or project identity needs a new trust relationship with a fresh 48-hour window.
- Expired configurations stay visible in settings and can be recreated.
- npm now rejects trusted publishing tokens from
issue_commentevents, alongside the existing block onpull_request_target.
How to check if you're exposed
List the trust configuration for each package you maintain. The npm trust command needs npm 11.15.0 or later:
npm --version
npm trust list your-package --json
For each GitHub binding, confirm the repository still exists at that exact name and belongs to you. A rename leaves a redirect, which hides the problem when you browse to it, so check the API's canonical name:
gh api repos/OWNER/REPO --jq '.full_name, .id, .owner.login'
If the returned full_name differs from the slug in your binding, or the call returns 404, the binding points at a name someone else can claim.
Then check your publishing workflows for the event that is now refused:
grep -rnE 'issue_comment|pull_request_target' .github/workflows/
Any workflow that publishes from either trigger will fail. Neither belongs in a release path, because both can run in response to people outside your team.
What to do now
-
Publish soon after you create a binding. A configuration you create and then leave idle for two days now expires. Create it in the same session as the first release.
-
Fix bindings that point at old names. Revoke the stale configuration and create a new one against the current repository:
npm trust list your-package npm trust revoke your-package --id=TRUST_ID npm trust github your-package --repo your-org/your-repo --file release.yml --allow-publish -
Revoke bindings before deleting or transferring a repository. Bloom's advice is to treat the trust binding as part of the repository's lifecycle.
-
Move publishing to allowed events. GitHub lists
push,releaseandworkflow_dispatch. Pair them with a protected environment that requires approval. -
Watch for publishes you didn't make. Compare npm's version times against your release workflow runs. A version with no matching run is an incident.
Why it matters
Trusted publishing is the right direction, and this flaw doesn't change that. It removes the long-lived npm token that so many 2026 incidents started with. But it moves trust onto an identifier, and names are not identities. Any system that trusts owner/repo without the numeric ID inherits GitHub's rename and reuse rules.
The same lesson applies to anything you configure by name: OIDC trust policies in cloud accounts, workflow references, Go module paths. Check that you pin the immutable identifier where the platform offers one, and clean up trust when the thing you trusted goes away.
Try it yourself
Pick one npm package your team publishes. Run npm trust list on it, resolve the repository with gh api, and look at the last ten versions on the registry. Can you match every version to a workflow run in that repository? If not, write down which step of your release process would have caught a publish from somewhere else.
Click through it at your own pace, or open it full screen.
Quick check
Which trusted publishing configurations were exposed to SlugFest?
Configurations that had never been used to publish, and whose named repository or owner had been renamed, transferred or deleted so the old name could be claimed.
What happens to a configuration you create but don't use for three days?
It expires after 48 hours and can no longer authorize publishing. You can recreate it to start a new 48-hour window.
Which GitHub Actions events can no longer mint npm trusted publishing tokens?
issue_comment and pull_request_target. Use push, release or workflow_dispatch instead.