Skip to content

shodohq/npmvulncheck

npmvulncheck

CI OpenSSF Scorecard npm version License

npmvulncheck is a govulncheck-inspired vulnerability scanner for npm projects. It combines lockfile/installed dependency analysis with optional source reachability to help reduce noisy findings.

Community quick links

Why this tool

  • Uses OSV as the vulnerability source (/v1/querybatch, /v1/vulns/{id})
  • Supports three scan modes: lockfile, installed, source
  • Supports package-lock.json / npm-shrinkwrap.json / pnpm-lock.yaml / yarn.lock in lockfile-based modes
  • Understands JS/TS import, require, and literal dynamic import(...)
  • Resolves imports from each containing workspace/package context (not only root)
  • Supports text, json, sarif, and openvex outputs
  • CI-friendly exit code control (--exit-code-on, --fail-on, --severity-threshold)
  • Includes local cache support and offline scanning

Requirements

  • Node.js >=18
  • Node.js project with one of:
    • package-lock.json / npm-shrinkwrap.json
    • pnpm-lock.yaml
    • yarn.lock
  • node_modules installed for installed mode (npm tree only)

Installation

From npm

npm install -g npmvulncheck

Or run without global install:

npx npmvulncheck --help

From source

npm install
npm run build
npm link

Docker image

docker run --rm -v "$PWD:/work" shodohq/npmvulncheck:latest --mode lockfile --format text

Quick start

# Default scan (lockfile + text)
npmvulncheck

# Installed tree scan
npmvulncheck --mode installed --format text

# Source reachability scan
npmvulncheck --mode source --entry src/index.ts --show traces

# Machine-readable output
npmvulncheck --mode source --format json > findings.json

Scan modes

Mode Input graph When to use Notes
lockfile lockfile dependency graph Fast, deterministic CI scans Supports npm/pnpm/yarn lockfiles
installed actual node_modules tree Match what is actually installed npm installed tree only
source lockfile + source imports Prioritize reachable findings Keeps non-reachable findings at lower priority; unresolved imports remain unknown

Entry points in source mode

You can pass explicit entry files with repeatable --entry. If no valid entries are provided, entries are auto-discovered from:

  • package.json fields (main, bin, exports)
  • Common conventions (src/index.ts, src/index.js, index.ts, index.js, etc.)

Commands

# Scan
npmvulncheck [options]

# Scan with remediation planning strategy
npmvulncheck --strategy auto
npmvulncheck --strategy override --format sarif

# Show vulnerability detail
npmvulncheck explain GHSA-xxxx-xxxx-xxxx

# Show tool/db cache metadata
npmvulncheck version

Main options

  • --mode lockfile|installed|source
  • --format text|json|sarif|openvex
  • --strategy override|direct|in-place|auto (default: auto)
  • --scope global|by-parent
  • --upgrade-level patch|minor|major|any
  • --only-reachable / --include-unreachable (remediation planning filter)
  • --root <dir>
  • --entry <file> (repeatable)
  • --conditions <condition> (repeatable; source-mode module conditions override)
  • --include-type-imports (include import type / export type in source reachability)
  • --explain-resolve (include unresolved import diagnostics and resolution candidates)
  • --show traces|verbose
  • --include dev / --omit dev (default: omit dev)
  • --include-dev / --omit-dev
  • --cache-dir <dir>
  • --offline
  • --ignore-file <path>
  • --exit-code-on none|findings|reachable-findings
  • --fail-on all|reachable|direct
  • --severity-threshold low|medium|high|critical

Integrated remediation planning

Remediation planning runs as part of the default scan flow. The selected strategy controls how remediation actions are generated:

  • override: transitive dependency overrides
  • direct: direct dependency upgrades
  • auto: direct + transitive candidates
  • in-place: alias of auto (kept for compatibility)

