# Evidence Pipeline Consolidation - Sprint Plan **Created**: 2026-01-11 **Status**: Planning **Owner**: Stella Ops Engineering --- ## Executive Summary This document consolidates the product advisory for "reachability + runtime + patch-diff" evidence into actionable sprints. **Critical finding: 70-80% of the proposed infrastructure already exists in Stella Ops.** The work is primarily about: 1. **Connecting** existing components 2. **Implementing** missing adapters (runtime capture, binary diff) 3. **Orchestrating** CVE-to-verdict flow 4. **Enhancing** evidence-to-VEX integration --- ## Existing Infrastructure Analysis ### Already Implemented (No New Development Needed) | Component | Location | Status | |-----------|----------|--------| | **ReachabilityAnalyzer** | `Scanner.CallGraph/Analysis/` | Complete - BFS traversal, deterministic paths | | **ReachabilityLattice** | `Scanner.Emit/Reachability/` | Complete - Score-based verdict merge | | **ReachabilityStackEvaluator** | `Scanner.Reachability/Stack/` | Complete - 3-layer evaluation | | **ReachabilityWitnessDsseBuilder** | `Scanner.Reachability/Attestation/` | Complete - in-toto statements | | **ReachabilityEvidence** | `Evidence.Bundle/` | Complete - FunctionPath, ImportChain | | **ReachabilityResult** | `Scanner.Reachability/Witnesses/` | Complete - PathWitness/SuppressionWitness | | **EvidenceBundle** | `Evidence.Bundle/` | Complete - Multi-type evidence container | | **EvidenceDbContext** | `Evidence.Persistence/` | Complete - Postgres persistence | | **RuntimeEvidence models** | `Scanner.Analyzers.Native/RuntimeCapture/` | Complete - Session, LoadEvent, Edge | | **DotNetCallGraphExtractor** | `Scanner.CallGraph/Extraction/DotNet/` | Complete - .NET call graphs | | **DotNetReachabilityLifter** | `Scanner.Reachability/Lifters/` | Complete - Lift to union model | | **CVE-Symbol mapping schema** | `devops/database/migrations/` | Complete - Tables and indexes | | **DSSE signing** | `Scanner.Worker/Processing/Surface/` | Complete - IDsseEnvelopeSigner | | **Multi-language analyzers** | `Scanner.Analyzers.Lang.*` | Complete - 10 languages | ### Needs Implementation | Component | Gap | Priority | |-----------|-----|----------| | **Runtime Capture Adapters** | Models exist, no Tetragon/ETW/dtrace adapters | P0 | | **CVE-to-Sink Orchestrator** | Schema exists, no service to trigger analysis | P0 | | **VEX Verdict Emitter** | Reachability results don't flow to VEX | P0 | | **Binary Patch Diff** | No B2R2-based patch verification | P1 | | **Evidence Job Queue** | Manual triggering, no automated pipeline | P1 | ### Architecture Truth Table ``` Verdict Decision (ReachabilityStackEvaluator): | L1 (Static) | L2 (Binary) | L3 (Runtime) | Verdict | |-------------|-------------|--------------|---------------------| | Reachable | Resolved | Not Gated | Exploitable | | Reachable | Resolved | Unknown | LikelyExploitable | | Reachable | Resolved | Gated | Unreachable | | Reachable | Unknown | Unknown | PossiblyExploitable | | Reachable | Not Resolved| * | Unreachable | | Not Reach | * | * | Unreachable | | Unknown | * | * | Unknown | ``` --- ## Sprint Plan ### Phase 0: Foundation Validation (1 Sprint - 2 weeks) **Goal**: Validate existing components work end-to-end with manual triggering. #### Sprint S0.1: Smoke Test Existing Pipeline | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S0.1.1 | Write integration test: DotNetCallGraphExtractor -> ReachabilityAnalyzer -> ReachabilityResult | Backend | 2d | | S0.1.2 | Write integration test: ReachabilityStackEvaluator with mock L1/L2/L3 | Backend | 1d | | S0.1.3 | Write integration test: ReachabilityWitnessDsseBuilder -> signed envelope | Backend | 1d | | S0.1.4 | Validate CVE-symbol mapping schema with real CVE data (Log4Shell, Spring4Shell) | Backend | 1d | | S0.1.5 | Document gaps found during validation | Tech Lead | 1d | **Exit Criteria**: - All integration tests pass - Gap analysis document produced - Existing components confirmed working --- ### Phase 1: CVE-to-Verdict Orchestration (2 Sprints - 4 weeks) **Goal**: Enable "given CVE + image, produce reachability verdict" flow. #### Sprint S1.1: CVE-Symbol Mapping Service | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S1.1.1 | Create `ICveSymbolMappingService` interface | Backend | 0.5d | | S1.1.2 | Implement `PostgresCveSymbolMappingRepository` using existing schema | Backend | 2d | | S1.1.3 | Create `CveSymbolMappingLoader` - import from OSV/NVD advisories | Backend | 3d | | S1.1.4 | Create `PatchAnalysisExtractor` - parse git diffs for symbols | Backend | 2d | | S1.1.5 | Wire to Concelier - enrich CVE data with sink mappings | Backend | 2d | **Schema** (exists in `reachability.cve_symbol_mappings`): ```sql -- Already implemented, no changes needed ``` **Interface**: ```csharp public interface ICveSymbolMappingService { Task> GetSinksForCveAsync( string cveId, string purl, CancellationToken ct); Task HasMappingAsync(string cveId, CancellationToken ct); } ``` #### Sprint S1.2: Reachability Evidence Job | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S1.2.1 | Create `ReachabilityEvidenceJob` record for queue | Backend | 1d | | S1.2.2 | Create `ReachabilityEvidenceJobExecutor` in Scanner.Worker | Backend | 3d | | S1.2.3 | Wire to existing `CallGraphSnapshot` -> `ReachabilityAnalyzer` | Backend | 2d | | S1.2.4 | Emit `ReachabilityStack` with L1 analysis | Backend | 1d | | S1.2.5 | Store result in `EvidenceDbContext` | Backend | 1d | | S1.2.6 | Add WebService endpoint: `POST /api/reachability/analyze` | Backend | 1d | **Job Flow**: ``` Request: { imageDigest, cveId, purl } -> Lookup sinks from CveSymbolMappingService -> Get CallGraphSnapshot from cache/compute -> Run ReachabilityAnalyzer with sinks -> Build ReachabilityStack (L1 only initially) -> Store EvidenceBundle -> Return ReachabilityResult ``` --- ### Phase 2: VEX Integration (2 Sprints - 4 weeks) **Goal**: Reachability results automatically influence VEX status. #### Sprint S2.1: Verdict-to-VEX Bridge | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S2.1.1 | Create `IVexStatusDeterminer` interface | Backend | 0.5d | | S2.1.2 | Implement verdict -> VEX status mapping | Backend | 2d | | S2.1.3 | Create `ReachabilityVexJustificationBuilder` | Backend | 2d | | S2.1.4 | Wire to VexHub - emit VEX with evidence references | Backend | 3d | | S2.1.5 | Add evidence URI to VEX justification | Backend | 1d | **Mapping Logic**: ```csharp public VexStatus MapVerdictToVexStatus(ReachabilityVerdict verdict) => verdict switch { ReachabilityVerdict.Exploitable => VexStatus.Affected, ReachabilityVerdict.LikelyExploitable => VexStatus.Affected, ReachabilityVerdict.PossiblyExploitable => VexStatus.UnderInvestigation, ReachabilityVerdict.Unreachable => VexStatus.NotAffected, ReachabilityVerdict.Unknown => VexStatus.UnderInvestigation, _ => VexStatus.UnderInvestigation }; ``` #### Sprint S2.2: Automated VEX Refresh | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S2.2.1 | Create `VexRefreshTrigger` - on new CVE or new scan | Backend | 2d | | S2.2.2 | Implement incremental VEX update (don't regenerate all) | Backend | 3d | | S2.2.3 | Add `vex_evidence_links` table for evidence->VEX tracking | Backend | 1d | | S2.2.4 | Create VexLens query: "show VEX decisions with evidence" | Backend | 2d | | S2.2.5 | Add UI endpoint for evidence drill-down | Frontend | 2d | --- ### Phase 3: Runtime Observation (2 Sprints - 4 weeks) **Goal**: Implement Layer 3 (Runtime Gating) with actual runtime data. #### Sprint S3.1: Runtime Capture Infrastructure | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S3.1.1 | Create `IRuntimeCaptureAdapter` interface | Backend | 1d | | S3.1.2 | Implement `TetragonAdapter` for Linux/Kubernetes | Backend | 5d | | S3.1.3 | Implement `EtwAdapter` for Windows | Backend | 3d | | S3.1.4 | Create `RuntimeEvidenceCollector` service | Backend | 2d | | S3.1.5 | Wire to existing `RuntimeEvidence` models | Backend | 1d | **Interface** (leverages existing models): ```csharp public interface IRuntimeCaptureAdapter { Task StartSessionAsync( RuntimeCaptureOptions options, CancellationToken ct); Task StopSessionAsync(string sessionId, CancellationToken ct); IAsyncEnumerable StreamEventsAsync( string sessionId, CancellationToken ct); } // Uses existing: RuntimeCaptureSession, RuntimeLoadEvent, RuntimeEvidence // from StellaOps.Scanner.Analyzers.Native.RuntimeCapture ``` #### Sprint S3.2: Runtime-to-Stack Integration | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S3.2.1 | Create `RuntimeEvidenceCorrelator` - map events to symbols | Backend | 3d | | S3.2.2 | Implement Layer 3 population from runtime evidence | Backend | 2d | | S3.2.3 | Update `ReachabilityStackEvaluator` to use real L3 data | Backend | 2d | | S3.2.4 | Add runtime evidence to DSSE attestation | Backend | 1d | | S3.2.5 | Create `RuntimeObservationEvidence` bundle type | Backend | 1d | **Tetragon Policy** (example for container runtime): ```yaml apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: stella-vuln-tracing spec: kprobes: - call: "security_file_open" selectors: - matchActions: - action: Sigkill # or just observe args: - index: 0 type: "file" ``` --- ### Phase 4: Binary Patch Verification (2 Sprints - 4 weeks) **Goal**: Implement patch verification for "distro says fixed" cases. #### Sprint S4.1: B2R2 Patch Diff Engine | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S4.1.1 | Create `IBinaryDiffService` interface | Backend | 1d | | S4.1.2 | Implement `B2R2BinaryDiffService` using existing B2R2 deps | Backend | 5d | | S4.1.3 | Create function similarity matching (basic) | Backend | 3d | | S4.1.4 | Create `PatchDiffEvidence` model | Backend | 1d | | S4.1.5 | Add to evidence bundle | Backend | 1d | **Note**: B2R2 is already in dependencies for binary lifting. No Ghidra needed. ```csharp public interface IBinaryDiffService { Task DiffAsync( Stream vulnerableBinary, Stream patchedBinary, IReadOnlyList targetSymbols, CancellationToken ct); } public record PatchDiffResult( bool IsPatched, IReadOnlyList ChangedFunctions, double SimilarityScore, string DiffSummary); ``` #### Sprint S4.2: Patch Verification Pipeline | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S4.2.1 | Create `PatchVerificationJob` for queue | Backend | 1d | | S4.2.2 | Implement binary fetching from registry/distro | Backend | 3d | | S4.2.3 | Wire to Layer 2 (Binary Resolution) | Backend | 2d | | S4.2.4 | Add patch verification to verdict logic | Backend | 2d | | S4.2.5 | Create "backport detected" VEX justification | Backend | 1d | --- ### Phase 5: DSSE Attestation & Policy Gate (1 Sprint - 2 weeks) **Goal**: All evidence signed, policy gates enforce requirements. #### Sprint S5.1: Attestation Pipeline | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S5.1.1 | Create `ReachabilityAttestationPublisher` | Backend | 2d | | S5.1.2 | Wire to Authority for real signing (replace deterministic fallback) | Backend | 2d | | S5.1.3 | Create `PolicyGateEvaluator` using attestations | Backend | 3d | | S5.1.4 | Add Rekor-compatible logging (optional) | Backend | 2d | | S5.1.5 | Create attestation verification endpoint | Backend | 1d | **Policy Example**: ```yaml # Release gate policy gates: - name: reachability-evidence require: - predicateType: "https://stella.ops/reachabilityWitness/v1" conditions: - verdict: ["Unreachable", "Unknown"] # Block Exploitable - signed: true ``` --- ### Phase 6: UI & Observability (1 Sprint - 2 weeks) **Goal**: Make evidence visible and actionable. #### Sprint S6.1: Evidence UI | Task ID | Task | Owner | Effort | |---------|------|-------|--------| | S6.1.1 | Add "Evidence" tab to finding detail view | Frontend | 3d | | S6.1.2 | Visualize call path (entry -> sink) | Frontend | 2d | | S6.1.3 | Show runtime observation timeline | Frontend | 2d | | S6.1.4 | Display patch diff summary | Frontend | 1d | | S6.1.5 | Add DSSE signature verification badge | Frontend | 1d | --- ## Database Migrations Required ### New Tables ```sql -- Runtime observation storage (extends existing RuntimeEvidence model) CREATE TABLE IF NOT EXISTS reachability.runtime_observations ( observation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), scan_id UUID NOT NULL, image_digest TEXT NOT NULL, session_id TEXT NOT NULL, -- Observation data symbol_name TEXT, observed_at TIMESTAMPTZ NOT NULL, load_type TEXT, process_id INTEGER, -- Correlation correlated_cve_id TEXT, correlated_finding_id UUID, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_runtime_obs_image ON reachability.runtime_observations(image_digest); CREATE INDEX idx_runtime_obs_symbol ON reachability.runtime_observations(symbol_name); -- VEX-Evidence linkage CREATE TABLE IF NOT EXISTS reachability.vex_evidence_links ( link_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), vex_document_id UUID NOT NULL, evidence_bundle_id UUID NOT NULL, evidence_type TEXT NOT NULL, linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_vex_evidence_vex ON reachability.vex_evidence_links(vex_document_id); CREATE INDEX idx_vex_evidence_bundle ON reachability.vex_evidence_links(evidence_bundle_id); ``` --- ## Key Interfaces Summary ### Core Services to Implement ```csharp // CVE-to-Sink mapping public interface ICveSymbolMappingService { Task> GetSinksForCveAsync(string cveId, string purl, CancellationToken ct); } // Runtime capture public interface IRuntimeCaptureAdapter { Task StartSessionAsync(RuntimeCaptureOptions options, CancellationToken ct); IAsyncEnumerable StreamEventsAsync(string sessionId, CancellationToken ct); } // Binary diff public interface IBinaryDiffService { Task DiffAsync(Stream vulnerable, Stream patched, IReadOnlyList symbols, CancellationToken ct); } // VEX integration public interface IVexStatusDeterminer { VexStatus DetermineStatus(ReachabilityVerdict verdict); VexJustification BuildJustification(ReachabilityStack stack, IReadOnlyList evidenceUris); } // Evidence job public interface IReachabilityEvidenceJobExecutor { Task ExecuteAsync(ReachabilityEvidenceJob job, CancellationToken ct); } ``` --- ## Risk Assessment | Risk | Mitigation | Owner | |------|------------|-------| | Tetragon requires privileged container | Provide fallback to log-based observation | Platform | | Binary diff performance on large binaries | Queue-based processing with timeouts | Backend | | CVE-symbol mapping accuracy | Confidence scores, manual curation workflow | Security | | Runtime observation overhead | Sampling, targeted policies | Platform | --- ## Success Metrics | Metric | Target | Measurement | |--------|--------|-------------| | Reachability evidence coverage | 80% of high/critical CVEs | Evidence bundle count | | Verdict accuracy (vs manual triage) | 90% | Audit sample | | VEX auto-population rate | 60% of findings | VEX with evidence links | | Runtime observation latency | < 5s to verdict | P95 latency | --- ## Timeline Summary | Phase | Sprints | Duration | Focus | |-------|---------|----------|-------| | Phase 0 | 1 | 2 weeks | Validation | | Phase 1 | 2 | 4 weeks | CVE-to-Verdict | | Phase 2 | 2 | 4 weeks | VEX Integration | | Phase 3 | 2 | 4 weeks | Runtime | | Phase 4 | 2 | 4 weeks | Binary Diff | | Phase 5 | 1 | 2 weeks | Attestation | | Phase 6 | 1 | 2 weeks | UI | | **Total** | **11** | **22 weeks** | | --- ## Appendix: Existing Code References ### Reachability Stack (3-Layer Model) - `Scanner.Reachability/Stack/ReachabilityStack.cs` - Stack model - `Scanner.Reachability/Stack/ReachabilityStackEvaluator.cs` - Verdict logic - `Scanner.Reachability/Stack/ReachabilityLayer1.cs` - Static analysis layer - `Scanner.Reachability/Stack/ReachabilityLayer2.cs` - Binary resolution layer - `Scanner.Reachability/Stack/ReachabilityLayer3.cs` - Runtime gating layer ### Evidence Models - `Evidence.Bundle/ReachabilityEvidence.cs` - Reachability proof - `Evidence.Bundle/EvidenceBundle.cs` - Container - `Evidence.Bundle/CallStackEvidence.cs` - Call stack trace - `Evidence.Bundle/DiffEvidence.cs` - Diff proof ### Attestation - `Scanner.Reachability/Attestation/ReachabilityWitnessDsseBuilder.cs` - DSSE builder - `Scanner.Reachability/Attestation/ReachabilityWitnessStatement.cs` - Statement model - `Scanner.Reachability/Attestation/ReachabilityWitnessPublisher.cs` - Publisher ### Call Graph - `Scanner.CallGraph/Analysis/ReachabilityAnalyzer.cs` - BFS analysis - `Scanner.CallGraph/Extraction/DotNet/DotNetCallGraphExtractor.cs` - .NET extraction ### Runtime (Models Only) - `Scanner.Analyzers.Native/RuntimeCapture/RuntimeEvidence.cs` - Models - `Scanner.Analyzers.Native/RuntimeCapture/RuntimeCaptureOptions.cs` - Options ### Lattice - `Scanner.Emit/Reachability/ReachabilityLattice.cs` - Score-based merge