Supply chain: the attack you cannot code around
Why a vulnerability in someone else's dependency is still your production incident, and the concrete habits (lockfiles, audits, pinning) that limit the blast radius.

Every lesson before this one had a line of code you could point at and fix. This one doesn't. npm install pulls in a dependency tree, and most of what's in it, you never chose directly and never read. A single express install drags in dozens of packages you didn't pick, each maintained by someone you've never met, each one a place an attacker only has to compromise once to reach every app that depends on it.
The trust you already extended without noticing
Run npm ls --all in any real project and count the packages. Linkstash itself depends on express, zod, and argon2 directly, three packages. The actual install tree underneath those three is a lot longer, because each of them has its own dependencies, and those have dependencies too.
You audited none of those transitive packages. You trusted the ones above them to have audited the ones below, and that chain of trust runs a lot deeper than most teams think about until something in it turns out to be malicious.
A real one, not a hypothetical
In August 2026, eleven npm packages including keyv, flat-cache, and cache-manager shipped with a credential-stealing preinstall script, signed with valid npm provenance from a legitimate GitHub Actions pipeline. flat-cache alone sits behind roughly 580 million monthly downloads, mostly because ESLint's caching depends on it. If your project has a linter, there's a real chance that dependency tree touched yours.
The full writeup is worth reading end to end: the keyv npm supply-chain attack covers what the payload actually did, why the signing chain didn't catch it, and the exact order to run cleanup in if you were exposed. The short version for this lesson: the attacker didn't break npm's security model. They compromised a maintainer's account, committed malware to a repository that was already trusted, and let the normal, honest build-and-publish pipeline sign it for them.
Provenance proves the build, not the source
npm provenance answers "was this package built from the code in this repository, by this CI system." It says nothing about whether that code was trustworthy in the first place. That's the gap the keyv attack lived in, and it's a gap no signature can close on its own.
What actually limits the damage
You can't audit every transitive dependency by hand, and you shouldn't try. What works instead is reducing how much any single compromised package can do, and how fast a bad version reaches your build.
Commit your lockfile, and treat it as load-bearing. package-lock.json or pnpm-lock.yaml pins the exact resolved version of every package in the tree, not just the range in package.json. Without it, two installs of the same package.json, on two different days, can pull two different dependency trees. npm ci (not npm install) in CI installs strictly from the lockfile and fails if it's out of sync, which is exactly the behavior you want in a pipeline.
Turn off lifecycle scripts by default. The keyv payload ran through a preinstall script, which fires the moment a package is installed, before you've imported a single line of it:
ignore-scripts=trueMost packages don't need install-time scripts at all. The ones that do (usually native builds) can be allowlisted explicitly. This one setting would have stopped the keyv attack cold on any machine that already had it on.
Put a delay between publish and install. A malicious package usually gets caught within hours, sometimes minutes, by registry scanners or the community. A brand-new dependency version that's five minutes old carries more risk than one that's been live for a week with no reports against it. Tools like npm's minimumReleaseAge or a private registry proxy with a cooldown window turn "we got hit on day one" into "we read about it before it ever reached us."
Run npm audit (or an equivalent) in CI, not just locally. It won't catch a fresh zero-day the moment it's published, nothing will, but it catches the much larger volume of known, disclosed vulnerabilities sitting unpatched in a tree nobody's looked at in months.
npm audit --audit-level=highQuick check
Why didn't npm's provenance attestation stop the keyv attack, if the signature was genuinely valid?
Scope what a compromised package can actually reach
The keyv attack's worm behavior depended on tokens that could do more than the one job that needed them, a stolen CI token that could publish any package the account had rights to, not just the one the job was building. Scope tokens as narrowly as the job allows. A CI pipeline that publishes one package needs publish rights to that one package, not your whole npm organization. The same logic applies to any credential a build step touches: the less a single leak can reach, the smaller the incident.
None of this makes a supply-chain attack impossible. Nothing does, not for a dependency tree you didn't write and can't fully read. What it does is turn a worst-case "every credential on every machine that ran npm install this week" into a much smaller, contained problem, and buy you the hours between a malicious publish and your build actually pulling it.
Next: rate limiting, abuse, and denial of wallet, the last of the attacks in this series that doesn't need a bug at all, just an endpoint with no limit on how many times it can be called.

Written by
Rhythm Bhiwani
Engineer and relentless builder, happiest reverse-engineering hard problems until they click.
Enjoyed this?
Tap the heart to leave some love.
Be the first to react
Comments
Join the conversation.
Loading comments…


