Cover Image for Major npm incident: how to protect projects from supply-chain attacks
[npm][JavaScript][Security][SupplyChain][Node.js]
August 12, 2025

Major npm incident: how to protect projects from supply-chain attacks

In July 2025, the npm ecosystem faced one of the largest supply-chain attacks in its history. Attackers gained access to maintainers' accounts of popular packages and published malicious versions that millions of developers installed.

What happened

Compromised packages:

How the attack unfolded:

  1. Phishing via a typosquatted domain npnjs.com → maintainers entered their credentials.
  2. Attackers gained access and published malicious builds directly to npm.
  3. The malicious code was disguised as a postinstall script that downloaded a DLL or opened a WebSocket backdoor.
  4. Releases bypassed GitHub repositories — typical commit monitoring did not help.

Total reach — over 180M downloads per week.

Sources:


How to defend against similar attacks

1. Pin exact versions

In package.json:

"eslint-config-prettier": "10.1.0"

Instead of:

"eslint-config-prettier": "^10.1.0"

Command for npm to record exact versions by default for future installs:

npm config set save-exact true

And in .npmrc (in the repository) you can additionally enforce this:

save-exact=true

How to quickly pin versions across the whole project

If your package.json already uses ^ or ~, you can quickly remove them and pin exact numbers.

For npm:

npx npm-check-updates -f "/.*/" --removeRange --upgrade
  • npm-check-updates is a utility for managing dependency versions.
  • The --removeRange flag removes ^ and ~ from all entries.

npm-check-updates does not read package-lock.json. It operates only on your package.json, replacing dependency versions with the latest from npm (respecting filters and constraints you provide). If you keep your package-lock.json in the repository, it is safer to pin versions directly from the lockfile. Here's a working Node.js script that takes exact versions from package-lock.json and writes them to package.json without ^ and ~.

2. Use lockfiles

Commit package-lock.json to your repository. This pins all dependencies, including transitive ones.

3. Enable dependency audits

  • npm audit

4. Minimize postinstall scripts

Avoid dependencies that execute code during installation unless absolutely necessary.

5. Enable 2FA for your npm account

This makes account takeover harder even if a password leaks.

6. Verify emails and domains

Do not follow links from “official” emails until you verify the address. The difference between npmjs.com and npnjs.com is just one letter.


Conclusion

Supply-chain attacks are becoming more frequent, and even a popular package with a long history can be compromised. Minimizing automatic updates, controlling dependencies, and staying vigilant are key steps to protecting your projects.

Read next

Join our community