Skip to main content

npm Package Security: Best Practices for 2026

·PkgPulse Team

Supply chain attacks on npm packages grew 150% between 2023 and 2025. The event-stream incident, ua-parser-js hijack, and colors.js sabotage proved that a single compromised package can affect millions of projects.

In 2026, npm security isn't optional — it's survival. Here's a practical guide to protecting your project.

The Threat Landscape

Common Attack Vectors

  1. Typosquatting — Malicious packages with names similar to popular ones (lodas instead of lodash)
  2. Account takeover — Attackers compromise a maintainer's npm account and publish malicious versions
  3. Dependency confusion — Internal package names that overlap with public npm packages
  4. Malicious install scripts — Packages that run code during npm install via postinstall scripts
  5. Protestware — Maintainers intentionally adding destructive code to their own packages

Real Impact

  • event-stream (2018): Backdoor targeting a Bitcoin wallet — 8M weekly downloads affected
  • ua-parser-js (2021): Cryptominer injected into a package with 8M weekly downloads
  • colors + faker (2022): Maintainer sabotaged their own packages used by thousands of projects
  • @rspack/core (2025): Supply chain attack via compromised npm tokens

Essential Security Practices

1. Audit Dependencies Regularly

Run npm audit on every CI build. Don't just check — enforce it.

# Fail CI on high/critical vulnerabilities
npm audit --audit-level=high

# Or with pnpm
pnpm audit --audit-level high

Better yet, use automated tools:

  • Socket.dev — Detects supply chain risks that npm audit misses (typosquatting, install scripts, network access)
  • Snyk — Deep vulnerability scanning with fix suggestions
  • GitHub Dependabot — Automated PRs for vulnerable dependencies

2. Lock Your Dependencies

Always commit your lock file (package-lock.json, yarn.lock, or pnpm-lock.yaml). In CI, use the exact versions from the lock file:

# npm — uses package-lock.json exactly
npm ci

# pnpm — uses pnpm-lock.yaml exactly
pnpm install --frozen-lockfile

# yarn — uses yarn.lock exactly
yarn install --immutable

Never run npm install in CI. Use npm ci — it's faster and ensures reproducible builds.

3. Pin Exact Versions

Semver ranges (^1.2.3, ~1.2.3) allow automatic updates that could introduce malicious code. For critical dependencies, pin exact versions:

{
  "dependencies": {
    "express": "4.21.2",
    "next": "15.1.4"
  }
}

Use .npmrc to make exact versions the default:

# .npmrc
save-exact=true

4. Disable Install Scripts for Unknown Packages

Many supply chain attacks use postinstall scripts to run malicious code during installation. Block them by default:

# .npmrc
ignore-scripts=true

Then whitelist trusted packages that need install scripts:

// package.json (pnpm)
{
  "pnpm": {
    "onlyBuiltDependencies": ["sharp", "bcrypt", "esbuild"]
  }
}

5. Use npm Provenance

npm now supports package provenance — cryptographic proof that a package was built from a specific source repository and commit. Look for the provenance badge on npmjs.com.

# Publish with provenance (in GitHub Actions)
npm publish --provenance

When evaluating packages, prefer those with provenance attestations. Check this on PkgPulse — our health scores factor in security practices.

6. Review New Dependencies Before Installing

Before running npm install some-package, check:

  1. Source code — Is the repository linked and does the code match what's published?
  2. Maintainers — Who publishes this package? Are they known?
  3. Install scripts — Does it run scripts during install? (npm show some-package scripts)
  4. Dependencies — How many transitive dependencies does it pull in?
  5. Health score — Check PkgPulse for a health assessment
# Check package info before installing
npm info some-package

# Check for install scripts
npm show some-package scripts

# See all transitive dependencies
npm explain some-package

7. Use Scoped Packages for Internal Code

Prevent dependency confusion attacks by using scoped packages for internal/private code:

{
  "name": "@mycompany/auth-utils",
  "version": "1.0.0",
  "private": true
}

Configure your .npmrc to route scoped packages to your private registry:

# .npmrc
@mycompany:registry=https://npm.mycompany.com

8. Enable 2FA on npm Accounts

If you publish packages, enable two-factor authentication on your npm account. Require it for publishing:

npm profile enable-2fa auth-and-writes

This prevents account takeover attacks — one of the most common vectors for injecting malicious code into popular packages.

9. Monitor for Vulnerability Disclosures

Set up automated monitoring:

  • GitHub Dependabot alerts — Enable on every repository
  • Snyk monitoring — Continuous scanning of your deployed dependencies
  • Socket.dev GitHub App — Alerts on suspicious dependency changes in PRs
  • npm audit signatures — Verify package integrity

10. Have an Incident Response Plan

When (not if) a dependency is compromised:

  1. Identify affected projectsnpm ls vulnerable-package across all repos
  2. Pin to last known good version — Immediately update lock files
  3. Check for data exfiltration — Review network logs from CI/CD
  4. Rotate secrets — Any environment variables or tokens accessible during builds
  5. Communicate — Notify your team and users if applicable

Automated Security Pipeline

Here's a GitHub Actions workflow that enforces security on every PR:

name: Security Check
on: [pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: npm audit
        run: npm audit --audit-level=high

      - name: Check for known malicious packages
        uses: nicolo-ribaudo/checklocks@v1

      - name: License compliance
        run: npx license-checker --failOn "GPL-3.0;AGPL-3.0"

Evaluating Package Security on PkgPulse

When comparing packages on PkgPulse, look for these security indicators:

  • Maintenance frequency — Regular updates indicate active vulnerability patching
  • Dependency count — Fewer dependencies = smaller attack surface
  • Health score — Factors in security practices, maintenance, and community size
  • Download trends — Sudden spikes or drops can indicate issues

Conclusion

npm security in 2026 requires active defense — auditing, pinning, monitoring, and reviewing. The JavaScript ecosystem's strength (massive package variety) is also its vulnerability (massive attack surface).

Start with the basics: npm ci in CI, npm audit on every build, and save-exact=true in .npmrc. Then layer on Socket.dev or Snyk for deeper analysis. Your future self will thank you.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.