- Published on
How to Safely Use npm
- Authors

- Name
- nikUnique
- @commitnobug

Every JavaScript project starts the same way - you open a terminal, run npm install, and trust that the code you just downloaded is safe. Most of the time it is. But npm hosts over two million packages, and not all of them are trustworthy. A few simple habits are all it takes to significantly reduce your risk.
What is a supply chain attack?
Your code is probably fine. The problem is everything your code depends on.
A supply chain attack targets something upstream of you - a package you installed, a transitive dependency you've never heard of, a build tool you trust implicitly. The attacker doesn't break into your repo. They break into the ecosystem you rely on, and your npm install does the rest.
This works because a typical project doesn't just install the packages listed in package.json. It installs their dependencies, and their dependencies, and so on. A single install can pull in 500+ packages written by hundreds of different people. You're trusting all of them.
How it happens
The most common vectors:
- Abandoned packages. A maintainer stops caring. An attacker claims the package name or buys the account, then publishes a new version with malicious code baked in.
- Compromised accounts. A maintainer gets phished. A legitimate package gets a backdoored release published under a trusted name.
- Typosquatting. A package named lodahs or expres sits on the registry waiting for a typo.
- Malicious install scripts. A package adds a postinstall script that runs automatically on install and quietly exfiltrates your .env file - API keys, database credentials, tokens.
Why it's hard to defend against
You didn't write those 500 packages. You probably haven't read them. They run with the same permissions as your own code, and npm executes its install scripts automatically, the moment you run npm install.
This is why the advice in this post exists. You can write perfect code and still ship malware - because the attack surface isn't your code, it's your package.json.
Check Before You Install
Before installing any package, spend 30 seconds on its npm page.
Weekly downloads are your first signal. A package with 50 downloads per week claiming to solve a common problem is a red flag. Popular, well-maintained packages have consistent download numbers. If something looks suspiciously low for what it claims to do, skip it.
Last publish date tells you whether the package is alive. An unpublished package from three years ago won't receive security patches. For anything you're putting in production, make sure someone is still maintaining it.
The GitHub repo is the real source of truth. No repo, no stars, no open issues - skip it. A healthy package has an active issue tracker, recent commits, and a community around it.
Watch Out for Typosquatting
One of the most common supply chain attacks is typosquatting - publishing a malicious package with a name nearly identical to a popular one. Examples:
- lodash vs 1odahs
- express vs expres
- react vs reakt
Always double-check the package name before hitting enter. If you're copy-pasting an install command from a tutorial - verify the name on the npm website first.
Count the Dependencies
A small utility with 40 transitive dependencies should raise an eyebrow. Every dependency is a potential attack surface. You're not just trusting the package - you're trusting everything it depends on.
Run this to see what you're getting before committing:
npm info <package-name> dependencies
Use npm audit
After every install, run:
npm audit
This checks your dependency tree against a database of known vulnerabilities. If something is flagged, read the details - not every vulnerability affects your specific usage, but you should know what's there.
Wait a Few Days After Updates
This one is underrated. If a malicious update gets published, it usually gets caught and pulled within hours - but only after some people have already installed it. A simple habit: when a package releases an update, wait about 3 days before upgrading.
The urgency should scale with severity, though. Some situations call for acting immediately:
- A known CVE (Common Vulnerabilities and Exposures) is actively being exploited
- The update fixes a bug that's breaking your app
Outside of those, there's no rush - let the community surface regressions first. The "wait" protects you on two fronts. It skips the window where a malicious update is still live, and lets others catch regressions before you upgrade. For minor version bumps with no security implications, a few days cost nothing, and occasionally saves you an afternoon of debugging.
Be Careful with Install Scripts
Some packages run scripts automatically during install - a common vector for malicious code. Rather than remembering to pass a flag each time, disable scripts globally:
# ~/.npmrc
ignore-scripts=true
When a package genuinely needs scripts to build, you can run the install with scripts enabled just for that one package:
npm install --ignore-scripts=false <package-name>
Before enabling scripts for a package, check what they actually run. In the package's package.json, find the scripts field - it'll point to a file like "postinstall": "node scripts/build.js". Open that file and read it. You're not doing a full security audit - you're just checking whether it looks like a build step or something suspicious.
Prefer Packages from Known Authors
This isn't a hard rule, but it helps. Packages maintained by known organizations or well-known individuals in the ecosystem carry more accountability. A few names worth trusting by default:
- Vercel - Next.js, SWR, and related tooling
- Sindre Sorhus - hundreds of high-quality utilities
- The OpenJS Foundation - Node.js, Express, and others
Set Up Dependabot
If your project is on GitHub, go to your repo's Settings, find Advanced Security in the sidebar, and enable two things:
- Dependabot alerts - notifies you when a vulnerable dependency is found
- Dependabot security updates - automatically opens pull requests to fix them
Both are free for public and private repositories, and take two minutes to set up.
Summary
| Habit | Effort | Benefit |
|---|---|---|
| Check downloads and publish date | Low | Catches abandoned or suspicious packages |
| Verify the GitHub repo | Low | Confirms the package is real and active |
| Double-check the package name | Low | Prevents typosquatting |
| Run npm audit after install | Low | Catches known CVEs immediately |
| Wait 3 days after updates | Zero | Avoids malicious update windows |
| Use --ignore-scripts by default | Low | Prevents malicious install scripts |
| Enable Dependabot | One-time | Continuous automated monitoring |
None of these takes more than a few minutes. Combined, they cover the most common attack vectors and make your npm usage significantly safer.
Conclusion
Security isn't a one-time setup - it's a habit. npm is just one surface. The same mindset applies everywhere: verify before you trust, stay informed, and automate what you can - tools like Dependabot handle the ongoing monitoring so you don't have to. Getting compromised once can mean stolen credentials, leaked environment variables, backdoored builds, or worse - your package becoming the vector that hits your users. These habits won't make you invulnerable, but they make you a harder target than the next developer who just runs npm install and hopes.
Found this helpful? Please share it with someone who also might find it helpful. Would you like more posts from me? Subscribe to the newsletter. Got questions? Send an email to commitnobug@outlook.com.