DEV Community

Ryan Cuff
Ryan Cuff

Posted on

I scanned the top 20 npm packages. Everyone passed CVE checks, but here's what the static analysis found

Every time you run npm install, you're trusting someone else's code to run on your machine. Not eventually — right now. Postinstall hooks fire the second a package lands. No review, no prompt.

I built plum to change that. It's a CLI that downloads the package tarball into memory, reads the source, and scores it before anything touches your project.

I pointed it at the 20 most downloaded npm packages. The results are mostly reassuring — but a few are worth talking about.

How plum works

When you run plum axios, it:

  1. Fetches the .tgz from npm into memory (never to disk)
  2. Scans every .js file for red flags — obfuscated eval, credential access, shell execution, outbound HTTP in install scripts
  3. Checks CVEs against the exact resolved version
  4. Looks at maintainer age, publish recency, download count, and typosquatting distance

Instead of pass/fail, plum gives a 0–100 score. The same signal — like child_process access — means something very different in TypeScript's language server versus a brand-new package from an unknown maintainer.

The results

Package Downloads/wk Score Status
debug 553M 100/100 ✅ SAFE
semver 635M 100/100 ✅ SAFE
commander 367M 100/100 ✅ SAFE
chalk 410M 100/100 ✅ SAFE
dotenv 119M 100/100 ✅ SAFE
cors 49M 100/100 ✅ SAFE
jsonwebtoken 40M 100/100 ✅ SAFE
socket.io 12M 100/100 ✅ SAFE
lodash 146M 100/100 ✅ SAFE
moment 31M 100/100 ✅ SAFE
react 125M 90/100 ✅ SAFE
express 92M 90/100 ✅ SAFE
vue 11M 90/100 ✅ SAFE
eslint 126M 80/100 ✅ SAFE
uuid 240M 80/100 ✅ SAFE
mongoose 5M 80/100 ✅ SAFE
axios 99M 70/100 ✅ SAFE
typescript 181M 70/100 ✅ SAFE ⚠
webpack 44M 75/100 ✅ SAFE ⚠
next 34M 55/100 ⚠ RISKY

20/20 had zero CVEs. 16 scored SAFE. 1 scored RISKY.

The interesting ones

Next.js — 55/100, RISKY

Next.js is not malicious. It's maintained by Vercel, one of the most trusted teams in JS. The score comes from three child_process references in helper scripts that detect your package manager and check if you're online. Totally legitimate.

But 55/100 is a conversation starter, not a verdict. A framework using shell access is something you should know about, even if it's expected. That's the point of a score — it surfaces information and lets you decide.

TypeScript & Webpack — flagged, still SAFE

Both got dinged for child_process. TypeScript's language server needs shell access. Webpack spawns child processes for parallel builds. Neither is surprising. Plum caught real signals and still scored them SAFE given the context.

Axios — 70, worth a look

Axios scored 70 despite being clean with no CVEs and 99M weekly downloads. There's likely a pattern match hitting unnecessarily in the tarball — I'm investigating. If a tool flags something, it should explain every deduction. That's on the roadmap.

Why this matters

I ran npm audit on all 20 packages too. Zero issues. Also correct.

The difference is what each tool checks. npm audit is a database lookup — it tells you if a known CVE exists for a version. That's valuable. But the supply chain attacks that actually caused damage — event-stream, ua-parser-js, node-ipc — were all zero-days. No CVE existed when developers installed them.

Behavior-based scanning doesn't replace CVE checking. It adds a layer that databases can't.

Try it

npm install -g plum-scanner
plum <any-package>
Enter fullscreen mode Exit fullscreen mode

It's open source: github.com/rjcuff/plum

Built in Rust. Scans in under a second. No account, no API key.

Top comments (0)