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

10 KiB

Stella Ops vNext - SBOM Spine and Deterministic Evidence

Status: IMPLEMENTED — Architecture overview document Date: 2025-12-26 Updated: 2025-12-26 Type: Vision Document / Architecture Summary Implementation: 100% complete in existing infrastructure


Implementation Status

This advisory describes the existing StellaOps architecture. All proposed features are implemented:

Core Infrastructure

Component Implementation Location
SBOM-first canonical graph SbomService module src/SbomService/
CycloneDX 1.6 + SPDX 3.x SbomNormalizationService src/SbomService/.../Services/
in-toto + DSSE attestations Attestor module (6+ predicate types) src/Attestor/
OCI referrers OciReferrerPushClient, OciReferrerDiscovery src/ExportCenter/.../Oci/
Cosign/Sigstore signatures Signer module src/Signer/
Regional crypto (eIDAS/FIPS/GOST/SM) Cryptography module src/Cryptography/
Evidence graph ProofChain library src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/
Smart-diff algorithm DeltaComputationEngine src/__Libraries/StellaOps.DeltaVerdict/
Signed delta verdicts DeltaSigningService src/__Libraries/StellaOps.DeltaVerdict/Signing/
Content-addressed IDs IContentAddressedIdGenerator src/Attestor/__Libraries/.../Identifiers/
Decision digest (Provcache) DecisionDigest, VeriKey src/__Libraries/StellaOps.Provcache/
Merkle proof verification MerkleProofVerifier src/Attestor/__Libraries/.../Merkle/
Deterministic replay ReplaySeed, FrozenEpoch src/__Libraries/StellaOps.Provcache/
PostgreSQL + Valkey All modules Per-module storage

Predicate Types (All Implemented)

Predicate Type URI Implementation
Build Provenance StellaOps.BuildProvenance@1 Attestor.ProofChain
SBOM Attestation StellaOps.SBOMAttestation@1 Attestor.ProofChain
Scan Results StellaOps.ScanResults@1 Attestor.ProofChain
Policy Evaluation StellaOps.PolicyEvaluation@1 Attestor.ProofChain
VEX Attestation StellaOps.VEXAttestation@1 Attestor.ProofChain
Risk Profile Evidence StellaOps.RiskProfileEvidence@1 Attestor.ProofChain

Implementation Checklist (All Complete)

Pipelines:

  • Build: emit SBOM (CDX + SPDX), SLSA provenance (in-toto/DSSE), sign all
  • Scan: OS + language + config; one attestation per tool
  • Policy: evaluate rules -> signed verdict attestation; include unknowns count
  • Publish: push all as OCI referrers; enable verification gate

Schema & IDs:

  • Normalize component IDs (PURL/CPE) + strong hashes
  • Evidence graph store: Postgres (authoritative) + cache (Valkey)
  • Index by image digest; maintain as-of snapshots

Determinism:

  • Lock feeds, rule versions, tool versions; record all input digests
  • Provide replay manifest capturing inputs -> expected verdict hash

Security & Sovereignty:

  • Pluggable crypto: eIDAS/FIPS/GOST/SM; offline bundle export/import
  • Air-gapped profile: Postgres-only with documented trade-offs

Documentation References

  • SbomService Architecture: docs/modules/sbomservice/architecture.md
  • Attestor Architecture: docs/modules/attestor/architecture.md
  • Signer Keyless Guide: docs/modules/signer/guides/keyless-signing.md
  • ProofChain Specification: docs/modules/attestor/proof-chain-specification.md
  • Determinism Gates: docs/testing/determinism-gates.md

Advisory Content (Original Vision)

Here's a simple, practical way to think about a SBOM-first, VEX-ready supply-chain spine and the evidence graph + smart-diff you can build on top of it—starting from zero and ending with reproducible, signed decisions.

SBOM-first spine (VEX-ready)

Goal: make the SBOM the canonical graph of "what's 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 first-class. Keep both in sync; normalize component IDs (PURL/CPE), hashes, licenses, and relationships.
  • Attestations: use in-toto + 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 OCI-attached 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 content-addressed blobs.

Minimum viable workflow:

  1. Build step

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

    • OS + language deps + container layers; add reachability proofs where possible.
    • Emit one scan attestation per tool (don't 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 image's referrer set; verify signatures; reconstruct graph locally; replay the policy evaluation deterministically.

Data model tips:

  • Stable identifiers: PURLs for packages, digests for layers, Build-ID for binaries.
  • Edges: component->dependsOn, component->vulnerability, component->evidence(attestation), component->policyClaim.
  • Keep time (as-of) and source on every node/edge for replay.

Evidence graph + smart-diff

Goal: persist an explainability graph (findings <-> components <-> provenance <-> policies) and compute signed delta-verdicts 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).

Smart-diff 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
  • Re-evaluate only affected subgraph; produce a Delta Verdict:
    • status: safer / risk-equal / risk-higher
    • why: list of net-new 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.
  • Smart-diff view: left vs right image -> "net-new reachable CVEs (+3)", "downgraded risk (-1)" with drill-downs 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).

APIs & types (implemented media types)

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

Existing Implementation Examples

Scan Attestation (StellaOps.ScanResults@1):

{
  "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 (StellaOps.PolicyEvaluation@1):

{
  "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 (stella.dev/delta-verdict/v1):

{
  "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 (adopted)

  • Everything is evidence. If it influenced a decision, it's 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.

Module Purpose Documentation
SbomService SBOM ledger, lineage, versioning docs/modules/sbomservice/
Attestor DSSE, in-toto, Rekor integration docs/modules/attestor/
Signer Keyless + KMS signing docs/modules/signer/
Provcache Decision digest, VeriKey src/__Libraries/StellaOps.Provcache/
DeltaVerdict Smart-diff engine src/__Libraries/StellaOps.DeltaVerdict/
ExportCenter OCI referrer publishing src/ExportCenter/
Policy Lattice evaluation, verdicts src/Policy/