← ~/blog

I Read Our Dependency Tree So You Don't Have To: A Supply Chain Horror Story

 /  security  /  291 words

Ran npm ls on our main app during an audit and started counting. We directly depend on 61 packages. Transitively, 1,340. I have reviewed maybe 30 of them, ever. The other 1,310 are code we ship to production on the strength of a version string and vibes.

npm audit reporting 41 vulnerabilities including a critical prototype pollution bug in deep-merge-lite, and an npm ls tree showing it pulled in three levels deep beneath the form builder.

The audit output is its own genre of horror. A critical prototype pollution bug in deep-merge-lite, a package I had never heard of, pulled in by config-loader, which I had also never heard of, pulled in by our form library. Three levels of trust delegation deep, and the thing at the bottom is maintained by one person who, when I checked, had last published anything in 2023.

This is the actual supply chain risk, not the movie version. Not nation states, mostly, but abandonment, ownership transfer to whoever asks, maintainer accounts with reused passwords, and install scripts that run arbitrary code on every developer laptop and CI box at npm install time. The blast radius of one compromised package four levels down is your entire build.

What we changed, none of it heroic. Lockfiles committed and CI installs frozen from them, so a surprise publish cannot walk into a build unreviewed. Install scripts disabled by default, allowlisted for the handful that genuinely need them. Automated PRs for updates so patching is a review click, not a project, because the alternative to easy updates is no updates. A budget mindset for new deps, every addition is adopting its whole subtree, so a left pad sized utility gets vendored instead. And audit gates in CI at high severity, tuned to be signal, not noise.

None of that makes the 1,310 strangers trustworthy. It makes betrayal detectable and recovery fast, which is the achievable version of trust.