npmvulncheck
v0.1.4
Published
govulncheck-inspired vulnerability scanner for npm projects
Maintainers
Readme
npmvulncheck
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.lockin lockfile-based modes - Understands JS/TS
import,require, and literal dynamicimport(...) - Resolves imports from each containing workspace/package context (not only root)
- Supports
text,json,sarif, andopenvexoutputs - 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.jsonpnpm-lock.yamlyarn.lock
node_modulesinstalled forinstalledmode (npm tree only)
Installation
From npm
npm install -g npmvulncheckOr run without global install:
npx npmvulncheck --helpFrom source
npm install
npm run build
npm linkDocker image
docker run --rm -v "$PWD:/work" shodohq/npmvulncheck:latest --mode lockfile --format textQuick 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.jsonScan 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 dependencies | Falls back to full inventory when imports are unresolved |
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.jsonfields (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 versionMain 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(includeimport type/export typein 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 overridesdirect: direct dependency upgradesauto: direct + transitive candidatesin-place: alias ofauto(kept for compatibility)
Output mapping:
sarif: remediation actions are emitted in each result'sfixespropertyjson: remediation plan is emitted as top-levelremediation, and per-affected notes are merged intofindings[].affected[].fix.noteopenvex: remediation actions are emitted instatements[].action_statementtext: remediation actions are shown in each finding'sfix:line
Exit codes and CI behavior
Default behavior depends on output format:
text: default--exit-code-on findings(exit1when filtered findings exist)json/sarif/openvex: default--exit-code-on none(exit0unless 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 highIgnore 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
untilvalues 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 --offlineUse --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 textSource reachability
examples/complex-unused-deps demonstrates how source mode can reduce findings from dependencies that exist in the lockfile but are not reachable from your entrypoint.
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 textNote: 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 buildCI/CD
GitHub Actions workflows are configured in .github/workflows.
ci.yml: runs on everypushandpull_requestwith Node.js18,20, and22- Steps:
npm ci->npm run lint->npm test->npm run build
- Steps:
scorecards.yml: runs OpenSSF Scorecard onmainpush and weekly schedule, then uploads SARIF to code scanningcd.yml: runs onv*tag push and publishes to npm and Docker Hub after lint/test/build pass- Includes a guard that checks
vX.Y.Ztag matchespackage.jsonversion - Uses
npm publish --provenance --access public - Builds and pushes multi-arch image tags (
X.Y.Z,X.Y,latest)
- Includes a guard that checks
Required repository secrets
Set these secrets in GitHub repository settings:
NPM_TOKEN: npm automation token with publish permissionDOCKERHUB_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-tagsWhen 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)
