When to use
Step 1 - Install
Per osv-usage (opens in new window):
docker pull ghcr.io/google/osv-scanner:latestOther install paths (consult google.github.io/osv-scanner for current options per platform):
# Go install
go install github.com/google/osv-scanner/cmd/osv-scanner@v1
# Homebrew
brew install osv-scanner
# Direct binary download from github.com/google/osv-scanner/releasesStep 2 - Basic recursive scan
Per osv-usage (opens in new window):
osv-scanner scan -r ./my-project-dir/Auto-detects manifest files (package-lock.json, Pipfile.lock, go.mod, Cargo.lock, etc.) and queries OSV.dev for each.
Per-lockfile scan (-L targets one lockfile without recursing, useful in monorepos):
osv-scanner scan -L package-lock.json --output-file scan-results.txtStep 3 - Output formats
Per osv-usage (opens in new window): --format json and --format=vertical and --format=html are referenced. Full format list per the osv-scanner documentation:
| Format | Use |
|---|---|
--format json | For downstream aggregation |
--format sarif | GitHub Code Scanning upload |
--format markdown | PR comments |
--format vertical | Default human-readable |
--format html | Standalone report |
Step 4 - SBOM-driven scanning
For projects with custom build systems that emit SBOMs but lack natively-parsed lockfiles:
osv-scanner scan --sbom my-app.cyclonedx.json
osv-scanner scan --sbom my-app.spdx.jsonThis composes with syft-generation - Syft generates the SBOM, OSV-Scanner queries OSV.dev against it.
Step 5 - osv-scanner.toml config
Suppress a vuln with an expiring, reasoned entry (auditable in git):
# osv-scanner.toml
[[IgnoredVulns]]
id = "CVE-2024-1234"
ignoreUntil = 2026-12-15T00:00:00Z
reason = "Reachability confirms unreachable; tracked in JIRA-1234"The full schema ([[PackageOverrides]], multi-entry config, --config flag, the mandatory justification template) is in references/osv-scanner-config-and-ci.md.
Step 6 - False-positive triage (MANDATORY)
Suppress via [[IgnoredVulns]] (per-CVE, with ignoreUntil) or [[PackageOverrides]] (per pinned package + ecosystem) in osv-scanner.toml; --severity-threshold is a scan-time filter, not a suppression. Every entry carries a mandatory reason + approver + re-review date. ignoreUntil is enforced - past-due ignores re-surface in the scan results. The suppression-layer table and justification template are in references/osv-scanner-config-and-ci.md.
Step 7 - Exit codes + CI gating
OSV-Scanner exit codes (verify against current docs):
The google/osv-scanner-action GHA (Docker invocation + SARIF upload) is in references/osv-scanner-config-and-ci.md.
Step 8 - License-checking (adjacent feature)
OSV-Scanner has experimental license-summary support:
osv-scanner --experimental-licenses-summary scan -r .For full license-compliance + scanning, pair with sbom-formats or use a dedicated tool like ScanCode / FOSSology.
Anti-patterns
Limitations
References
Deep reference for the osv-scanner SKILL.md. SKILL.md keeps install, the recursive/per-lockfile scan, output formats, SBOM input, exit codes, and a minimal ignore snippet; this file holds the full osv-scanner.toml schema, the justification template, and the GitHub Actions workflow.
Full osv-scanner.toml schema
Point the scanner at a config with --config (osv-usage (opens in new window)):
osv-scanner --config ./my-osv-scanner-config.toml scan -r .# osv-scanner.toml
[[IgnoredVulns]]
id = "CVE-2024-1234"
ignoreUntil = 2026-12-15T00:00:00Z
reason = "Reachability analysis confirms unreachable; tracked in JIRA-1234"
[[IgnoredVulns]]
id = "GHSA-xxxx-yyyy-zzzz"
ignoreUntil = 2026-09-30T00:00:00Z
reason = "Vendor-supplied; pin to current version pending Q3 upgrade"
[[PackageOverrides]]
name = "lodash"
version = "4.17.20"
ecosystem = "npm"
ignore = true # Suppress all vulns in this exact pinned version
reason = "Test fixture; not in production dependency graph"Suppression layers and justification
Three suppression layers:
| Mechanism | Where | Use |
|---|---|---|
[[IgnoredVulns]] in osv-scanner.toml (with ignoreUntil) | Repo root | Per-CVE; auditable in git |
[[PackageOverrides]] in osv-scanner.toml | Repo root | Per-package version + ecosystem |
--severity-threshold filter (when supported by version) | CI flag | Scan-time filter, not suppression |
Justification is mandatory in osv-scanner.toml:
[[IgnoredVulns]]
id = "CVE-2024-1234"
ignoreUntil = 2026-12-15T00:00:00Z
reason = """
Reachability: function `vulnerable_func` not exported; verified via
git grep + dynamic instrumentation. Issue requires admin context
which is separately controlled.
Approved-by: alice@example.com
Re-review-date: 2026-09-15
"""ignoreUntil is enforced by osv-scanner - past-due ignores are re-surfaced in the scan results. Cadence: every quarter, list all [[IgnoredVulns]] entries + re-validate the reason; expired ones removed, persistent ones escalated to upgrade work.
GitHub Actions CI
The google/osv-scanner-action GHA wraps the Docker invocation + SARIF upload:
jobs:
osv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: google/osv-scanner-action/osv-scanner-action@v1
with:
scan-args: |-
--recursive
--skip-git
--format=sarif
--output=osv.sarif
./
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: osv.sarif }