A maintainer of a package with millions of weekly downloads opens an email that looks like npm support, types in their credentials, and the next day their package ships a version that steals tokens from everyone who installs it. That’s not a movie plot — it’s the pattern that repeats quarter after quarter, with phishing campaigns getting sharper every time. And you don’t need to own a famous package to get hurt: one dependency that auto-updates in your CI is enough.
GitHub has spent months shipping changes across npm and Actions to break these attack chains, and this week they published a roundup of everything already in production. The good news: most of these defenses are free and turned on through configuration, not new infrastructure.
The bad news: many are opt-in, and if nobody enables them in your project, they don’t exist. So instead of summarizing the news, here’s what you can apply today, point by point, with links to the official docs for each measure. If you only have ten minutes, go straight to item 1.

How your package gets hijacked: the attack chain in 30 seconds
The pattern repeats: an attacker compromises a maintainer’s account or sneaks code into a CI workflow, exfiltrates credentials from there (npm tokens, pipeline secrets), and uses them to publish malicious versions that spread to hundreds of projects before anyone reacts. GitHub splits this into three phases: initial compromise, credential exfiltration, and propagation. Each measure below breaks one of those phases.
1. Publish packages without tokens: trusted publishing with OIDC
The best effort-to-impact ratio: remove the long-lived npm token sitting in your CI secrets. With trusted publishing, your workflow authenticates to npm via OIDC: short-lived, signed tokens valid only for that specific workflow. There’s nothing to steal.
It works with GitHub Actions, GitLab CI/CD and, since April, CircleCI. You need npm CLI 11.5.1+ and Node 22.14.0+. Two setup steps: on npmjs.com (package Settings → Trusted Publisher) you declare the org, repo and workflow filename; and in the workflow you add the OIDC permission:
permissions:
id-token: write # required for OIDC
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish # no NODE_AUTH_TOKEN, no secrets
Two details worth knowing from the docs: publishing this way from a public repo generates provenance automatically (cryptographic proof of where and how the package was built), and once migrated you should go to Settings → Publishing access and select “Require two-factor authentication and disallow tokens” so classic tokens stop working for that package. Also note: only one trusted publisher per package.
2. Staged publishing: CI credentials alone shouldn’t be enough to publish
If you want to go further, staged publishing (opt-in, available since May) decouples “having credentials” from “publishing”: the package stays in staging until a maintainer approves it with 2FA from the CLI or npmjs.com. The combination npm’s own docs recommend is configuring the trusted publisher with only the npm stage publish permission (no npm publish), so everything coming out of CI needs human approval before going public. If your pipeline gets compromised, the attacker ends up one step away from nothing.
3. npm v12: install scripts are off by default
This malware’s favorite propagation path isn’t package code at runtime — it’s preinstall/postinstall scripts: they run the moment you type npm install, before anyone has read anything. With npm v12, three defaults change:
allowScriptsdefaults to off: no dependency runs install scripts unless you explicitly allow it. This includes implicitnode-gypbuilds.--allow-gitdefaults tonone: Git dependencies won’t resolve unless you opt in.--allow-remotedefaults tonone: same for remote-URL tarballs.
To prepare, upgrade to npm 11.16.0 or later and review the warnings from your normal install. The workflow is:
# See which packages have scripts pending approval
npm approve-scripts --allow-scripts-pending
# Approve the ones you trust / deny the rest
npm approve-scripts
npm deny-scripts
The resulting allowlist is written to package.json and committed, so the decision is auditable in code review. If you already install with --ignore-scripts in CI out of habit, v12 simply makes that caution the default for everyone.
4. GitHub Actions: shut down pwn requests
On the CI side, June’s changes target the “pwn request” pattern: a workflow on pull_request_target that checks out fork code and runs it with elevated permissions. Three things to review:
- actions/checkout no longer checks out fork code by default in commonly exploited triggers; you have to explicitly opt out to get the old unsafe behavior back. If your workflow relied on that, it needs a review: you were almost certainly exposed.
- Workflow execution policies: at repo, org or enterprise level you can limit who triggers workflows and which trigger types are allowed. If none of your workflows need
pull_request_target, turn it off in one shot. - Read-only Actions cache for untrusted triggers: a low-privilege workflow can no longer poison the cache it shares with your release workflow.
And the evergreen basics, still free: declare minimal permissions: in every workflow (the GITHUB_TOKEN is born with too many if you say nothing), pin actions by commit SHA instead of tags, and review who can modify .github/workflows/. It’s all in the official Actions hardening guide.
5. Dependabot: the three-day cooldown is now the default
Since July 14, Dependabot waits at least three days after a version is published before opening an update PR. The logic: these attacks depend on speed — if a malicious release is detected and pulled within 48 hours, a 3-day cooldown saves you without lifting a finger. Security updates still open immediately.
If you want a different value (more days, or per-bump-type settings), use the cooldown option in dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 5
semver-major-days: 10
6. For when something goes wrong
Two response tools worth knowing before you need them: the credential revocation API now covers OAuth and GitHub App tokens too (it launched in 2025 for PATs only), and enterprise admins get self-service revocation of all credentials for a user. On top of that, npm puts high-impact accounts into read-only mode for 72 hours when the email changes or a 2FA recovery code is used — exactly the window phishing campaigns abuse to publish before the maintainer reacts.
And if you run an organization, take a look at the Actions network firewall (technical preview): it logs all outbound traffic from your workflows, which is exactly where an exfiltration shows up.
The checklist, condensed
- Migrate publishing to trusted publishing (OIDC) and disallow tokens on the package.
- Enable staged publishing if the package is critical.
- Upgrade to npm 11.16+, review warnings and generate the script allowlist with
npm approve-scripts. - Review workflows using
pull_request_target; apply execution policies and minimalpermissions:. - Configure
cooldownindependabot.ymlif the 3-day default doesn’t fit. - Save the credential revocation links in your incident runbook.
None of this costs money or requires new tooling: it’s configuration already available on your account. The difference between making the news as a victim and never noticing there was an attempt is, literally, an afternoon of configuration. The next supply chain attack won’t wait for you to schedule it into a sprint, so I’d start with item 1 this very week. If you want to reinforce other security layers, also check out how to block AI bots in Cloudflare without losing SEO and how to protect web apps for free with SafeLine WAF.
Frequently asked questions
Does trusted publishing work with self-hosted runners?
No. For now it only works with GitHub-hosted runners, GitLab.com shared runners and CircleCI cloud, per the npm docs. Self-hosted support is planned but has no date.
Will npm v12 break my builds?
If your dependencies use install scripts (esbuild, sharp, bcrypt and other packages with native binaries), yes: you’ll need to approve them with npm approve-scripts. That’s why it’s worth upgrading to 11.16+ now, reading the warnings and committing the allowlist before you jump to v12.
Does the Dependabot cooldown delay security patches?
No. The three-day cooldown only applies to version updates. Security updates open immediately, same as before.



What do you think?
Leave your opinion, question or suggestion. Comments are synced with GitHub Discussions .