This week's developer news had a story with a shape I can't stop thinking about: Postman reportedly shipped an update that auto-installed plugins into people's AI coding tools — Claude Code, Codex, Cursor, Copilot — without asking first. They said they'd revert it. But the episode points at a door most of us leave wide open without a thought:
Code runs when you install things. Not when you run the program. When you install it.
In npm, that door is the install script: preinstall, install, postinstall entries in a package's scripts that npm executes during installation, with your user's permissions, on your machine, before you've read a single line of the package. It's the mechanism behind real supply-chain attacks — and behind plenty of legitimate native builds. I wanted to know how wide the door actually stands in the packages everyone uses, so I checked the 25 most-depended-upon packages on npm and read their script blocks.
Result: 0 of the 25 declare an install-time hook. Not one.
| Package | Install hooks | What its scripts actually hold |
|---|---|---|
| express | none | lint + test commands |
| lodash | none | a test note |
| react / react-dom | none | nothing / a start script |
| debug | none | lint, test, coverage |
| chalk | none | test + benchmark |
| commander | none | lint, format, type checks |
| typescript / cors / morgan / prettier | none | nothing at all |
The closest calls were a handful of prepare scripts (axios, webpack, glob, mkdirp, rimraf) — and a prepare script is a different animal: npm runs it when a package is installed from a git repo or locally, not when you install the published tarball from the registry. It's the maintainer's build step, not your install step. Reading the raw output, the giants of the ecosystem are almost boringly well-behaved: their scripts are tests and linters, pointed at their own contributors, not at you.
Two reactions to that zero, in order.
First, relief — the centre of the ecosystem is not where the danger lives. The packages with a billion downloads are maintained by people who know exactly what an install hook would look like in an audit, and they don't ship one.
Second, the correct amount of suspicion: 25 famous packages are not your dependency tree. Your tree has hundreds of packages in it, most of them small, single-maintainer, and exactly as audited as you'd expect. The door being closed at the top of the tree says nothing about the leaves. The famous supply-chain incidents never start with express. They start with a colour library, a date formatter, a package with a cute name and a tired maintainer.
The check itself costs one command per package, and it's the same one I ran:
npm view <package> scripts
Look for preinstall, install, postinstall (and their pre/post siblings on anything that runs at install). Most of the time you'll find tests. Occasionally you'll find the reason a security team somewhere had a long week. For your own project, npm ls plus a script that greps every dependency's manifest is an afternoon's paranoia at most — and plain npm install --ignore-scripts exists for the days when you'd rather nothing run at all, accepting that a few native packages will sulk.
The Postman story will fade by next week. The door won't. Everything you install executes with your permissions at the exact moment you trust it most — before you've read it. Twenty-five for twenty-five clean at the top of the tree is a good headline. It's the other four hundred packages in your node_modules that the headline is not about.
P.S. Everything I write stays free — that part doesn't change. But if this one was worth something to you, you can buy me a coffee ☕ (one-time or monthly, both work). It covers the API credits and hosting behind these experiments — and honestly, it's also the nudge that gets the next one started on the evenings I'd otherwise skip it. — Salam


Top comments (3)
Clean architecture is easy to maintain in a slide deck; the real test is how gracefully the system degrades under cascading production failures.
Minimizing abstraction layers and owning your critical runtime path will always outperform adding more third-party glue.
That slide deck line is the whole piece in one sentence, honestly. Twenty-five clean packages at the top is the easy part — the degradation test lives in the other four hundred in your tree, the small ones with a tired maintainer. That's the bit I didn't check, and the bit that'd actually bite.
The npm view scripts check is brilliantly simple.