Dependency Vulnerability Scanning: npm audit, Snyk, and Dependabot
Third-party dependencies are the largest attack surface in modern web apps. Learn how to find, prioritize, and fix vulnerabilities with npm audit, Snyk, Dependabot, and automated CI/CD gates.
The average Node.js application has over 1,000 transitive dependencies. Each one is a potential attack surface. The Log4Shell vulnerability (Log4j) affected millions of applications and took months to fully remediate — because most teams didn't know they had Log4j in their dependency tree.
This guide covers the tools and practices to keep your dependency chain secure.
Why Dependencies Are Your Biggest Risk
Unlike your application code, which you control and review, third-party packages:
- Are often authored by unknown developers
- Can be compromised after your initial install (supply chain attacks)
- Include deeply nested transitive dependencies you've never heard of
- May have vulnerabilities discovered years after you added them
The 2021 npm ua-parser-js hijack, the 2022 node-ipc protest-ware, and countless typosquatting packages demonstrate that npm is an active attack surface.
npm audit
npm audit is built into npm and checks your dependencies against the GitHub Advisory Database.
Basic usage
# Audit all dependencies
npm audit
# Audit only production dependencies
npm audit --omit=dev
# Output as JSON (for CI/CD processing)
npm audit --json
# Show only critical and high severity
npm audit --audit-level=high
Understanding npm audit output
found 3 vulnerabilities (1 moderate, 2 high)
high Prototype Pollution in lodash
Package lodash
Patched in >=4.17.21
Dependency of your-package > lodash
Path your-package > lodash
More info https://github.com/advisories/GHSA-...
Key fields:
- Severity: Critical > High > Moderate > Low > Info
- Patched in: The version that fixes it
- Path: Which of your direct dependencies pulls it in
npm audit fix
# Fix vulnerabilities that have compatible upgrades
npm audit fix
# Fix including breaking changes (major version bumps)
npm audit fix --force
# Dry run to see what would change
npm audit fix --dry-run
⚠️ --force can introduce breaking changes. Always test after running it.
When you can't upgrade
If a vulnerability is in a transitive dependency and the direct parent hasn't released a fix:
- Check if a newer version of the direct dependency fixes it
- Add an
overridesorresolutionsinpackage.jsonto force a specific version:
{
"overrides": {
"vulnerable-package": ">=1.2.3"
}
}
- Open an issue on the direct dependency's repo
- Consider replacing the dependency if it's unmaintained
Snyk
Snyk provides deeper analysis than npm audit:
- Broader vulnerability database
- License compliance checking
- Container and IaC scanning
- Remediation PRs
# Install Snyk CLI
npm install -g snyk
# Authenticate
snyk auth
# Test current project
snyk test
# Test only production deps
snyk test --production
# Open source monitoring (tracks over time)
snyk monitor
# Fix with automatic PR creation
snyk fix
Snyk in CI/CD
# GitHub Actions
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=upgradable
GitHub Dependabot
Dependabot is free and built into GitHub. It:
- Scans your dependencies for known vulnerabilities
- Opens automated PRs to update vulnerable packages
- Can also open routine version update PRs
Enable Dependabot security alerts
In your repo: Settings → Code security and analysis → Dependabot alerts → Enable
Dependabot version updates (.github/dependabot.yml)
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
groups:
# Group minor/patch updates to reduce PR noise
minor-and-patch:
update-types:
- "minor"
- "patch"
Dependabot PRs include:
- What changed
- Release notes and changelog
- Compatibility score (based on CI results from other repos using this package)
CVSS Scores: How to Prioritize
Not every vulnerability needs immediate action. The Common Vulnerability Scoring System (CVSS) rates severity 0–10:
| Score | Severity | Action |
|---|---|---|
| 9.0–10.0 | Critical | Fix within 24–72 hours |
| 7.0–8.9 | High | Fix within 1–2 weeks |
| 4.0–6.9 | Moderate | Fix within 1 month or next sprint |
| 0.1–3.9 | Low | Fix in next release |
However, CVSS score alone isn't enough. Also consider:
- Exploitability: Is there a known working exploit?
- Reachability: Is the vulnerable code path actually called?
- Context: Is this in a dev dependency or production?
- Exposure: Is the vulnerable functionality exposed to untrusted input?
A CVSS 9.0 vulnerability in a dev-only dependency that never touches user input is lower priority than a CVSS 6.0 one in a production API handler.
Supply Chain Security
Beyond known CVEs, supply chain attacks are a growing concern. Key defenses:
Lock files
Always commit your lock file (package-lock.json or yarn.lock or pnpm-lock.yaml). Never run npm install in CI — use npm ci which installs exactly what's in the lock file:
# In CI: installs exact versions from lock file
npm ci
# In development: updates lock file
npm install
Integrity verification
npm's lock file includes cryptographic hashes. The npm ci command verifies these hashes, detecting if a package was tampered with after you last updated the lock file.
Package origin checks
# Check who published a package and when
npm info package-name
# Check recent publish dates (sudden new maintainer = risk)
npm info package-name time
Avoid overly broad permissions
Be careful with packages that request write access to your filesystem, network, or environment variables. Review preinstall/postinstall scripts in package.json.
Integrating into CI/CD
Add a security gate that blocks deploys when critical vulnerabilities are found:
# .github/workflows/security.yml
name: Security Scan
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=critical
# Fails if any critical vulnerabilities exist
- name: Snyk scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
The goal: make security scanning automatic so it requires zero discipline to maintain. If it runs on every PR, vulnerabilities can't sneak in undetected.