You run npm install for a package with 40 million weekly downloads, and in the span of that command, code from a dozen strangers you've never vetted lands on your machine with the same permissions as you. That's not a hypothetical. That's Tuesday for most developers. The uncomfortable truth is that a modern app isn't the few thousand lines you wrote. It's the hundreds of thousands of lines you inherited from your dependency tree, plus every transitive dependency underneath those.
Attackers figured this out years ago. Sonatype's 2023 State of the Software Supply Chain report tracked more than 245,000 malicious packages published to open source registries in a single year, which was roughly twice the total of all previous years combined. The event-stream incident, the compromised ua-parser-js releases, the XZ Utils backdoor that nearly slipped into every major Linux distro in early 2024 — these weren't obscure libraries. They were dependencies you almost certainly use indirectly.
This guide walks through how to vet an open source tool for open source supply chain security risks before it touches your environment. You'll get a repeatable checklist, a worked example scoring a real-world package, a comparison of scanning tools, and the exact signals that separate a healthy project from a soon-to-be headline.
Key Takeaways
- Most breaches enter through transitive dependencies you never chose directly, so vet the whole tree, not just the top-level package.
- Pin exact versions and use lockfiles. A floating
^1.2.0range is an open invitation for a poisoned patch release.- The four highest-signal checks are: maintainer count and activity, install scripts, recent ownership changes, and a diff between the published tarball and the source repo.
- Automate what you can with tools like
npm audit, Socket, OSV-Scanner, and Dependabot, but treat their output as a starting point, not a verdict.- A five-minute manual review before
installbeats a five-day incident response after compromise.
What a supply chain attack actually looks like
A software supply chain attack is any compromise that reaches you through a component you trusted rather than a system you own. Instead of breaking down your front door, the attacker hides inside a delivery you invited in.
In open source, the common patterns are surprisingly few:
- Typosquatting: A malicious package named
crossenvmimics the realcross-env, banking on a mistyped install command. - Dependency confusion: An attacker publishes a public package with the same name as your internal private one, and your build tool grabs the public version because it has a higher version number.
- Account takeover: A legitimate, popular package gets a malicious update after a maintainer's credentials are phished or their token leaks.
- Maintainer handoff gone wrong: A burned-out solo maintainer hands the keys to a helpful stranger who then ships a payload weeks later. This is roughly the XZ Utils playbook.
- Malicious install scripts: Code that runs during
npm installorpip install— before you've ever imported the library — to exfiltrate environment variables or SSH keys.
The payload is almost always the same goal: steal secrets, mine crypto, or open a backdoor. What changes is the delivery vehicle. Knowing these five patterns is half the battle, because your review is really just checking whether a package could be a vehicle for any of them.
The pre-install vetting checklist
Before you type install, run through this. Most of it takes under five minutes for a single package, and you get faster with practice.
1. Confirm you have the right package
Read the package name character by character. Check the official homepage, the GitHub repo linked from the registry, and the download count. A legitimate popular library has millions of downloads and a repo with thousands of stars. A typosquat has a few hundred downloads and a suspiciously recent publish date.
2. Check maintenance health
Open the repository and look at:
- Commit recency: Last commit within the past few months is a good sign. A last commit three years ago on a security-sensitive tool is a red flag.
- Maintainer count: One maintainer is a bus-factor and takeover risk. Several active maintainers means more eyes on releases.
- Open issue and PR ratio: Hundreds of stale issues with no responses suggest an abandoned project.
- Release cadence: Regular, documented releases beat a sudden version jump after six months of silence.
3. Inspect install scripts
This is the single highest-value check for npm. Run npm view <package> scripts or open the package.json. Look for preinstall, install, and postinstall hooks. A UI component library has no business running a shell script on install. If it does, read that script line by line.
4. Diff the published artifact against source
The tarball on the registry does not always match the code in the public repo. Download the published package and compare. Tools like npm pack plus a manual diff, or Socket's "shipped code" view, will surface files that exist in the tarball but not the repo. That gap is where backdoors hide.
5. Review permissions and capabilities
Does the package touch the network, the filesystem, or environment variables? A date-formatting library that suddenly reads process.env and makes HTTP requests is behaving well outside its job description.
6. Check for recent ownership or maintainer changes
A new maintainer added last week to a package that's been stable for five years is exactly the profile of a takeover in progress. Registry metadata and GitHub contributor history both show this.
A worked example: scoring a package before install
Let's make this concrete. Say you need a small library to parse CSV files in a Node project, and you're deciding between two candidates: csv-parse (the well-known option) and a newcomer we'll call fast-csv-lite. Here's the five-minute review I actually run.
Step 1 — Downloads and age. csv-parse shows roughly 4 million weekly downloads and a first publish date over a decade ago. fast-csv-lite shows 900 weekly downloads and was first published 11 days ago. Already the risk profiles diverge sharply.
Step 2 — Repository. csv-parse links to an active monorepo with dozens of contributors and CI badges. fast-csv-lite links to a repo with three commits, all authored the same day, and no tests.
Step 3 — Install scripts. I run npm view fast-csv-lite scripts. It returns a postinstall that pipes a remote URL into node. That's a hard stop. No legitimate CSV parser needs to fetch and execute remote code on install.
Step 4 — Dependencies. csv-parse has zero runtime dependencies. fast-csv-lite pulls in four packages, two of which were also published in the last two weeks by the same author. That's a classic dependency-cluster planted to look legitimate.
Scored on a simple 0-to-5 scale where higher is safer:
| Signal | csv-parse | fast-csv-lite |
|---|---|---|
| Download volume | 5 | 1 |
| Repo health | 5 | 1 |
| Install scripts | 5 (none) | 0 (remote exec) |
| Dependency tree | 5 (zero deps) | 2 |
| Maintainer track record | 4 | 1 |
The decision writes itself. The point isn't that new packages are always malicious — plenty of great libraries start small. The point is that a five-minute score gives you a defensible, repeatable reason to trust or reject something, instead of installing on vibes.
Automated scanning tools compared
Manual review scales poorly across a 1,200-dependency project. You need automation for breadth and manual review for depth. Here's how the main options compare on the criteria that matter.
| Tool | Detects known CVEs | Flags malicious behavior | Install-time scanning | Cost |
|---|---|---|---|---|
npm audit |
Yes | No | No | Free, built in |
| OSV-Scanner | Yes (multi-ecosystem) | No | No | Free, open source |
| Dependabot | Yes | No | No | Free on GitHub |
| Socket | Partial | Yes (behavioral) | Yes | Free tier + paid |
| Snyk | Yes | Partial | Partial | Free tier + paid |
The key distinction: most scanners check dependencies against a database of known vulnerabilities. That's essential, but it only catches yesterday's problems. A brand-new malicious package won't have a CVE yet. Behavioral tools like Socket flag suspicious capabilities — a package that newly added network access, filesystem writes, or install scripts — which is how you catch a fresh attack the databases haven't logged.
My working recommendation: run npm audit or OSV-Scanner in CI for known CVEs, enable Dependabot for automated update PRs, and layer a behavioral tool for the zero-day-style catches. If you're managing dependencies across multiple operating systems, our guide on setting up a cross-platform package manager on any OS pairs well with this workflow.
Locking down your install workflow
Vetting a package once doesn't help if a poisoned update sneaks in next week. Harden the process itself.
- Commit your lockfile.
package-lock.json,yarn.lock, orpoetry.lockpin the exact versions and hashes of every dependency, transitive ones included. Without it, two developers on the same repo can end up








