Compromised npm packages have become a weekly occurrence. This is being accelerated by AI tools that find and exploit vulnerabilities automatically.
Below you will find minimal configuration to harden your package manager against supply chain attacks. We included instructions for pnpm (recommended), npm and Yarn 1.
1. Enforce minimum package age
Compromised packages are usually discovered within a few hours or days.
We recommend installing only packages older than 4 days ("cooldown").
pnpm
In your pnpm-workspace.yaml:
minimumReleaseAge: 5760
npm
Ensure your team uses npm 11.10.0+.
In your .npmrc:
min-release-age=4
Yarn 1
Yarn 1 does not support package cooldowns.
Schedule a migration to pnpm. Time estimate is 1-4 hours manually, faster with coding agents. Arne can help.
If a project is no longer maintained and cannot be migrated to pnpm, stop making package upgrades. Only upgrade individual packages that have a known vulnerability.
2. Block transitive git dependencies
Some compromised packages included malicious code by way of a git dependency. That git URL will not appear in your package.json, only in your giant lockfile.
We recommend blocking git sources for transitive dependencies.
pnpm
Ensure your team uses pnpm 10.26+.
In your pnpm-workspace.yaml:
blockExoticSubdeps: true
To make an exception (e.g. to use a trusted fork), list the git source as a direct dependency in your package.json.
npm
Ensure your team uses npm 11.10.0+.
In your .npmrc:
allow-git=root
To make an exception (e.g. to use a trusted fork), list the git source as a direct dependency in your package.json.
Yarn 1
Yarn 1 does not support blocking of git dependencies.
Schedule a migration to pnpm. Time estimate is 1-4 hours manually, faster with coding agents. Arne can help.
If a project is no longer maintained and cannot be migrated to pnpm, stop making package upgrades. Only upgrade individual packages that have a known vulnerability.
3. Disable npm scripts
Compromised packages like to use npm script hooks (e.g. postinstall) to run code on your developer PC in order to extract API keys, run malware, etc.
We recommend blocking npm scripts.
pnpm
pnpm 10+ blocks scripts by default. If you want to be explicit, you can configure in pnpm-workspace.yaml:
ignoreScripts: true
You can make exceptions using allowBuilds (pnpm 10.26+) or onlyBuiltDependencies (pnpm 10.0–10.25):
allowBuilds:
esbuild: true
Note
When
ignore-scripts=trueis set on your machine, pnpm will not consider scripts at all so it has no dependency to approve/warn about.
To see the same warnings you get during deploy, execute the following:
rm -rf node_modules && pnpm install --config.ignore-scripts=false
npm
In your .npmrc:
ignore-scripts=true
There is no built-in way to make exceptions. A workaround is
@lavamoat/allow-scripts
Show archive.org snapshot
.
Yarn 1
In your .yarnrc:
ignore-scripts true
There is no built-in way to make exceptions.
4. Reconsider automatic package updates
Your project might use bots like Renovate or Dependabot to automatically upgrade your dependencies. This exposes you to compromised packages.
Reconsider whether automatic upgrades are actually bringing value to your project. If not, disable them.
If yes, make sure to configure bots to respect your package cooldown:
-
Renovate:
minimumReleaseAgeoption Show archive.org snapshot -
Dependabot:
cooldownoptions Show archive.org snapshot
5. Harden or avoid npx
npx (or npm exec) is a convenient way to install and run an npm script in a single command.
npx bypasses any settings your configured for your project. It resolves and runs a package directly from the registry on every invocation. A package compromised between two runs is fetched and executed immediately. This is especially risky for MCP servers and other dev tools commonly launched via npx @scope/tool.
Try to avoid npx, or only use it with packages you know and trust.
If you need to keep using npx, make sure you configure similiar protections globally in your ~/.npmrc:
min-release-age=4
ignore-scripts=true
allow-git=none
For tools you use regularly within a project, install them as dev-dependency so they are covered by the protections configured for your project.
pnpm
Instead of npx cowsay hello we can do:
pnpm add -D cowsay
pnpm exec cowsay hello
npm
Instead of npx cowsay hello we can do:
npm install --save-dev cowsay
npx --no-install cowsay hello
Yarn 1
Instead of npx cowsay hello we can do:
yarn add --dev cowsay
yarn cowsay hello