Here’s 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 call‑stack “entry→sink” traces for relevant code paths (e.g., public handlers → sensitive sinks). * Symbol map concordance: `{ function, file, address-range, Build‑ID, 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/Mach‑O: capture Build‑ID, 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 (in‑toto envelope). Auditors can replay using the published Build‑ID + 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, Build‑ID tuples. * **Canonicalization:** * Resolve PCs → (Build‑ID, 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, Build‑ID, function)` → SBOM component (pkg + version) and to your **Static→Binary proof** entry. * **VEX policy reaction:** * If a CVE’s vulnerable symbol appears in observed stacks **and** matches your static proof: * Auto‑emit 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), * Build‑ID(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 time‑boxed 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 Stella Ops (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 Proof‑of‑Integrity Graph. * **Signals/Excititor**: eBPF sampler daemonset; push collapsed frames with `(image-digest, Build‑ID)` to pipeline. * **Concelier**: resolver service mapping frames → SBOM components + funcproof presence; maintain hot‑symbol index. * **Vexer/Policy Engine**: when hot vulnerable symbol is confirmed, produce signed VEX downgrade; route to **Authority** for policy‑gated actions (quarantine, canary freeze, diff-aware release gate). * **Timeline/Notify**: human‑readable evidence pack: “CVE‑2025‑XXXX observed in `libfoo::parse_hdr` (Build‑ID abc…), 17.3% of CPU in api‑gw@prod between 12:00–14:00 UTC; VEX → affected.” # Data shapes (keep them tiny) * **FuncProof JSON (per binary):** ```json { "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 + Build‑ID resolution; store only canonical frames. 3. **Phase 3 — Correlation & VEX:** map frames ↔ SBOM ↔ funcproof; auto‑downgrade 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 * **Symbol‑level truth**, not just package versions. * **Runtime‑aware VEX** that flips based on evidence, not assumptions. * **Tiny proof objects** make audits fast and air‑gap‑friendly. * **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.