securitynpmjavascriptbest practices
Recent npm supply chain attacks have forced me to rethink how I handle dependencies. Here's what happened, why it matters, and three simple fixes you can apply today.
May 18, 2026
I used to install npm packages without thinking much about it. pnpm add axios. Done. Move on. It felt no different from importing a built-in module.
Then the supply chain attacks started getting harder to ignore.
In 2025, a self-replicating worm called Shai-Hulud hijacked maintainer accounts across the npm registry and aggressively spread malware from package to package. It wasn’t a one-off incident targeting an obscure library. It spread.
Then in March 2026, Axios got compromised. Axios. One of the most downloaded and trusted packages in the JavaScript ecosystem, something that lives inside thousands of production apps around the world, quietly started delivering malicious code to its users.
If a package that trusted can be weaponized overnight, no repository is safe by default.
Here’s a mindset shift that changed how I think about this: every time you run npm install or pnpm add, you aren’t just adding a utility tool to your project.
You are agreeing to run someone else’s code directly inside your local machine and production environment.
That code can read your environment variables. It can make network requests. It can write to your filesystem. When you install a package with a postinstall script, that script runs automatically, without any confirmation prompt, the moment the download finishes.
Are you absolutely sure you trust every package in your dependency tree? What about their dependencies? Their dependencies’ dependencies?
The honest answer for most of us is: no, not really. And that’s the problem.
Let’s be clear about what’s actually at stake here.
.env file is full of API keys, database credentials, and tokens. A malicious postinstall script can exfiltrate all of it before you ever notice something is wrong.Supply chain attacks are getting smarter. The attackers are patient and methodical. Waiting for our defenses to catch up isn’t an option.
The good news is you don’t need complex enterprise security tooling to meaningfully reduce your exposure. Here are three immediate fixes.
Your lockfile, whether it’s package-lock.json or pnpm-lock.yaml, is a snapshot of the exact package versions your project depends on. It is your first line of defense.
Always commit it. Never .gitignore it.
More importantly, treat lockfile changes in pull requests with the same scrutiny you’d give to regular code changes. If a PR suddenly updates 12 transitive dependencies without explanation, that’s worth asking about.
And in your CI/CD pipeline or deployment scripts, use flags that prevent any automatic resolution of newer versions:
# npmnpm ci
# pnpmpnpm install --frozen-lockfileThese commands fail loudly if the lockfile doesn’t match package.json, which is exactly what you want.
Many supply chain attacks hide their payload inside postinstall, preinstall, or prepare scripts. These run automatically when a package is installed, and most developers never think to check them.
You can disable all lifecycle scripts globally with one line in your .npmrc:
ignore-scripts=trueThis single change blocks the most common vector for malicious code execution during installation. Yes, some legitimate packages use these scripts for things like compiling native bindings. For those specific packages, you can explicitly opt back in. The point is to make allowlisting the default, not denylisting.
This one is my favorite because it requires almost no ongoing effort.
If you use pnpm, you can configure a Release Age Protection feature that creates a buffer between when a new package version is published and when it’s eligible to be installed in your project:
minimumReleaseAge: 10080 # 7 days in minutesWhy seven days? Looking at the history of major npm supply chain attacks from 2018 to 2026, more than half of malicious packages were detected and yanked from the registry within the first week of publication. The security community is watching. Automated scanners are running. The crowd-sourced detection net actually works pretty well, but it takes a little time.
By waiting a week, you let others be the guinea pigs. If something slips through, it’s far less likely to affect you.
None of these fixes are complicated. None require significant time investment. But they do require intentionality, because the default behavior of every package manager is to trust blindly and move fast.
In 2026, blind trust is a vulnerability.
Lock your dependencies. Review what you install. Guard your supply chain. These are small habits, but applied consistently, they make a real difference. Your project’s safety starts here.
Thank you for reading 👋
Comments