Files
git.stella-ops.org/docs/product-advisories/25-Dec-2025 - Evolving Evidence Models for Reachability.md
2025-12-25 20:15:19 +02:00

6.1 KiB
Raw Blame History

Heres a practical blueprint for linking what you build to what actually runs, and turning that into proof-grade security decisions.

Static → Binary braid (build-time proof of “what functions are inside”)

Goal: Prove exactly which functions/offsets shipped in an artifact—without exposing full source.

  • What to store (per artifact):

    • Minimal callstack “entry→sink” traces for relevant code paths (e.g., public handlers → sensitive sinks).
    • Symbol map concordance: { function, file, address-range, BuildID, debug-id }.
    • Hashes per function-range (e.g., rolling BLAKE3 over .text subranges), plus overall .text/.rodata digests.
  • How to generate:

    • During build, emit:

      • ELF/PE/MachO: capture BuildID, section ranges, and DWARF/CodeView ↔ symbol table mapping.
      • Function-range hashing: disassemble to find prolog/epilog (fallback to symbol boundaries), hash byte ranges.
      • Entry→sink traces: from static CFG or unit/integration tests with instrumentation; serialize as compact spans (start fn, end fn, edge list hash).
  • Proof object (tiny & replayable):

    • { build_id, section_hashes, [ {func: name, addr: start..end, func_hash}, … ], [trace_hashes] }
    • Sign with DSSE (intoto envelope). Auditors can replay using the published BuildID + debug symbols to verify function boundaries without your source.
  • Attach & ship:

    • Publish as an OCI referrers artifact alongside the image (e.g., application/vnd.stellaops.funcproof+json), referenced from SBOM (CycloneDX evidence or SPDX verificationCode extension).
  • Why it matters:

    • When a CVE names a symbol (not just a package version), you can prove whether that symbol (and exact byte-range) is present in your binary.

Runtime → Build braid (production proof of “what code ran”)

Goal: Observe live stacks (cheaply), canonicalize to symbols, and correlate to SBOM components. If a vulnerable symbol appears in hot paths, automatically downgrade VEX posture.

  • Collection (Linux):

    • eBPF sampling for targeted processes/containers; use bpf_get_stackid to capture stack traces (user & kernel) into a perf map with low overhead.
    • Collapse stacks (“frameA;frameB;… count”) à la flamegraph format; include PID, container image digest, BuildID tuples.
  • Canonicalization:

    • Resolve PCs → (BuildID, function, offset) via perf-map-agent/eu-stack, or your own resolver using .note.gnu.build-id + symbol table (prefer debuginfod in lab; ship a slim symbol cache in prod).
    • Normalize language runtimes: Java/.NET/Python frames mapped to package+symbol via runtime metadata; native frames via ELF.
  • Correlate to SBOM:

    • For each frame: map (image-digest, BuildID, function) → SBOM component (pkg + version) and to your Static→Binary proof entry.
  • VEX policy reaction:

    • If a CVEs vulnerable symbol appears in observed stacks and matches your static proof:

      • Autoemit a VEX downgrade (e.g., from not_affected to affected) with DSSE signatures, including runtime evidence:

        • Top stacks where the symbol was hot (counts/percentile),
        • BuildID(s) observed,
        • Timestamp window and container IDs.
    • If symbol is present in build but never observed (and policy allows), maintain or upgrade to not_affected(conditions: not_reachable_at_runtime)—with timeboxed confidence.

  • Controls & SLOs:

    • Sampling budget per workload (e.g., 49 Hz for N minutes per hour), P99 overhead <1%.
    • Privacy guardrails: hash short-lived arguments; only persist canonical frames + counts.

How this lands in StellaOps (concrete modules & evidence flow)

  • Sbomer: add funcproof generator at build (ELF range hashing + entry→sink traces). Emit CycloneDX components.evidence link to funcproof artifact.
  • Attestor: wrap funcproof in DSSE, push as OCI referrer; record in ProofofIntegrity Graph.
  • Signals/Excititor: eBPF sampler daemonset; push collapsed frames with (image-digest, BuildID) to pipeline.
  • Concelier: resolver service mapping frames → SBOM components + funcproof presence; maintain hotsymbol index.
  • Vexer/Policy Engine: when hot vulnerable symbol is confirmed, produce signed VEX downgrade; route to Authority for policygated actions (quarantine, canary freeze, diff-aware release gate).
  • Timeline/Notify: humanreadable evidence pack: “CVE2025XXXX observed in libfoo::parse_hdr (BuildID abc…), 17.3% of CPU in apigw@prod between 12:0014:00 UTC; VEX → affected.”

Data shapes (keep them tiny)

  • FuncProof JSON (per binary):

    {
      "buildId": "ab12…",
      "sections": {".text": "hash", ".rodata": "hash"},
      "functions": [
        {"sym": "foo::bar", "start": "0x401120", "end": "0x4013af", "hash": "…"}
      ],
      "traces": ["hash(edge-list-1)", "hash(edge-list-2)"],
      "meta": {"compiler": "clang-18", "flags": "-O2 -fno-plt"}
    }
    
  • Runtime frame sample (collapsed):

    api-gw@sha256:…;buildid=ab12…;foo::bar+0x3a;net/http::Serve;… 97
    

Rollout plan (short and sweet)

  1. Phase 1 — Build plumbing: implement function-range hashing + DSSE attestation; publish as OCI referrer; link from SBOM.
  2. Phase 2 — Runtime sampler: ship eBPF agent with stack collapse + BuildID resolution; store only canonical frames.
  3. Phase 3 — Correlation & VEX: map frames ↔ SBOM ↔ funcproof; autodowngrade VEX on hot vulnerable symbols; wire policy actions.
  4. Phase 4 — Auditor replay: stella verify --image X downloads funcproof + symbols and replays hashes and traces to prove presence/absence without source.

Why this is a moat

  • Symbollevel truth, not just package versions.
  • Runtimeaware VEX that flips based on evidence, not assumptions.
  • Tiny proof objects make audits fast and airgapfriendly.
  • Deterministic replay: “same inputs → same verdict,” signed.

If you want, I can draft:

  • the DSSE schemas,
  • the eBPF sampler config for Alpine/Debian/RHEL/SLES/Astra,
  • and the exact CycloneDX/SPDX extensions to carry funcproof links.