Skip to main content

npm Package Security: Best Practices for 2026

·PkgPulse Team
0

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.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.