Supply Chain Security
Defend against malicious packages, typosquatting, account takeover, and the npm install foot-gun.
Why
Every npm install runs arbitrary postinstall scripts with your permissions — a compromised package can steal env vars, exfiltrate source, or cryptomin. The supply-chain attacks of 2021-2024 (ua-parser-js, event-stream, coa, rc) show this isn't theoretical: a maintainer's account is phished, a new version ships malware, and within hours thousands of CI pipelines run it. You can't eliminate this risk but you can shrink it dramatically: lockfiles pin what installs, allowlists block unknown packages, provenance (SLSA) verifies the artifact matches the source, and disabling scripts removes the execution vector. Defense in depth is the only sane posture.
When
Continuously — make it a CI gate, not a quarterly check. Revisit whenever you add a new dependency.
How
1. Commit lockfiles (pins exact versions + integrity hashes — npm ci won't install a tampered tarball) 2. Disable install scripts by default: npm config set ignore-scripts true; allowlist only trusted packages 3. Use an allowlist/denylist: npm config set @scope:registry or a tool like socket.dev / npm-audit-resolver 4. Verify provenance: prefer packages with SLSA provenance (npm v9+ shows the "provenance" badge) 5. Scan on every PR: OSV-Scanner or Snyk in CI, fail on high/critical 6. Prefer packages with 2FA on the maintainer's npm account, active maintenance, and a clear owner 7. For high-value deps: pin the exact version + integrity hash, never use a floating range in production
Example
# .npmrc — disable scripts, allowlist trusted
ignore-scripts=true
# Re-enable scripts only for trusted packages:
enable-scripts-for=@trusted-scope/*
# CI supply-chain gate (GitHub Actions)
- name: OSV-Scanner
run: npx @google/osv-scanner --lockfile package-lock.json
- name: Socket
run: npx socket-security # alerts on install scripts, network access, eval
# Verify provenance (npm v9+)
npm install lodash --provenance
# Check: npm view lodash@4.17.21 dist.attestations
# Pin a high-value dep with integrity hash in package.json
"dependencies": {
"express": "4.18.2" # pinned exact, not ^4.18.2
}
# The lockfile adds: "integrity": "sha512-..." so npm ci
# refuses to install a tarball that doesn't match the hash.Common Problems
- ⚠Using ^ or ~ ranges in production deps — a transitive bump can pull a compromised version without you changing anything.
- ⚠Ignoring install scripts risk — a package's postinstall can run anything; most teams never audit which packages have them.
- ⚠Trusting a package because it has 10M weekly downloads — popularity makes it a bigger target, not a safer one.
Best Practices
- ✓Pin exact versions in production and rely on the lockfile's integrity hashes — npm ci then refuses any tampered tarball.
- ✓Disable install scripts globally and allowlist only the handful of trusted packages that genuinely need them.
- ✓Run OSV-Scanner or Snyk on every PR as a CI gate — supply-chain defense must be continuous, not quarterly.