Skip to content

feat(oci): OCI registry ingestion + cosign verification for SBOM/VEX artifacts #192

Description

@jimmytoenners

Hi — I'd like to propose a new oci command family for sbom-tools. Before I open a PR I wanted to check whether you're open to the direction and the dependency footprint.

The gap

sbom-tools today is file-in: every command takes a local path (src/pipeline/parse.rs). To analyse the SBOM/VEX attached to a container image, an operator has to dance between cosign verify, cosign download sbom, cosign verify-attestation, then sbom-tools vex apply — each with its own flags and exit codes. Crucially, that manual flow never checks that the attestation's subject digest matches the image actually analysed ("right SBOM, wrong image"). I think that check belongs in the tool.

What it adds

Three subcommands, all behind an off-by-default oci feature:

sbom-tools oci pull   <REF> [OPTIONS]   # fetch + verify + write SBOM/VEX to disk
sbom-tools oci verify <REF> [OPTIONS]   # verify only; report + exit code (SARIF supported)
sbom-tools oci report <REF> [OPTIONS]   # pull + verify + enrich + vuln picture (one-shot)
  • Discovery: OCI 1.1 Referrers API + cosign tag-scheme fallback + plain artifacts
  • Cosign verification: key-based and keyless (Fulcio + Rekor inclusion + identity/issuer matching, per-attestation Fulcio chain validation)
  • Digest binding: in-toto subject digest is checked against the resolved image digest — the check the manual flow omits
  • New exit code 6 OCI_VERIFICATION_FAILED — next free after the existing 0–5
  • Pipeline unchanged: src/oci/ is an input resolver that materialises verified blobs as local files and hands them off; diff / view / quality / validate / vex / enrichment all "just work" on registry inputs

Full design doc (architecture, discovery matrix, SARIF rule catalog, test plan, risk register) on my fork:
https://github.com/jimmytoenners/sbom-tools/blob/feature/oci-registry-ingestion/docs/oci-verify-plan.md

Things you'll probably want to push back on

  1. New deps. sigstore = 0.13, oci-client = 0.15, tokio, plus base64 / x509-cert / pem / rustls-pki-types pinned directly (transitive via sigstore, promoted so the lifetime-erased ManualTrustRoot construction stays in our control). Sigstore is the only mature Rust path for cosign verification; pre-1.0, so versions are pinned with =x.y.z. All of this is gated behind --features ocicargo build with default features pulls in zero new deps.
  2. MSRV. Stays on Rust 1.88 — the pinned sigstore 0.13 + oci-client 0.15 compile clean on the current toolchain. No MSRV bump.
  3. async leakage. tokio is confined to src/oci/client.rs + verify.rs behind a block_on bridge. No async on any signature outside src/oci/. Same shape reqwest::blocking already uses internally under enrichment.
  4. CI surface. Adds two jobs: cargo build/test --features oci and cargo deny check --features oci. Default-feature jobs and the MSRV job are unchanged.

Status

Already implemented on a branch on my fork — 13 commits, your CI gates pass locally:

cargo fmt --check
cargo clippy --all-features -- -D warnings
cargo test --all-features

Branch: jimmytoenners:feature/oci-registry-ingestion

Each commit is a self-contained step (scaffold → registry fetch → DSSE/in-toto unwrap → key-based verify → keyless → per-attestation Fulcio chain → --trust-root + --insecure-ignore-tlog → SARIF emitter → docs). Happy to squash per your default; the per-commit history is just to make review easier.

How would you prefer this?

CONTRIBUTING.md says "keep PRs focused on a single change." I can do either:

  • (a) One PR — the whole branch as a single feature. Easier to land as a cohesive unit, but ~13 commits' worth of review surface.
  • (b) Split into 4 PRs, matching the phases in the design doc:
    1. Scaffold + registry fetch + --no-verify path
    2. Key-based cosign verification (image sig + DSSE envelopes + digest binding)
    3. Keyless (Fulcio + Rekor + identity + per-attestation chain) + --trust-root
    4. SARIF emitter + docs

I'll do whichever you prefer. Also entirely fine if the answer is "not a direction we want" or "slim it first" — better to find out before opening the PR than after.

Thanks!

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions