Here’s a simple way to make vulnerability triage almost noise‑free: combine **static SBOM call‑graph pruning** with **runtime reachability hints** before making a VEX decision. --- # Why this matters (plain English) * Static scanners flag lots of CVEs that your app never calls. * Runtimes (traces, signals) know what actually executed. * Merging the two yields “only the CVEs that matter,” so your VEX is credible and quiet. --- # The hybrid model (static → dynamic → VEX) 1. **Static stage (Sbomer + Feedser assist)** * Build SBOM (CycloneDX/SPDX). * Generate a **per‑package call graph** (library → functions → entrypoints). * Compute a **pruned reachable set** using import graphs, symbol tables, and package metadata. * Output: `reachability_static.json` (component → callable IDs). 2. **Dynamic stage (Signals + Scheduler traces)** * Ingest **runtime traces** (e.g., eBPF/ETW/CLR Profiler/JFR) from Scheduler‑managed jobs. * Normalize to **reachability vectors**: `(component, symbol, freq, last_seen, context)`. * Output: `reachability_runtime.json`. 3. **Decision stage (Vexer)** * For each CVE → vulnerable symbols (from advisories/OSVs, delta‑sigs if available). * If vulnerable symbol ∉ static set → **Not Affected (NA: not reachable)**. * If ∈ static but ∉ runtime → **Under Investigation / Likely NA** (confidence < 1). * If ∈ runtime → **Affected** with confidence and evidence URIs. * Emit **VEX** entries with justification (`not_provided_code_path`, `requires_config`, `fixed`, etc.) and confidence scores. --- # Minimal PoC plan (2 weeks of evening work) **Goal:** Let **Vexer** import runtime reachability vectors from **Scheduler** traces and use them to gate VEX verdicts. ### 0) Contracts (day 1) * **Schema:** * `reachability_static.jsonl`: `{ component, version, symbols:[...] }` * `reachability_runtime.jsonl`: `{ component, symbol, last_seen, count, context:{pid, container, route}}` * **Evidence URIs:** `stella://signals/#symbol=` ### 1) Static extractor (days 2–4) * Sbomer plugin: emit **symbol list + call graph** per artifact. * Start with **.NET**: use Roslyn/IL metadata to map `assembly → type → method`. * Heuristic fallbacks for native: ELF/PE export tables. ### 2) Runtime collector (days 3–6) * Signals module: * .NET CLR Profiler or EventPipe→method enter/leave (sampling ok). * Map back to `(assembly, type, method)` + container tags. * Scheduler: batch traces into **reachability vectors** and push to **Router**. ### 3) Vexer importer (days 6–8) * New `Vexer.Reachability` package: * Merge static + runtime with **component coordinate normalization** (purl). * `bool IsRuntimeReachable(component, symbol)`; `ReachabilityScore 0..1`. ### 4) VEX decision filter (days 8–10) * For each CVE advisory record (from Feedser): vulnerable symbols → lookup. * Decision table: * `Runtime=1` → **Affected (A)**. * `Static=1, Runtime=0` → **NA (Not reachable) with low confidence** unless policy overrides. * `Static=0` → **NA (No code path)**. * Emit CycloneDX VEX or CSAF with **justifications + evidence links**. ### 5) UI & Ops (days 10–14) * **Stella UI:** badge per CVE: `Affected / NA / Probable NA`, hover shows symbols and last‑seen. * **Policy Engine:** org rules like “treat Probable NA as NA after 14 days of runtime with p95 traffic.” * **Notify:** only alert on `Affected` or `Probable NA → A` transitions. --- # Data model & code stubs (illustrative) **Vexer reachability interface** ```csharp public record SymbolRef(string Purl, string Assembly, string Type, string Method); public interface IReachabilityIndex { bool InStatic(SymbolRef s); bool InRuntime(SymbolRef s, TimeSpan since, out int count); double Score(SymbolRef s); // 0..1 weighted by freq/recency } ``` **Decision kernel** ```csharp VexVerdict Decide(Cve cve, IEnumerable vulnSyms, IReachabilityIndex idx) { var anyRuntime = vulnSyms.Any(s => idx.InRuntime(s, TimeSpan.FromDays(14), out _)); var anyStatic = vulnSyms.Any(s => idx.InStatic(s)); return anyRuntime ? VexVerdict.Affected : anyStatic ? VexVerdict.ProbableNotAffected // gated by policy window : VexVerdict.NotAffected; } ``` **Evidence snippet in VEX (CycloneDX)** ```json { "vulnerability": { "id": "CVE-2025-12345" }, "analysis": { "state": "not_affected", "justification": "Vulnerable_code_not_in_execute_path", "response": [ "will_not_fix" ], "detail": "Symbols present but not executed across 14d p95 traffic", "evidence": [ "stella://signals/trace-9f12#symbol=Contoso.Crypto.Rsa::Sign" ] } } ``` --- # Where it plugs into Stella Ops * **Sbomer**: adds call‑graph/symbol export plugin. * **Signals**: runtime tracer + compressor → reachability vectors. * **Scheduler**: tags traces by job/tenant/env; hands them to Router. * **Vexer**: new Reachability importer + decision filter. * **Feedser**: enriches CVEs with symbol hints (from OSV, delta‑sigs). * **Policy Engine**: organization rules for confidence windows. * **Notify/UI/Timeline**: surface verdicts, evidence, and flips over time. --- # Guardrails & edge cases * **Backports:** pair with your existing *binary‑delta signatures* so “fixed but looks vulnerable” becomes **NA (fixed by backport)** even if symbol name matches. * **Lazy paths & feature flags:** allow **context filters** (route, tenant, env) before declaring NA. * **Air‑gapped:** ship traces via offline bundle (`*.signals.zip`) and replay. --- # Next steps I can do now 1. Draft the JSON schemas + purl mapping table. 2. Add the `Vexer.Reachability` package and wire the decision filter. 3. Provide a tiny sample repo (toy service + vulnerable lib) to demo **A → Probable NA → NA** as traffic evolves. If you want, I’ll generate the initial schemas and a .NET tracer checklist so you can drop them into `docs/modules/`.