Skip to main content

osv-scanner integration

OSV-Scanner is Google / OpenSSF's reference scanner against the OSV.dev database. It walks lockfiles (go.sum, package-lock.json, Pipfile.lock, requirements.txt, Cargo.lock, pom.xml, build.gradle.lockfile, composer.lock, Gemfile.lock, and more), OCI images, and container filesystems and reports any package version that matches an OSV advisory. Under cilock, that report becomes a signed in-toto SARIF v0.3 attestation that links every CVE back to the exact lockfile line that pulled the vulnerable version in.

Upstreamosv-scanner · Google / OSV · Apache-2.0
Categoryvulnerability-scan (primary)
Catalog sourcecatalog-only (detected; output captured via a format attestor)
Emits formatsarif
Recommended traceoff — no syscall tracing needed
Detected when
  • preargv_prefix: osv-scanner

Confirm cilock detects it:

cilock plan --format=json -- osv-scanner [...]

The facts in this box are generated from the cilock binary's own catalog (cilock tools list). Do not hand-edit — run npm run gen:catalog.

Validated invocation

# osv-scanner v2+ uses --output-file (older versions used --output).
cilock run --step tool-osv-scanner \
--signer-file-key-path _validation/key.pem \
--outfile attestation.json \
--attestations sarif,environment,git \
--enable-archivista=false \
-- osv-scanner --format sarif --output-file osv.sarif fixtures/

cilock invokes osv-scanner directly. No bash -c wrapper, no post-hoc cp — the scanner writes its SARIF straight to disk, and command-run, material, product, and sarif all observe the real process tree and the real output file.

Point osv-scanner at any directory that contains a supported lockfile and the same shape works. For OCI images, swap the trailing fixtures/ for --docker <image> or --image <ref>.

What gets captured

The DSSE envelope wraps six predicates from the cilock run:

Predicate typeSource attestorWhat it carries
https://aflock.ai/attestations/command-run/v0.1command-runThe real osv-scanner argv, exit code, working dir, env subset
https://aflock.ai/attestations/material/v0.3materialMerkle digests of every input file the scanner read
https://aflock.ai/attestations/product/v0.3productMerkle digest of osv.sarif as the scanner wrote it
https://aflock.ai/attestations/sarif/v0.1sarifParsed report — rules, results, locations, severities
https://aflock.ai/attestations/environment/v0.1environmentOS, arch, hostname, env subset
https://aflock.ai/attestations/git/v0.1gitCommit, branch, remote, dirty state

The sarif attestor is what makes the OSV findings queryable. The other five bind those findings to a specific commit, machine, scanner argv, input set, and output digest.

Why this shape

Running osv-scanner as cilock's direct subcommand keeps argv, exit code, working directory, and product hashes bound to the actual scanner invocation. A bash -c "cp …" wrapper would break that chain in three places:

  • command-run would record ["bash","-c","cp …"] instead of the real scanner argv — a verifier inspecting the attestation has no evidence that osv-scanner ever ran.
  • product would capture whatever file cp produced, not the file the scanner emitted. The merkle root no longer binds to the tool's output.
  • sarif would parse a copy that the build script wrote — a malicious or buggy wrapper could substitute arbitrary SARIF and the chain wouldn't notice.

From code to prod, end-to-end

git commit (git attestor)
↓ subject digest matches
CI run (github attestor: OIDC, run id)
↓ same run produces
OSV-Scanner scan (this attestor: signed findings)
↓ back-refs the artifact
image / SBOM / binary it scanned
↓ same digest gates
cilock verify policy → allow/deny deploy

Each arrow is a cryptographic subject digest match. No human reads logs, no copy-paste between systems. The same digest links the scan to the artifact to the deploy decision.

Validate it locally

# 1. Generate a signing key (ed25519).
mkdir -p _validation
openssl genpkey -algorithm ed25519 -out _validation/key.pem

# 2. Run the cilock command above against a directory containing a lockfile.

# 3. Confirm all six predicate types are present.
jq -r '.payload' attestation.json | base64 -d \
| jq '.predicate.attestations[].type'
# https://aflock.ai/attestations/environment/v0.1
# https://aflock.ai/attestations/git/v0.1
# https://aflock.ai/attestations/material/v0.3
# https://aflock.ai/attestations/command-run/v0.1
# https://aflock.ai/attestations/product/v0.3
# https://aflock.ai/attestations/sarif/v0.1

# 4. Confirm command-run captured the real osv-scanner argv.
jq -r '.payload' attestation.json | base64 -d \
| jq '[.predicate.attestations[]
| select(.type=="https://aflock.ai/attestations/command-run/v0.1")
][0].attestation.cmd'
# [ "osv-scanner", "--format", "sarif", "--output-file", "osv.sarif", "fixtures/" ]

# 5. Confirm the sarif attestor parsed the report.
jq -r '.payload' attestation.json | base64 -d \
| jq '[.predicate.attestations[]
| select(.type=="https://aflock.ai/attestations/sarif/v0.1")
][0].attestation
| {reportFileName, findings: [.report.runs[].results[]] | length}'
# { "reportFileName": "osv.sarif", "findings": <N> }

