How to Protect Yourself from npm Supply Chain Attacks
Another day, another npm supply chain attack! In the fast-moving world of JavaScript, speed often overrides scrutiny. After reading Andrew Nesbitt’s recent blog post, it makes a compelling case that this wouldn’t have happened if npm didn’t have bad unsafe defaults.
Here’s a Cheatsheet to secure your npm environment immediately.
1. The Safest Option: Don’t Use NPM
The absolute best practice for npm security is to switch out of npm entirely if your project allows it. Other package managers (like pnpm, yarn, deno, or bun) are built with safer, stricter defaults out of the box. If you can migrate, do it. If you are stuck with npm, follow the steps below.
2. Disable Auto-Scripts (Stop immediate execution)
By default, npm runs preinstall and postinstall scripts. This is the #1 vector for malware to exfiltrate .env secrets or install backdoors the second you run an install command. One way to eliminate this threat is to turn it off globally.
npm config set ignore-scripts true
(Note: Only enable scripts on a per-package basis for dependencies you explicitly trust).
While you’re at it, you should remove unencrypted secrets from .env files anyway. Try something like varlock.
3. Enforce Strict Lockfiles in CI/CD (Stop floating versions)
npm ci is meant to be a safer replacement for npm install. Default installations allow “floating” versions (^ or ~), meaning you might silently pull a hijacked package update during a build. Never use npm install in your build pipelines. Always use npm ci to enforce the exact versions locked in package-lock.json.
npm ci
4. Secure your npx Usage (Stop silent typosquatting)
npx is dangerously convenient because it automatically downloads and executes a package if it isn’t found locally. This is a massive risk for typosquatting.
The Problem: If you type
npx tailwindscss(typo), npx will download and execute whatever malicious package is sitting at that name.The Best Practice: Always use the
--no-installflag if you expect the package to already be in your project. If you must download a one-off tool, use the--packageflag to be explicit.
# Only run if already installed (safe)
npx --no-install tailwindcss
# Be explicit about what you are downloading
npx --package=cowsay cowsay "Hello"
5. Implement Dependency Cooldowns (Stop day-zero attacks)
If a popular package is compromised, pulling the latest version immediately is dangerous. Configure a “min-release-age” buffer (available since npm v11.10.0) to ensure you only install versions that have survived 48 hours without community outcry.
npm config set min-release-age 2880 # 48 hours in minutes
6. Audit Transitive Trust (Map the deep tree)
Supply chain attacks usually hide 4-5 levels deep in your dependency tree. Go beyond npm audit and use robust metadata tools like Ecosyste.ms to evaluate the actual maintenance health of your entire tree.
AI Coding Agents Love npm install
If you use AI coding assistants (like Claude, Cursor, or Copilot), be aware that their default behavior is almost always to blindly run npm install when adding a new package. You must explicitly instruct your agents to follow these security practices. (Even then, agents don’t always follow these instructions, especially when their context windows become bloated.)
Add a section to your project’s agent instructions (AGENTS.md, CLAUDE.md, or .cursorrules) file to force the AI to avoid npm if possible, or at least use safer commands like npm ci and --ignore-scripts.
Even if you don’t use AI coding agents, many of your dependencies certainly DO.
Context: Why This Matters Now
Recent supply chain incidents prove that attackers are targeting our tools, not just our code. Relying on default settings prioritizes convenience over verification, leaving the door wide open for automated, wide-scale compromises. Securing your package manager is no longer optional; it is fundamental infrastructure security.
This was written by Daniel Lyons.
If you'd like to support him, please consider buying him a coffee so he can create more content like this.