DevSecOps

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.

October 7, 20255 min readShipSafer Team

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:

  1. Check if a newer version of the direct dependency fixes it
  2. Add an overrides or resolutions in package.json to force a specific version:
{
  "overrides": {
    "vulnerable-package": ">=1.2.3"
  }
}
  1. Open an issue on the direct dependency's repo
  2. 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:

  1. Scans your dependencies for known vulnerabilities
  2. Opens automated PRs to update vulnerable packages
  3. 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:

ScoreSeverityAction
9.0–10.0CriticalFix within 24–72 hours
7.0–8.9HighFix within 1–2 weeks
4.0–6.9ModerateFix within 1 month or next sprint
0.1–3.9LowFix 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.

dependencies
npm-audit
snyk
dependabot
supply-chain
devsecops

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.