Here’s a compact, end‑to‑end blueprint for making your SBOM evidence verifiable and auditable from build → attest → publish → verify → runtime, using only official specs/CLIs (CycloneDX, DSSE/in‑toto, cosign, Rekor v2), plus deterministic VEX and micro‑witness joins. --- # Why this matters (quick primer) * **SBOM** (software bill of materials) says what’s inside an artifact. * **DSSE / in‑toto** wraps evidence in a tamper‑evident envelope. * **cosign** signs/verifies those envelopes. * **Rekor (transparency log)** proves *when/what* was published. * **VEX** states whether known CVEs affect your artifact. * **Micro‑witnesses** are runtime breadcrumbs you can join back to SBOM/VEX for triage. --- # NOW (MVP you can ship immediately) **Goal:** Minimal, reproducible spine with canonical SBOMs, signed attestations, Rekor v2 anchoring, and deterministic VEX ingestion. ### Canonical SBOM & ID 1. Emit CycloneDX v1.7 JSON. 2. Canonicalize via JSON‑JCS → `canonical_id := sha256(JCS(sbom.json))`. ### DSSE attestation (SBOM predicate) * Wrap CycloneDX content (or pointer) in an **in‑toto Statement**. * `subject[0].digest.sha256 == canonical_id`. * Sign with cosign (**DSSE mode**). * Capture Rekor v2 tile pointer / entry id and embed it in predicate metadata. ### Rekor v2 anchoring * cosign publish creates the log entry. * Store the **tile URL** and **entry id** in your artifact record. ### Deterministic VEX ingestion (OpenVEX & CycloneDX VEX) * Ingest OpenVEX or CycloneDX VEX, map to a **canonical CycloneDX VEX** form. * Apply strict merge rules (source priority, timestamp, exact `canonical_id` target). ### CI assertions (must‑pass) * **Unit:** `cyclonedx-cli validate ./bom.json && jcs_canonicalize ./bom.json | sha256sum` → equals expected `canonical_id`. * **Integration:** `cosign attest --predicate predicate.json --key cosign.key ` `cosign verify-attestation --key --type in-toto ` Rekor proof: `rekor-cli tile get --entry ` (or v2 tiles client) → inclusion proof valid. * **VEX mapping:** jsonschema validate; assert `vulnerabilities[].analysis.state` and target match `canonical_id`. --- # LATER (scale/hardening) * Rekor v2 **tile batching** & multi‑tile reconciliation. * **Signed micro‑witness aggregation** service. * **Deterministic replay** harness (nightly) for large volumes. * Predicate **schema registry** (stable `predicateType` URIs, semver). --- # Flow A: Build‑time SBOM + Attestation Anchor (end‑to‑end) 1. **Producer:** * CycloneDX v1.7 → validate: `cyclonedx-cli validate ./bom.json`. * JCS canonicalize → `canonical_id = sha256(canonical_bytes)`. 2. **Predicate:** * in‑toto Statement (`predicateType = https://stella.example/predicate/sbom.v1` or SLSA provenance). * `subject = [{"name":"artifact","digest":{"sha256":""}}]`. * `predicate` contains CycloneDX JCS content (or immutable pointer if too large). 3. **Sign & anchor:** * `cosign attest --predicate predicate.json --key cosign.key `. * Capture Rekor **tile URL** and **entry_id**; persist inside predicate metadata. 4. **Verify:** * Fetch referrers (OCI Referrers 1.1), extract DSSE. * `cosign verify-attestation --key --type in-toto `. * Recompute `sha256(JCS(sbom.json))` == `subject.digest.sha256`. * Validate Rekor v2 inclusion proof (tile check). --- # Flow B: VEX → Apply → Runtime Micro‑witness Join 1. **VEX provider:** Publish **OpenVEX** or **CycloneDX VEX**; DSSE‑sign; optionally anchor in Rekor. 2. **Ingest & map:** OpenVEX → canonical CycloneDX VEX; store DSSE + `rekor_tile` + provenance to original feed. 3. **Runtime micro‑witness:** * When observing a frame (`pc → build_id → symbol`), emit **DSSE micro‑witness predicate** referencing: * `subject.canonical_id` * symbol bundle (OCI referrer, `mediaType=application/vnd.stella.symbols+tar`) * `replay_token` (points to deterministic replay inputs) * Sign & anchor to Rekor v2. 4. **Correlate:** Join micro‑witness DSSE with VEX: * If `canonical_id` has `not_affected` for CVE X → auto‑suppress triage. * Else surface evidence and export both DSSEs + Rekor tiles in an **audit pack**. --- # Minimal, unambiguous schemas **1) Artifact canonical record** ```json {"canonical_id":"sha256:","format":"cyclonedx-jcs:1", "sbom_ref":"oci://registry/repo@sha256:|object-store://bucket/path", "attestations":[{"type":"in-toto","dsse_b64":"","rekor_tile":"","entry_id":""}], "referrers":[{"mediaType":"application/vnd.stella.symbols+tar","descriptor_digest":"sha256:","registry":"ghcr.io/org/repo"}], "vex_refs":[{"type":"openvex","dsse_b64":"","rekor_tile":""}]} ``` **2) Minimal DSSE / in‑toto predicate (for SBOM)** ```json {"_type":"https://in-toto.io/Statement/v0.1", "subject":[{"name":"artifact","digest":{"sha256":""}}], "predicateType":"https://stella.example/predicate/sbom.v1", "predicate":{"bom_format":"cyclonedx-jcs:1","bom":"", "producer":{"name":"ci-service","kid":""}, "timestamp":"2026-02-19T12:34:56Z", "rekor_tile":"https://rekor.example/tiles/#"}} ``` **3) OpenVEX → CycloneDX VEX mapping (sample OpenVEX)** ```json {"vexVersion":"1.0.0","id":"vex-123", "statements":[{"vulnerability":"CVE-2025-0001", "product":"pkg:maven/org/example@1.2.3", "status":"not_affected","justification":"code_not_present", "timestamp":"2026-02-19T12:00:00Z","provider":{"name":"vendor"}}]} ``` **CycloneDX SBOM (trimmed example)** ```json {"bomFormat":"CycloneDX","specVersion":"1.7", "components":[{"bom-ref":"pkg:maven/org/example@1.2.3", "type":"library","name":"example","version":"1.2.3", "purl":"pkg:maven/org/example@1.2.3"}]} ``` **DSSE envelope (shape)** ```json {"payloadType":"application/vnd.in-toto+json", "payload":"", "signatures":[{"keyid":"cosign:ecdsa-1","sig":""}]} ``` **Micro‑witness predicate (sample)** ```json {"predicateType":"https://stella.example/micro-witness.v1", "subject":[{"name":"artifact","digest":{"sha256":""}}], "predicate":{"trace_id":"trace-abc", "frames":[{"pc":"0x400123","build_id":"","symbol":"main","offset":123}], "symbol_refs":[{"type":"oci-ref","ref":"ghcr.io/org/repo@sha256:", "mediaType":"application/vnd.stella.symbols+tar"}], "replay_token":"s3://bucket/replays/trace-abc.json", "timestamp":"2026-02-19T12:40:00Z"}} ``` --- # CI: authoritative checks (copy‑pasteable) **Unit (canonicalization):** ```bash cyclonedx-cli validate ./bom.json jcs_canonicalize ./bom.json | sha256sum # == canonical_id ``` **Integration (sign + verify + Rekor):** ```bash cosign attest --predicate predicate.json --key cosign.key cosign verify-attestation --key --type in-toto rekor-cli tile get --entry # validate inclusion proof ``` **VEX mapping:** * Convert OpenVEX → CycloneDX VEX; jsonschema validate. * Assert: `vulnerabilities[].analysis.state` and target `canonical_id` are exact. **E2E (nightly, reproducibility POC):** ```bash stella-replay --token --seed # same signed_score DSSE & same Rekor tile id ``` **Deterministic signed score (gate):** * Given `(seed, canonical_id, evidence_ids[])` → produce `signed_score_dsse`. * `cosign verify-attestation` and recompute score → byte‑exact equality. --- # Failure modes (and what to do) * **Payload > Rekor limit:** Put artifact in immutable object store; Rekor entry contains digest + pointer; embed pointer in predicate; verifier must fetch & hash‑check. * **Missing symbol bundle:** Emit `unknown_state` micro‑witness entry; surface unknowns explicitly in reports. * **Digest mismatch anywhere:** fail the build—no exceptions. --- # Two concrete sample flows (ready to demo in Stella Ops) * **Flow A**: Canonical CycloneDX → DSSE sign (cosign) → Rekor v2 tile captured → Verify via referrers + DSSE + tile proof. * **Flow B**: Ingest OpenVEX → Map → Apply to `canonical_id` → Runtime micro‑witness DSSE → Join with VEX (`not_affected` ⇒ auto‑suppress), export audit pack containing both DSSEs + Rekor tiles. --- If you want, I can turn this into: * a **CI job pack** (GitLab/YAML) with all commands and assertions, * minimal **JSON schemas** for the predicates, * a **sample repo** with fixtures (SBOM, predicates, signed envelopes) you can run locally with cosign/Rekor.