Files
git.stella-ops.org/docs/product-advisories/26-Dec-2025 - Stella Ops vNext - SBOM Spine and Deterministic Evidence.md

7.4 KiB
Raw Blame History

Heres a simple, practical way to think about a SBOMfirst, VEXready supplychain spine and the evidence graph + smartdiff you can build on top of it—starting from zero and ending with reproducible, signed decisions.

SBOMfirst spine (VEXready)

Goal: make the SBOM the canonical graph of “whats inside,” then layer signed evidence (build, scans, policy) so every verdict is portable, replayable, and auditable across registries.

Core choices

  • Canonical graph: treat CycloneDX 1.6 and SPDX 3.x as firstclass. Keep both in sync; normalize component IDs (PURL/CPE), hashes, licenses, and relationships.

  • Attestations: use intoto + DSSE for all lifecycle facts:

    • build (SLSA provenance),
    • scan results (vuln, secrets, IaC, reachability),
    • policy evaluation (allow/deny, risk budgets, exceptions).
  • Storage/transport: publish everything as OCIattached artifacts via OCI Referrers:

    • image:tag → SBOM (spdx/cdx), VEX, SARIF, provenance, policy verdicts, exception notes—each a referrer with media type + signature.
  • Signatures: cosign/sigstore (or your regional crypto: eIDAS/FIPS/GOST/SM) for contentaddressed blobs.

Minimum viable workflow

  1. Build step

    • Produce identical SBOMs in CycloneDX and SPDX.
    • Emit SLSAstyle provenance attestation.
  2. Scan step(s)

    • OS + language deps + container layers; add reachability proofs where possible.
    • Emit one scan attestation per tool (dont conflate).
  3. Policy step

    • Evaluate policies (e.g., OPA/Rego or your lattice rules) against the SBOM graph + scan evidence.
    • Emit a signed policy verdict attestation (pass/fail + reasons + unknowns count).
  4. Publish

    • Push image, then push SBOMs, VEX, scan attestations, policy verdicts as OCI referrers.
  5. Verify / consume

    • Pull the images referrer set; verify signatures; reconstruct graph locally; replay the policy evaluation deterministically.

Data model tips

  • Stable identifiers: PURLs for packages, digests for layers, BuildID for binaries.
  • Edges: component→dependsOn, component→vulnerability, component→evidence(attestation), component→policyClaim.
  • Keep time (asof) and source on every node/edge for replay.

Evidence graph + smartdiff

Goal: persist an explainability graph (findings ↔ components ↔ provenance ↔ policies) and compute signed deltaverdicts on diffs to drive precise impact analysis and quiet noise.

What to store

  • Provenance: who built it, from what, when (commit, builder, materials).
  • Findings: CVEs, misconfigs, secrets, license flags, each with source tool, version, rule, confidence, timestamp.
  • Policies & verdicts: rule set version, inputs hashes, outcome, rationale.
  • Reachability subgraphs: the minimal path proving exploitability (e.g., symbol → function → package → process start).

Smartdiff algorithm (high level)

  • Compare two images (or SBOM graphs) by component identity + version + hash.

  • For each change class:

    • Added/removed/changed component
    • New/cleared/changed finding
    • Changed reachability path
    • Changed policy version/inputs
  • Reevaluate only affected subgraph; produce a Delta Verdict:

    • status: safer / riskequal / riskhigher
    • why: list of netnew reachable vulns, removed reachable vulns, policy/exception impacts
    • evidenceRefs: hashes of attestations used
  • Sign the delta verdict (DSSE) and publish it as an OCI referrer too.

UX essentials

  • Artifact page shows: “Evidence Stack” (SBOM, scans, VEX, policy, provenance) with green checks for signatures.
  • Smartdiff view: left vs right image → “netnew reachable CVEs (+3)”, “downgraded risk (1)” with drilldowns to the exact path/evidence.
  • Explain button: expands to show why a CVE is (not) applicable (feature flag off, code path unreachable, kernel mitigation present, etc.).
  • Replay badge: “Deterministic ” (inputs hashes match; verdict reproducible).

Implementation checklist (teamready)

Pipelines

  • Build: emit SBOM (CDX + SPDX), SLSA provenance (intoto/DSSE), sign all.
  • Scan: OS + language + config + (optional) eBPF/runtime; one attestation per tool.
  • Policy: evaluate rules → signed verdict attestation; include unknowns count.
  • Publish: push all as OCI referrers; enable verification gate on pull/deploy.

Schema & IDs

  • Normalize component IDs (PURL/CPE) + strong hashes; map binaries (BuildID → package).
  • Evidence graph store: Postgres (authoritative) + cache (Valkey) for queries.
  • Index by image digest; maintain asof snapshots for timetravel.

Determinism

  • Lock feeds, rule versions, tool versions; record all input digests.
  • Provide a replay.yaml manifest capturing inputs → expected verdict hash.

Security & sovereignty

  • Pluggable crypto: eIDAS/FIPS/GOST/SM; offline bundle export/import.
  • Airgapped profile: Postgresonly with documented tradeoffs.

APIs & types (suggested media types)

  • application/vnd.cyclonedx+json
  • application/spdx+json
  • application/vnd.in-toto+json; statement=provenance|scan|policy
  • application/vnd.stella.verdict+json (your signed verdict/delta)

Minimal object examples (sketches)

Attestation (scan)

{
  "type": "https://in-toto.io/Statement/v1",
  "predicateType": "https://stella.dev/scan/v1",
  "subject": [{"name": "registry/app@sha256:…", "digest": {"sha256": "..."} }],
  "predicate": {
    "tool": {"name": "scannerX", "version": "1.4.2"},
    "inputs": {"sbom": "sha256:…", "db": "sha256:…"},
    "findings": [{"id": "CVE-2025-1234", "component": "pkg:pypi/xyz@1.2.3", "severity": "HIGH"}]
  }
}

Policy verdict (replayable)

{
  "type": "https://in-toto.io/Statement/v1",
  "predicateType": "https://stella.dev/verdict/v1",
  "subject": [{"name": "registry/app@sha256:…"}],
  "predicate": {
    "policy": {"id": "prod.v1.7", "hash": "sha256:…"},
    "inputs": {"sbom": "sha256:…", "scans": ["sha256:…","sha256:…"]},
    "unknowns": 2,
    "decision": "allow", 
    "reasons": [
      "CVE-2025-1234 not reachable (path pruned)",
      "License policy ok"
    ]
  }
}

Delta verdict (smartdiff)

{
  "predicateType": "https://stella.dev/delta-verdict/v1",
  "predicate": {
    "from": "sha256:old", "to": "sha256:new",
    "impact": "risk-higher",
    "changes": {
      "componentsAdded": ["pkg:apk/openssl@3.2.1-r1"],
      "reachableVulnsAdded": ["CVE-2025-2222"]
    },
    "evidenceRefs": ["sha256:scanA", "sha256:policyV1"]
  }
}

Operating rules you can adopt today

  • Everything is evidence. If it influenced a decision, its an attestation you can sign and attach.
  • Same inputs → same verdict. If not, treat it as a bug.
  • Unknowns budgeted by policy. E.g., “fail prod if unknowns > 0; warn in dev.”
  • Diffs decide deployments. Gate on the delta verdict, not raw CVE counts.
  • Portable by default. If you move registries, your decisions move with the image via referrers.

If you want, I can turn this into starter repos (SBOM/attestation schemas, OCIreferrer publish/verify CLI, and a smartdiff service stub in .NET 10) so your team can plug it into your current pipelines without a big rewrite.