npm Supply Chain Security: A 2026 Checklist for Small Teams
A practical npm supply chain security checklist for 2026: lockfiles, npm v12 script approvals, release cooldowns, provenance and tokens, for small teams.
Working through a similar problem? Tell us what you want to build.
Legacies is an independent digital product brand based in Bucharest, Romania, founded by Horia Stan and Alexandru Tălnaci, and we treat every npm install on a client project as a trust decision. You are receiving code written by strangers and, very often, running it on your laptop or in your production build.
So how do you keep npm dependencies safe in 2026? Commit and enforce your lockfile, let npm v12 block install scripts and approve only the few that need to run, wait a few days before adopting brand new versions, and lock down the tokens that can publish or deploy. None of it is expensive. It just has to be consistent.
Quick answer
- Make installs repeatable. Commit
package-lock.jsonand usenpm ciin CI so every build installs exactly what you reviewed. - Keep install scripts off by default. npm v12 already does this. Approve only packages that truly need a script, and pin approvals to versions.
- Add a cooldown. Set
min-release-agein.npmrcso fresh, possibly poisoned releases do not land in your tree on day one. - Protect credentials. Use trusted publishing (OIDC) instead of long-lived tokens, and keep CI secrets away from steps that install dependencies.
Why npm supply chain attacks keep working
The pattern has not changed much. An attacker gets hold of a maintainer's credentials, publishes a malicious version of a popular package, and the payload runs during install on thousands of machines. It steals more tokens. Then it publishes more malware with them.
In September 2025, CISA warned about the Shai-Hulud worm, which compromised over 500 npm packages and harvested GitHub tokens and cloud keys from developer environments. In August 2026, the ChainDrop worm poisoned 444 packages and 2,212 versions in under four hours, including keyv, flat-cache and file-entry-cache, each with well over 100 million weekly downloads.
ChainDrop is worth a closer look. The attackers used stolen GitHub credentials to push commits that triggered the projects' own release workflows. The malicious versions shipped with valid provenance attestations. A green provenance badge did not mean the code was safe. It only meant the build came from the expected repository, which the attacker controlled at that moment.
That is the honest picture. You cannot outsource trust to any single check. You can stack a few cheap ones so that one failure does not become a breach.
The checklist we use on client projects
These are the rules we have applied since before the recent wave of worms. We have updated them for npm v12 and the controls GitHub added in 2026.
1. Check who maintains the package
A package with a single maintainer, no recent activity and a critical role in your app is a risk signal. We prefer packages with several active contributors, or ones backed by a company or foundation. We also look at the repository: are releases tagged, is there a security policy, does the published code match the source?
2. Count the transitive dependencies
A small package that pulls in 40 indirect dependencies adds 40 more packages you now have to trust. Run npm ls package-name --all or look at the dependency graph before adding it. Sometimes writing 20 lines yourself beats installing a package for one function. Sometimes it does not, and that is fine. The point is to decide on purpose.
3. Commit the lockfile and use npm ci
package-lock.json always goes into git. Without it, each install can resolve slightly different transitive versions, including one that was compromised an hour ago. In CI we use npm ci, which installs exactly what the lockfile says and fails if package.json and the lockfile disagree. We also review lockfile diffs in pull requests. A surprise new dependency in a lockfile is worth a question.
4. Let install scripts stay off
Since npm v12 (released July 8, 2026), dependency preinstall, install and postinstall scripts no longer run unless you allow them, and git and remote tarball dependencies are blocked by default too. Most install-time worms, ChainDrop included, depended on exactly those scripts. We approve the handful of packages that genuinely need one, such as native modules, and commit the allowlist. We cover the details in our guide to npm v12 install scripts.
5. Add a release cooldown
Most malicious versions are caught within hours or days. A cooldown means you simply do not install anything that new. npm supports this with the min-release-age setting, measured in days:
; .npmrc
min-release-age=3
GitHub made the same idea a default for Dependabot in July 2026, with a three-day cooldown before version updates. For urgent security patches you can override it on purpose. That is the right way round.
6. Treat npm audit as a first filter
We run npm audit on every significant install and treat "high" and "critical" as blockers, not suggestions. But audit only knows about published advisories. It will not catch a fresh malicious release. We also run npm audit signatures, which verifies registry signatures and provenance attestations for what you installed. Useful, but remember the ChainDrop lesson above.
7. Update often, in small batches
One big upgrade a year is riskier than small, regular ones you can test and review. Outdated dependencies are a risk of their own, because many vulnerabilities are fixed by a version bump nobody applied. The same logic drives how we handle framework releases, which we wrote about in Next.js security patches as a maintenance task.
8. Separate the credentials
The real prize in most attacks is not your app. It is your tokens. Keep publish and deploy credentials out of the job that runs npm ci. If you publish packages, use trusted publishing with OIDC so there is no long-lived npm token to steal, and turn on staged publishing, which holds each release until a maintainer approves it with 2FA.
What each control actually stops
| Control | What it protects against | What it does not catch |
|---|---|---|
Lockfile plus npm ci | Silent drift to a newer, compromised version | A bad version you already locked in |
| Install scripts off (npm v12) | Malware that runs during npm install | Malicious code that runs when your app imports it |
min-release-age cooldown | Fresh poisoned releases in the first days | Long-lived, slow-burn backdoors |
npm audit | Known, published vulnerabilities | Brand new malware with no advisory |
npm audit signatures | Tampered tarballs, missing attestations | Malicious code built from a hijacked repo |
| Trusted publishing, staged publishing | Stolen long-lived publish tokens | A compromised CI workflow with no human review |
Read the right-hand column twice. Every control has a gap. Stacked together, the gaps rarely line up.
A one-hour hardening plan
- Check your npm version everywhereRun
npm -vlocally, in CI and in your Docker images. Mismatched versions mean different defaults and confusing failures. - Switch CI to npm ciReplace
npm installin build pipelines and make sure the lockfile is committed and reviewed. - Review install scriptsOn npm v12, run
npm approve-scripts --allow-scripts-pendingto see what is blocked, then approve only what you need. - Add a cooldownPut
min-release-age=3in the project.npmrcand turn on a Dependabot cooldown if you use it. - Audit your tokensList npm and CI tokens, delete what nobody uses, and move publishing to trusted publishing.
Where we fit in
This is part of how we approach client work. A useful business application is not only the interface people see on launch day. It needs a build process and a dependency set that another developer can inspect, update and trust months later. Our Monthly Maintenance plan covers monthly technical updates, monitoring and weekly backups for 499 lei per month (about 95 EUR), and new web apps start at 3,499 lei (about 665 EUR) with fixed-price quotes. If you just want a quick look at your public site's speed and SEO basics first, our free website audit takes seconds and needs no signup.
AI coding tools also add packages faster than ever, so dependency review matters more for small teams using them. We wrote about that shift in how AI coding agents are changing small teams.
Do it yourself or bring someone in?
Handle it yourself if you have one or two projects, a developer who owns the build, and you can do the hour above and then review lockfile changes in each pull request. That covers most of the risk.
Bring someone in if nobody clearly owns dependencies, if builds break when you update, if several apps share tokens, or if you shipped a site and have not touched its dependencies since. That is usually where the debt piles up quietly. A short engagement to set the defaults, plus a maintenance routine, is cheaper than a rushed rebuild after an incident.
Frequently Asked Questions
What is an npm supply chain attack?
It is an attack where malicious code reaches you through a dependency instead of through your own code. Attackers usually steal a maintainer's credentials or hijack a release workflow, publish a poisoned version of a trusted package, and let normal installs spread it to every project that pulls the new version.
Is npm audit enough to keep my project safe?
No. npm audit checks your dependencies against known, published advisories, so it is a good first filter for old vulnerabilities. It cannot flag a malicious version published an hour ago, which is exactly how worms like Shai-Hulud and ChainDrop spread before anyone had filed an advisory.
Does npm provenance mean a package is safe?
Provenance proves a package was built from a specific repository and workflow, which makes tampering after the build much harder. It does not prove the code is benign. In the ChainDrop attack, the malicious versions carried valid provenance because the attackers pushed code into the real repositories and used their real release pipelines.
Should I commit package-lock.json to git?
Yes, for applications you should always commit it. The lockfile records the exact version of every direct and transitive dependency, so npm ci can reproduce the same install everywhere. Without it, two installs a day apart can pull different versions, including a compromised one.
How many days should a dependency cooldown be?
Most teams pick between three and seven days. Three days matches the default GitHub chose for Dependabot in 2026 and catches most malicious releases, which tend to be detected within hours or days. Longer cooldowns add safety but slow down normal updates, so keep a manual override for urgent security fixes.
Do I still need to worry about this if I use pnpm, Yarn or Bun?
Yes. They all install from the same public registry, so a poisoned package is just as poisoned. Several of these tools already block dependency install scripts by default or offer an allowlist, but lockfiles, cooldowns and credential hygiene still matter no matter which package manager you choose.
Can a small team realistically secure its npm dependencies?
Yes. You do not need a security department, just a few defaults applied consistently: a committed lockfile, npm ci in CI, install scripts approved by hand, a short cooldown and tightly scoped tokens. Most of it takes an afternoon to set up and a few minutes per pull request after that.