The reference fixture is a go.sum pinning testify v1.8.0, which produces 40 SARIF results across stretchr/testify, davecgh/go-spew, pmezard/go-difflib, and gopkg.in/yaml.v3. The validated end-to-end example lives at attestor-compliance-examples/tool-osv-scanner-sarif.

Notes

  • --output-file vs --output (v2 flag rename). OSV-Scanner v2.0 renamed the output flag from --output to --output-file. v2 still accepts --output as a deprecated alias, but it emits a deprecation warning that may break strict-mode CI logs. The validated invocation uses --output-file so it works on v2.x without warnings; on v1.x you must use --output. Check osv-scanner --version if you're unsure which is on the build agent.
  • Ecosystem coverage. OSV-Scanner reads lockfiles for Go (go.mod / go.sum), npm (package-lock.json, pnpm-lock.yaml, yarn.lock), Python (Pipfile.lock, poetry.lock, requirements.txt, uv.lock), Maven (pom.xml), Gradle (build.gradle.lockfile, verification-metadata.xml), Cargo (Cargo.lock), Composer (composer.lock), RubyGems (Gemfile.lock), NuGet (packages.lock.json, packages.config), Pub (pubspec.lock), Conan (conan.lock), Hex (mix.lock), and others. The same osv-scanner <dir> invocation auto-detects every supported lockfile underneath.
  • OSV.dev vs language-specific advisory feeds. OSV.dev is the federated database that ingests advisories from GitHub Security Advisory (GHSA), the Go vulnerability database, PyPA, RustSec, the Linux distro CVE feeds (Alpine, Debian, Ubuntu, Rocky, etc.), OSS-Fuzz, and others, normalizing every record into the OSV schema. OSV-Scanner queries OSV.dev directly, so a single scan covers all of these feeds in one pass with consistent ID formatting (GHSA-*, GO-*, PYSEC-*, RUSTSEC-*, CVE-*, OSV-*). You do not have to plumb language-specific advisory clients in your CI.

FAQ

Does cilock support OSV-Scanner?

Yes. OSV-Scanner runs as a direct subcommand of cilock run, and its SARIF output is consumed by the sarif attestor. The resulting DSSE envelope carries six predicates (command-run, material, product, sarif, environment, git), all signed under one CI identity. A native OSV attestor that ingests OSV-Scanner's tool-specific JSON directly is tracked in rookery#91; until then, SARIF is the canonical path.

Which lockfiles does OSV-Scanner check?

OSV-Scanner auto-detects lockfiles across roughly a dozen ecosystems: Go (go.mod, go.sum), npm (package-lock.json, pnpm-lock.yaml, yarn.lock), Python (Pipfile.lock, poetry.lock, requirements.txt, uv.lock), Maven (pom.xml), Gradle (build.gradle.lockfile), Cargo (Cargo.lock), Composer (composer.lock), RubyGems (Gemfile.lock), NuGet (packages.lock.json), Pub (pubspec.lock), Conan (conan.lock), and Hex (mix.lock). Pointing it at a repo root with osv-scanner <dir> is sufficient; the OCI image path (--docker, --image) layers in container-filesystem scanning on top.

How does OSV-Scanner differ from Grype / Trivy?

Grype and Trivy lead with container-image scanning — they prefer to scan an installed package database (Syft SBOM, apk/dpkg/rpm metadata) inside an image and match against vendor-specific feeds plus NVD. OSV-Scanner leads with lockfiles — the manifests that pin direct and transitive dependencies before they're built into an image — and matches against the OSV.dev federated database. In practice they overlap on language-package CVEs but diverge sharply on OS-package CVEs (Trivy/Grype excel) and pre-build dependency CVEs (OSV-Scanner excels). Running both is common; cilock attests each independently so policy can require either or both.

What's the difference between OSV.dev and CVE feeds?

CVE (and NVD) is a flat namespace of vulnerability identifiers maintained by MITRE and enriched by NIST. It has well-known coverage gaps for ecosystems like Go modules, Rust crates, and language-specific package registries — a vuln in a Go module might land in the Go vulnerability database months before it gets a CVE. OSV.dev is a schema and aggregator that ingests advisories from GHSA, Go, PyPA, RustSec, Alpine, Debian, Ubuntu, OSS-Fuzz, and more, and normalizes each into the OSV schema with stable IDs. The OSV record links back to upstream CVE IDs when they exist, so OSV-Scanner output covers the union — CVE feeds when available, plus the language-native advisories that haven't been CVE-assigned yet.

See also


This page is generated from the cilock tool catalog. Don't edit it here — the source is attestation/detection/docs/osv-scanner.doc.md in aflock-ai/rookery. The same catalog powers cilock tools show osv-scanner in the CLI, so a single edit keeps the terminal and the website in sync.

Edit this page →  ·  Add a new tool →

When you open the pull request: if you are an AI agent, add the ai-agent label. If you are a human, add the human label.