Output mapping:

  • sarif: remediation actions are emitted in each result's fixes property
  • json: remediation plan is emitted as top-level remediation, and per-affected notes are merged into findings[].affected[].fix.note
  • openvex: remediation actions are emitted in statements[].action_statement
  • text: remediation actions are shown in each finding's fix: line

Priority mapping:

  • text: each finding header includes priority:<level>
  • json: each finding includes findings[].priority
  • sarif: each rule/result includes properties.priority_level, properties.priority_reason, properties.priority_score
  • openvex: each statement includes status_notes with priority/reason/score

Exit codes and CI behavior

Default behavior depends on output format:

  • text: default --exit-code-on findings (exit 1 when filtered findings exist)
  • json/sarif/openvex: default --exit-code-on none (exit 0 unless runtime error)

Examples:

# Fail CI only for reachable vulnerabilities with severity >= high
npmvulncheck \
  --mode source \
  --format json \
  --exit-code-on reachable-findings \
  --fail-on reachable \
  --severity-threshold high

Ignore policy

Default file: .npmvulncheck-ignore.json at project root.

{
  "ignore": [
    {
      "id": "GHSA-xxxx-xxxx-xxxx",
      "until": "2026-06-30",
      "reason": "Waiting for upstream patch"
    }
  ]
}

Notes:

  • Rules are matched by vulnerability id
  • Expired rules are ignored
  • Invalid until values are ignored

Cache and offline mode

npmvulncheck caches vulnerability details and can run offline.

# Warm cache (online)
npmvulncheck --mode lockfile

# Reuse cache only
npmvulncheck --mode lockfile --offline

Use --cache-dir <dir> to override the cache location.

Example projects

Guided remediation planning

examples/guided-remediation demonstrates transitive remediation flow (override) and can also be run with auto.

# override strategy
npmvulncheck --root examples/guided-remediation --strategy override --format text

# auto strategy (direct + transitive)
npmvulncheck --root examples/guided-remediation --strategy auto --format text

Source reachability

examples/complex-unused-deps demonstrates how source mode can prioritize dependencies that are reachable from your entrypoint while lowering the priority of non-reachable findings.

npmvulncheck --root examples/complex-unused-deps --mode lockfile --format text
npmvulncheck --root examples/complex-unused-deps --mode source --entry src/index.ts --show traces --format text

Note: this fixture is for reachability scans, not remediation workflow coverage. --strategy override may produce a no-op plan when findings are direct dependencies; use --strategy direct or --strategy auto to include direct upgrades.

Development

npm install
npm run lint
npm test
npm run build

CI/CD

GitHub Actions workflows are configured in .github/workflows.

  • ci.yml: runs on every push and pull_request with Node.js 18, 20, and 22
    • Steps: npm ci -> npm run lint -> npm test -> npm run build
  • scorecards.yml: runs OpenSSF Scorecard on main push and weekly schedule, then uploads SARIF to code scanning
  • cd.yml: runs on v* tag push and publishes to npm and Docker Hub after lint/test/build pass
    • Includes a guard that checks vX.Y.Z tag matches package.json version
    • Uses npm publish --provenance --access public
    • Builds and pushes multi-arch image tags (X.Y.Z, X.Y, latest)

Required repository secrets

Set these secrets in GitHub repository settings:

  • NPM_TOKEN: npm automation token with publish permission
  • DOCKERHUB_USERNAME: Docker Hub username (or organization bot user)
  • DOCKERHUB_TOKEN: Docker Hub access token

If you publish under a different Docker Hub repository name, update DOCKER_IMAGE in .github/workflows/cd.yml.

Release flow

# 1) bump version
npm version patch

# 2) push commit and tag
git push origin main --follow-tags

When the v* tag is pushed, the CD workflow publishes the package automatically. The same workflow also pushes Docker images and creates a GitHub Release with generated notes.

Contributing

See CONTRIBUTING.md for setup, PR checklist, and review expectations.

License

AGPL-3.0-only (see LICENSE)

Packages

 
 
 

Contributors