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
- 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 oci — cargo build with default features pulls in zero new deps.
- MSRV. Stays on Rust 1.88 — the pinned
sigstore 0.13 + oci-client 0.15 compile clean on the current toolchain. No MSRV bump.
- 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.
- 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:
- Scaffold + registry fetch +
--no-verify path
- Key-based cosign verification (image sig + DSSE envelopes + digest binding)
- Keyless (Fulcio + Rekor + identity + per-attestation chain) +
--trust-root
- 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!
Hi — I'd like to propose a new
ocicommand 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 betweencosign verify,cosign download sbom,cosign verify-attestation, thensbom-tools vex apply— each with its own flags and exit codes. Crucially, that manual flow never checks that the attestation'ssubjectdigest 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
ocifeature:subjectdigest is checked against the resolved image digest — the check the manual flow omits6 OCI_VERIFICATION_FAILED— next free after the existing0–5src/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 inputsFull 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
sigstore = 0.13,oci-client = 0.15,tokio, plusbase64/x509-cert/pem/rustls-pki-typespinned directly (transitive via sigstore, promoted so the lifetime-erasedManualTrustRootconstruction 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 oci—cargo buildwith default features pulls in zero new deps.sigstore 0.13+oci-client 0.15compile clean on the current toolchain. No MSRV bump.tokiois confined tosrc/oci/client.rs+verify.rsbehind ablock_onbridge. Noasyncon any signature outsidesrc/oci/. Same shapereqwest::blockingalready uses internally underenrichment.cargo build/test --features ociandcargo 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:
Branch:
jimmytoenners:feature/oci-registry-ingestionEach 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.mdsays "keep PRs focused on a single change." I can do either:--no-verifypath--trust-rootI'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!