save progress
This commit is contained in:
@@ -0,0 +1,828 @@
|
||||
# Hybrid Reachability and VEX Integration
|
||||
|
||||
> **Status:** Revised
|
||||
> **Original:** 09-Jan-2026
|
||||
> **Revision:** 09-Jan-2026
|
||||
> **Author:** Product/Engineering
|
||||
> **Epic:** Evidence-First Vulnerability Triage
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory defines the **Hybrid Reachability System** - a unified approach to determining vulnerability exploitability by combining **static call-graph analysis** with **runtime execution evidence** to produce **high-confidence VEX verdicts**. The system enables StellaOps to answer: "Is this CVE actually reachable in my running application?"
|
||||
|
||||
### Key Differentiators
|
||||
|
||||
1. **Evidence-First:** Every VEX verdict backed by auditable, reproducible evidence
|
||||
2. **Lattice-Based Decisions:** Mathematically sound state transitions (never weaker than strongest evidence)
|
||||
3. **Offline-Capable:** Full functionality in air-gapped environments via bundle replay
|
||||
4. **Deterministic:** Same inputs always produce same verdicts (auditable, reproducible)
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Current vulnerability scanners generate excessive noise:
|
||||
- **70-90% of CVEs** flagged are not reachable in actual code paths
|
||||
- **Static-only analysis** over-approximates (flags dead code, feature-flagged paths)
|
||||
- **Runtime-only analysis** under-approximates (misses rarely-executed paths)
|
||||
- **No evidence chain** connecting scanner output to VEX justification
|
||||
|
||||
### Current State in StellaOps
|
||||
|
||||
| Component | Status | Gap |
|
||||
|-----------|--------|-----|
|
||||
| Static call-graph extraction | Implemented (5 languages) | No unified query interface |
|
||||
| ReachGraph storage | Implemented | No runtime evidence merge |
|
||||
| Runtime fact ingestion | Contract defined | No collection agent |
|
||||
| VEX decision emission | Implemented | No reachability-aware justification |
|
||||
| Evidence-weighted scoring | Implemented (6 dimensions) | RTS dimension needs runtime feed |
|
||||
| Signals module | Implemented | Missing runtime trace pipeline |
|
||||
|
||||
---
|
||||
|
||||
## Solution Architecture
|
||||
|
||||
### Conceptual Model
|
||||
|
||||
```
|
||||
STATIC ANALYSIS RUNTIME ANALYSIS
|
||||
| |
|
||||
+--------------------+--------------------+ +---------------+---------------+
|
||||
| | | |
|
||||
v v v v
|
||||
+--------+ +----------+ +---------+ +----------+ +--------+ +--------+
|
||||
| SBOM |--->| CallGraph|--->|ReachGraph| | Runtime |--->| Symbol |--->|Reachability|
|
||||
|Generator| | Extractor| | Service | | Agent | |Normalizer| | Index |
|
||||
+--------+ +----------+ +---------+ +----------+ +--------+ +--------+
|
||||
| |
|
||||
+---------------+---------------+
|
||||
|
|
||||
v
|
||||
+----------------+
|
||||
| VEX Decision |
|
||||
| Filter |
|
||||
+----------------+
|
||||
|
|
||||
v
|
||||
+----------------+
|
||||
| OpenVEX Output |
|
||||
| + Evidence |
|
||||
+----------------+
|
||||
```
|
||||
|
||||
### Component Responsibilities
|
||||
|
||||
#### 1. Reachability Core Library (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/`
|
||||
|
||||
Provides the unified `IReachabilityIndex` interface that facades over:
|
||||
- ReachGraph (static call-graph queries)
|
||||
- Signals (runtime facts)
|
||||
- CVE-Symbol mapping corpus
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityIndex
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if symbol exists in static call graph from any entrypoint.
|
||||
/// </summary>
|
||||
Task<StaticReachabilityResult> QueryStaticAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Check if symbol was observed at runtime within observation window.
|
||||
/// </summary>
|
||||
Task<RuntimeReachabilityResult> QueryRuntimeAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
TimeSpan observationWindow,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Compute hybrid reachability score combining static + runtime evidence.
|
||||
/// </summary>
|
||||
Task<HybridReachabilityResult> QueryHybridAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
HybridQueryOptions options,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Batch query for multiple symbols (CVE-related).
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<HybridReachabilityResult>> QueryBatchAsync(
|
||||
IEnumerable<SymbolRef> symbols,
|
||||
string artifactDigest,
|
||||
HybridQueryOptions options,
|
||||
CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Symbol Canonicalization Service (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/Symbols/`
|
||||
|
||||
Normalizes symbols across different sources:
|
||||
- Static: Roslyn (`Namespace.Type.Method`), ASM (`class.method(descriptor)`)
|
||||
- Runtime: JIT names, ETW method IDs, eBPF uprobe symbols
|
||||
- Native: Mangled C++, demangled, ELF symbols
|
||||
|
||||
```csharp
|
||||
public interface ISymbolCanonicalizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Canonicalize symbol to portable format.
|
||||
/// </summary>
|
||||
CanonicalSymbol Canonicalize(RawSymbol raw, SymbolSource source);
|
||||
|
||||
/// <summary>
|
||||
/// Match two symbols with fuzzy tolerance for minor variations.
|
||||
/// </summary>
|
||||
SymbolMatchResult Match(CanonicalSymbol a, CanonicalSymbol b, MatchOptions options);
|
||||
}
|
||||
|
||||
public sealed record CanonicalSymbol
|
||||
{
|
||||
public required string Purl { get; init; } // pkg:npm/lodash@4.17.21
|
||||
public required string Namespace { get; init; } // lodash
|
||||
public required string Type { get; init; } // _ (for JS) or class name
|
||||
public required string Method { get; init; } // get
|
||||
public required string Signature { get; init; } // (object, string)
|
||||
public required string CanonicalId { get; init; } // SHA-256 of above
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. CVE-Symbol Mapping Corpus (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/CveMapping/`
|
||||
|
||||
Maps CVE identifiers to vulnerable symbols:
|
||||
|
||||
```csharp
|
||||
public interface ICveSymbolMappingService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get vulnerable symbols for a CVE.
|
||||
/// </summary>
|
||||
Task<CveSymbolMapping?> GetMappingAsync(string cveId, CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Ingest mapping from patch analysis or manual curation.
|
||||
/// </summary>
|
||||
Task IngestMappingAsync(CveSymbolMapping mapping, CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record CveSymbolMapping
|
||||
{
|
||||
public required string CveId { get; init; }
|
||||
public required ImmutableArray<VulnerableSymbol> Symbols { get; init; }
|
||||
public required MappingSource Source { get; init; }
|
||||
public required double Confidence { get; init; }
|
||||
public required DateTimeOffset ExtractedAt { get; init; }
|
||||
public string? PatchCommitUrl { get; init; }
|
||||
public string? DeltaSigDigest { get; init; }
|
||||
}
|
||||
|
||||
public sealed record VulnerableSymbol
|
||||
{
|
||||
public required CanonicalSymbol Symbol { get; init; }
|
||||
public required VulnerabilityType Type { get; init; } // Sink, TaintSource, GadgetEntry
|
||||
public string? Condition { get; init; } // e.g., "when input > 1024"
|
||||
}
|
||||
|
||||
public enum MappingSource
|
||||
{
|
||||
PatchAnalysis, // Automated extraction from git diff
|
||||
OsvDatabase, // OSV affected ranges with functions
|
||||
ManualCuration, // Security researcher input
|
||||
DeltaSignature, // Binary diff signature match
|
||||
AiExtraction // LLM-assisted extraction from description
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Runtime Agent Framework (NEW)
|
||||
|
||||
**Location:** `src/Signals/StellaOps.Signals.RuntimeAgent/`
|
||||
|
||||
Pluggable runtime collection agents:
|
||||
|
||||
| Platform | Agent Type | Collection Method | Overhead |
|
||||
|----------|-----------|-------------------|----------|
|
||||
| .NET | EventPipe | CLR method enter/leave | <2% |
|
||||
| .NET | CLR Profiler | ICorProfiler callbacks | <5% |
|
||||
| Java | JFR | Flight Recorder events | <1% |
|
||||
| Java | JVMTI | Agent attach | <3% |
|
||||
| Node.js | V8 Profiler | CPU profiler | <2% |
|
||||
| Python | sys.settrace | Frame tracing | <10% |
|
||||
| Native | eBPF | uprobe/uretprobe | <1% |
|
||||
| Windows | ETW | CLR/JScript providers | <1% |
|
||||
|
||||
```csharp
|
||||
public interface IRuntimeAgent
|
||||
{
|
||||
string AgentId { get; }
|
||||
RuntimePlatform Platform { get; }
|
||||
RuntimePosture Posture { get; }
|
||||
|
||||
Task StartAsync(RuntimeAgentOptions options, CancellationToken ct);
|
||||
Task StopAsync(CancellationToken ct);
|
||||
|
||||
IAsyncEnumerable<RuntimeMethodEvent> StreamEventsAsync(CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record RuntimeMethodEvent
|
||||
{
|
||||
public required string SymbolId { get; init; }
|
||||
public required string MethodName { get; init; }
|
||||
public required string TypeName { get; init; }
|
||||
public required string AssemblyOrModule { get; init; }
|
||||
public required DateTimeOffset Timestamp { get; init; }
|
||||
public required RuntimeEventKind Kind { get; init; } // Enter, Leave, Tail
|
||||
public string? ContainerId { get; init; }
|
||||
public int? ProcessId { get; init; }
|
||||
public string? ThreadId { get; init; }
|
||||
public IReadOnlyDictionary<string, string>? Context { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. VEX Decision Filter Enhancement (ENHANCEMENT)
|
||||
|
||||
**Location:** `src/Policy/StellaOps.Policy.Engine/Vex/`
|
||||
|
||||
Enhance existing `IVexDecisionEmitter` with reachability-aware justification:
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityAwareVexEmitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit VEX verdict with hybrid reachability evidence.
|
||||
/// </summary>
|
||||
Task<VexDecisionDocument> EmitVerdictAsync(
|
||||
Finding finding,
|
||||
HybridReachabilityResult reachability,
|
||||
VexEmissionOptions options,
|
||||
CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
### Reachability Lattice (8-State Model)
|
||||
|
||||
The system uses an 8-state lattice for evidence strength:
|
||||
|
||||
```
|
||||
X (Contested)
|
||||
/ \
|
||||
/ \
|
||||
CR (Confirmed CU (Confirmed
|
||||
Reachable) Unreachable)
|
||||
| \ / |
|
||||
| \ / |
|
||||
RO (Runtime RU (Runtime
|
||||
Observed) Unobserved)
|
||||
| |
|
||||
| |
|
||||
SR (Static SU (Static
|
||||
Reachable) Unreachable)
|
||||
\ /
|
||||
\ /
|
||||
U (Unknown)
|
||||
```
|
||||
|
||||
**State Transitions:**
|
||||
|
||||
| From | Evidence | To | Confidence Delta |
|
||||
|------|----------|-----|-----------------|
|
||||
| U | Static analysis finds path | SR | +0.3 |
|
||||
| U | Static analysis proves no path | SU | +0.4 |
|
||||
| SR | Runtime observes execution | RO | +0.3 |
|
||||
| SR | Runtime window expires with no observation | RU | +0.2 |
|
||||
| SU | Runtime observes execution (unexpected!) | X | -0.2 (conflict) |
|
||||
| RO | Multiple sources confirm | CR | +0.2 |
|
||||
| RU | Multiple sources confirm | CU | +0.2 |
|
||||
|
||||
### VEX Justification Mapping
|
||||
|
||||
| Lattice State | VEX Status | Justification | Confidence Range |
|
||||
|---------------|------------|---------------|------------------|
|
||||
| CU | not_affected | vulnerable_code_not_in_execute_path | 0.90-1.00 |
|
||||
| RU | not_affected | vulnerable_code_not_in_execute_path | 0.70-0.89 |
|
||||
| SU | not_affected | vulnerable_code_not_in_execute_path | 0.50-0.69 |
|
||||
| CR | affected | - | 0.90-1.00 |
|
||||
| RO | affected | - | 0.70-0.89 |
|
||||
| SR | under_investigation | - | 0.30-0.69 |
|
||||
| U | under_investigation | - | 0.00-0.29 |
|
||||
| X | under_investigation | - | requires_manual_review |
|
||||
|
||||
---
|
||||
|
||||
## Data Contracts
|
||||
|
||||
### 1. Static Reachability (existing, enhanced)
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.static@v2",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"component": "pkg:npm/lodash@4.17.21",
|
||||
"symbols": [
|
||||
{
|
||||
"canonicalId": "sha256:def456...",
|
||||
"namespace": "lodash",
|
||||
"type": "_",
|
||||
"method": "get",
|
||||
"signature": "(object, string)",
|
||||
"entrypoints": ["main", "handleRequest"],
|
||||
"pathLength": 3,
|
||||
"guards": [
|
||||
{"type": "FeatureFlag", "key": "ENABLE_LODASH", "value": "true"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extractedAt": "2026-01-09T10:00:00Z",
|
||||
"extractorVersion": "scanner.callgraph@3.2.1"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Runtime Reachability (new)
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.runtime@v1",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"observationWindow": {
|
||||
"start": "2026-01-01T00:00:00Z",
|
||||
"end": "2026-01-09T00:00:00Z",
|
||||
"durationDays": 8
|
||||
},
|
||||
"trafficProfile": {
|
||||
"requestCount": 1250000,
|
||||
"percentile": "p95",
|
||||
"environments": ["production"]
|
||||
},
|
||||
"symbols": [
|
||||
{
|
||||
"canonicalId": "sha256:def456...",
|
||||
"hitCount": 45230,
|
||||
"firstSeen": "2026-01-02T14:23:00Z",
|
||||
"lastSeen": "2026-01-08T22:15:00Z",
|
||||
"contexts": [
|
||||
{
|
||||
"containerId": "abc123",
|
||||
"processId": 1234,
|
||||
"route": "/api/v1/users",
|
||||
"frequency": 0.82
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"collectedAt": "2026-01-09T00:05:00Z",
|
||||
"agentVersion": "signals.runtime@1.0.0",
|
||||
"agentPosture": "ActiveTracing"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Hybrid Reachability Result
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.hybrid@v1",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"symbol": {
|
||||
"canonicalId": "sha256:def456...",
|
||||
"displayName": "lodash.get(object, string)"
|
||||
},
|
||||
"latticeState": "RO",
|
||||
"confidence": 0.85,
|
||||
"staticEvidence": {
|
||||
"present": true,
|
||||
"pathCount": 3,
|
||||
"shortestPath": 2,
|
||||
"guards": []
|
||||
},
|
||||
"runtimeEvidence": {
|
||||
"present": true,
|
||||
"hitCount": 45230,
|
||||
"lastSeen": "2026-01-08T22:15:00Z",
|
||||
"windowDays": 8
|
||||
},
|
||||
"verdict": {
|
||||
"status": "affected",
|
||||
"justification": null,
|
||||
"confidenceBucket": "high"
|
||||
},
|
||||
"evidenceUris": [
|
||||
"stella://reachgraph/sha256:abc123/slice?symbol=sha256:def456",
|
||||
"stella://signals/runtime/sha256:abc123?symbol=sha256:def456"
|
||||
],
|
||||
"computedAt": "2026-01-09T10:30:00Z",
|
||||
"computedBy": "reachability.index@1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. OpenVEX with StellaOps Evidence Extension
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"author": "StellaOps Policy Engine",
|
||||
"timestamp": "2026-01-09T10:35:00Z",
|
||||
"version": 1,
|
||||
"statements": [
|
||||
{
|
||||
"vulnerability": {
|
||||
"@id": "CVE-2021-44228",
|
||||
"name": "Log4Shell",
|
||||
"description": "Apache Log4j2 JNDI injection"
|
||||
},
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
|
||||
"subcomponents": [
|
||||
{"@id": "pkg:maven/org.apache.logging.log4j/log4j-api@2.14.1"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "not_affected",
|
||||
"justification": "vulnerable_code_not_in_execute_path",
|
||||
"impact_statement": "The vulnerable JNDI lookup is not reachable from any application entrypoint.",
|
||||
"action_statement": "No action required. Monitor for code changes that may introduce reachability.",
|
||||
"x-stellaops-evidence": {
|
||||
"schemaVersion": "stellaops.evidence@v1",
|
||||
"latticeState": "CU",
|
||||
"confidence": 0.95,
|
||||
"staticAnalysis": {
|
||||
"graphDigest": "blake3:abc123...",
|
||||
"pathCount": 0,
|
||||
"analyzerVersion": "scanner.callgraph@3.2.1"
|
||||
},
|
||||
"runtimeAnalysis": {
|
||||
"observationWindowDays": 14,
|
||||
"trafficPercentile": "p95",
|
||||
"hitCount": 0,
|
||||
"agentPosture": "EbpfDeep"
|
||||
},
|
||||
"cveSymbolMapping": {
|
||||
"source": "PatchAnalysis",
|
||||
"vulnerableSymbols": [
|
||||
"org.apache.logging.log4j.core.lookup.JndiLookup::lookup"
|
||||
],
|
||||
"mappingConfidence": 0.98
|
||||
},
|
||||
"evidenceUris": [
|
||||
"stella://reachgraph/blake3:abc123",
|
||||
"stella://signals/runtime/tenant123/sha256:def456"
|
||||
],
|
||||
"attestation": {
|
||||
"dsseDigest": "sha256:sig789...",
|
||||
"rekorLogIndex": 12345678
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Evidence URI Scheme
|
||||
|
||||
Define `stella://` URI scheme for evidence references:
|
||||
|
||||
| Pattern | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `stella://reachgraph/{digest}` | Full reachability graph | `stella://reachgraph/blake3:abc123` |
|
||||
| `stella://reachgraph/{digest}/slice?symbol={id}` | Symbol slice | `stella://reachgraph/blake3:abc123/slice?symbol=sha256:def` |
|
||||
| `stella://signals/runtime/{tenant}/{artifact}` | Runtime facts | `stella://signals/runtime/acme/sha256:abc` |
|
||||
| `stella://signals/runtime/{tenant}/{artifact}?symbol={id}` | Symbol runtime facts | `stella://signals/runtime/acme/sha256:abc?symbol=sha256:def` |
|
||||
| `stella://cvemap/{cveId}` | CVE-symbol mapping | `stella://cvemap/CVE-2021-44228` |
|
||||
| `stella://attestation/{digest}` | DSSE attestation | `stella://attestation/sha256:sig789` |
|
||||
|
||||
---
|
||||
|
||||
## Integration Architecture
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Scanner Module │
|
||||
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
||||
│ │ SBOM │──>│ CallGraph │──>│ ReachGraph Service │ │
|
||||
│ │ Generator │ │ Extractor │ │ (static reachability store) │ │
|
||||
│ └─────────────┘ └─────────────────┘ └─────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Static edges + nodes
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Reachability Core Library │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Symbol │ │ CVE-Symbol │ │ IReachabilityIndex │ │
|
||||
│ │ Canonicalizer │ │ Mapping Service │ │ (unified query facade) │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ Runtime facts
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Signals Module │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Runtime Agent │──>│ Symbol │──>│ RuntimeFacts Store │ │
|
||||
│ │ (.NET/Java/etc) │ │ Normalizer │ │ (Valkey + PostgreSQL) │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Hybrid queries
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Policy Engine │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ VEX Decision │ │ Reachability- │ │ Evidence-Weighted │ │
|
||||
│ │ Emitter │<─│ Aware Filter │<─│ Score Calculator │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ VEX verdicts
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ VexLens Module │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Consensus │ │ Trust Weight │ │ Conflict │ │
|
||||
│ │ Engine │ │ Engine │ │ Resolution │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### API Endpoints (New/Enhanced)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | `/v1/reachability/query` | Query hybrid reachability for symbol |
|
||||
| POST | `/v1/reachability/query/batch` | Batch query for CVE symbols |
|
||||
| GET | `/v1/reachability/artifact/{digest}/summary` | Artifact reachability summary |
|
||||
| POST | `/v1/runtime/ingest` | Ingest runtime facts from agent |
|
||||
| GET | `/v1/runtime/facts/{artifact}` | Get runtime facts for artifact |
|
||||
| POST | `/v1/cvemap/ingest` | Ingest CVE-symbol mapping |
|
||||
| GET | `/v1/cvemap/{cveId}` | Get CVE-symbol mapping |
|
||||
| POST | `/v1/vex/emit/reachability-aware` | Emit VEX with reachability evidence |
|
||||
|
||||
---
|
||||
|
||||
## Air-Gap Support
|
||||
|
||||
### Offline Bundle Format
|
||||
|
||||
```
|
||||
hybrid-reachability-bundle/
|
||||
├── manifest.json # Bundle metadata, digests
|
||||
├── static/
|
||||
│ ├── reachgraphs.jsonl # CallGraph snapshots
|
||||
│ └── reachgraphs.sig # DSSE signatures
|
||||
├── runtime/
|
||||
│ ├── facts.jsonl # Runtime observations
|
||||
│ └── facts.sig # DSSE signatures
|
||||
├── cvemap/
|
||||
│ ├── mappings.jsonl # CVE-symbol mappings
|
||||
│ └── mappings.sig # DSSE signatures
|
||||
├── verdicts/
|
||||
│ ├── vex-decisions.jsonl # Pre-computed VEX verdicts
|
||||
│ └── vex-decisions.sig # DSSE signatures
|
||||
└── signatures/
|
||||
└── bundle.dsse # Bundle attestation
|
||||
```
|
||||
|
||||
### Offline Workflow
|
||||
|
||||
1. **Export** (connected environment):
|
||||
```bash
|
||||
stella reachability export --artifact sha256:abc123 --output bundle.zip
|
||||
```
|
||||
|
||||
2. **Transfer** bundle to air-gapped environment
|
||||
|
||||
3. **Import** (air-gapped environment):
|
||||
```bash
|
||||
stella reachability import --bundle bundle.zip --verify
|
||||
```
|
||||
|
||||
4. **Query** (air-gapped, uses cached data):
|
||||
```bash
|
||||
stella reachability query --cve CVE-2021-44228 --artifact sha256:abc123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Determinism Guarantees
|
||||
|
||||
### Reproducibility Requirements
|
||||
|
||||
1. **Canonical Serialization:** RFC 8785 JSON (sorted keys, no nulls, minimal escaping)
|
||||
2. **Stable Symbol IDs:** SHA-256 of canonical symbol representation
|
||||
3. **Time Injection:** All timestamps via `TimeProvider` (testable, replayable)
|
||||
4. **Culture Invariance:** `InvariantCulture` for all string operations
|
||||
5. **Ordered Collections:** `ImmutableSortedSet` / `ImmutableSortedDictionary` for deterministic iteration
|
||||
|
||||
### Replay Verification
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityReplayService
|
||||
{
|
||||
/// <summary>
|
||||
/// Replay hybrid reachability computation from inputs.
|
||||
/// </summary>
|
||||
Task<ReplayResult> ReplayAsync(
|
||||
HybridReachabilityInputs inputs,
|
||||
HybridReachabilityResult expected,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record ReplayResult
|
||||
{
|
||||
public required bool Match { get; init; }
|
||||
public required string ExpectedDigest { get; init; }
|
||||
public required string ActualDigest { get; init; }
|
||||
public IReadOnlyList<string>? Differences { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Access Control
|
||||
|
||||
| Resource | Read | Write | Admin |
|
||||
|----------|------|-------|-------|
|
||||
| Static reachability | `reachability:read` | `reachability:write` | - |
|
||||
| Runtime facts | `runtime:read` | `runtime:write` | - |
|
||||
| CVE mappings | `cvemap:read` | `cvemap:write` | `cvemap:admin` |
|
||||
| VEX verdicts | `vex:read` | `vex:write` | - |
|
||||
|
||||
### Data Sensitivity
|
||||
|
||||
- **Runtime traces expose code paths:** May reveal internal architecture
|
||||
- **Symbol names may be sensitive:** Obfuscation support planned
|
||||
- **Tenant isolation:** RLS policies enforce strict separation
|
||||
|
||||
### Threat Model
|
||||
|
||||
| Threat | Mitigation |
|
||||
|--------|------------|
|
||||
| Malicious runtime agent | Agent authentication via mTLS, signed events |
|
||||
| CVE mapping poisoning | Mapping provenance tracking, multi-source consensus |
|
||||
| Evidence tampering | DSSE attestations, Rekor transparency log |
|
||||
| Information leakage | Tenant RLS, encrypted storage, audit logs |
|
||||
|
||||
---
|
||||
|
||||
## Observability
|
||||
|
||||
### Metrics
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| `reachability_query_duration_seconds` | Query latency histogram |
|
||||
| `reachability_lattice_state_total` | Count by lattice state |
|
||||
| `runtime_facts_ingested_total` | Runtime facts ingested |
|
||||
| `runtime_agent_connected_gauge` | Connected agents |
|
||||
| `cvemap_mappings_total` | CVE mappings count |
|
||||
| `vex_verdicts_by_reachability_total` | VEX verdicts by lattice state |
|
||||
|
||||
### Traces
|
||||
|
||||
| Span | Description |
|
||||
|------|-------------|
|
||||
| `reachability.query.static` | Static graph query |
|
||||
| `reachability.query.runtime` | Runtime facts query |
|
||||
| `reachability.query.hybrid` | Combined computation |
|
||||
| `reachability.canonicalize` | Symbol canonicalization |
|
||||
| `cvemap.lookup` | CVE-symbol lookup |
|
||||
|
||||
### Alerts
|
||||
|
||||
| Alert | Condition | Severity |
|
||||
|-------|-----------|----------|
|
||||
| `ReachabilityQuerySlow` | P95 > 500ms | warning |
|
||||
| `RuntimeAgentDisconnected` | No heartbeat 5min | warning |
|
||||
| `CveMappingStale` | No updates 7 days | info |
|
||||
| `LatticeConflictRate` | X state > 5% | warning |
|
||||
|
||||
---
|
||||
|
||||
## Performance Targets
|
||||
|
||||
| Operation | Target | Notes |
|
||||
|-----------|--------|-------|
|
||||
| Hybrid query (single symbol) | P95 < 50ms | Cached |
|
||||
| Hybrid query (batch, 100 symbols) | P95 < 500ms | Parallel |
|
||||
| Runtime fact ingestion | 10,000 events/sec | Per agent |
|
||||
| Symbol canonicalization | < 1ms | In-memory |
|
||||
| CVE mapping lookup | P95 < 10ms | Cached |
|
||||
| VEX emission | P95 < 100ms | Includes signing |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Internal Modules
|
||||
|
||||
| Module | Dependency Type | Purpose |
|
||||
|--------|-----------------|---------|
|
||||
| ReachGraph | Data source | Static call-graph queries |
|
||||
| Signals | Data source + sink | Runtime fact storage |
|
||||
| Scanner.CallGraph | Data producer | Call-graph extraction |
|
||||
| Policy | Consumer | VEX decision emission |
|
||||
| VexLens | Consumer | Consensus computation |
|
||||
| Attestor | Integration | DSSE signing |
|
||||
| Authority | Integration | Access control |
|
||||
|
||||
### External Dependencies
|
||||
|
||||
| Dependency | Purpose | Offline Alternative |
|
||||
|------------|---------|---------------------|
|
||||
| OSV API | CVE-symbol enrichment | Bundled corpus |
|
||||
| NVD API | CVE details | Bundled corpus |
|
||||
| Rekor | Transparency log | Local log or skip |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Foundation (Weeks 1-2)
|
||||
- Reachability Core library with `IReachabilityIndex`
|
||||
- Symbol canonicalization service
|
||||
- Integration with existing ReachGraph
|
||||
|
||||
### Phase 2: CVE Mapping (Weeks 3-4)
|
||||
- CVE-symbol mapping service
|
||||
- Patch analysis extractor (git diff parsing)
|
||||
- Initial corpus from high-profile CVEs
|
||||
|
||||
### Phase 3: Runtime Collection (Weeks 5-8)
|
||||
- Runtime agent framework
|
||||
- .NET EventPipe agent
|
||||
- Java JFR agent
|
||||
- Symbol normalization pipeline
|
||||
|
||||
### Phase 4: VEX Integration (Weeks 9-10)
|
||||
- Reachability-aware VEX emitter
|
||||
- Evidence extension schema
|
||||
- Policy Engine integration
|
||||
|
||||
### Phase 5: UI & Observability (Weeks 11-12)
|
||||
- Evidence panel enhancements
|
||||
- Metrics and alerts
|
||||
- Documentation and runbooks
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Quantitative
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
|--------|--------|-------------|
|
||||
| False positive reduction | >60% | CVEs marked NA with reachability evidence |
|
||||
| Confidence accuracy | >90% | Verdicts validated against manual review |
|
||||
| Query latency | P95 < 100ms | Hybrid queries |
|
||||
| Coverage | >80% | Artifacts with both static + runtime evidence |
|
||||
|
||||
### Qualitative
|
||||
|
||||
- Security teams trust VEX verdicts backed by evidence
|
||||
- Developers understand why CVE is/isn't relevant
|
||||
- Auditors can verify verdict provenance
|
||||
- Air-gapped deployments have full functionality
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [ReachGraph Architecture](../../modules/reach-graph/architecture.md)
|
||||
- [Signals Architecture](../../modules/signals/architecture.md)
|
||||
- [VexLens Architecture](../../modules/vex-lens/architecture.md)
|
||||
- [Evidence-Weighted Scoring](../../modules/signals/evidence-weighted-scoring.md)
|
||||
- [DSSE Attestation](../../modules/attestor/dsse-integration.md)
|
||||
|
||||
---
|
||||
|
||||
## Sprint Index
|
||||
|
||||
| Sprint ID | Title | Status |
|
||||
|-----------|-------|--------|
|
||||
| SPRINT_20260109_009_000 | INDEX: Hybrid Reachability | Planning |
|
||||
| SPRINT_20260109_009_001 | Reachability Core Library | Planning |
|
||||
| SPRINT_20260109_009_002 | Symbol Canonicalization | Planning |
|
||||
| SPRINT_20260109_009_003 | CVE-Symbol Mapping | Planning |
|
||||
| SPRINT_20260109_009_004 | Runtime Agent Framework | Planning |
|
||||
| SPRINT_20260109_009_005 | VEX Decision Integration | Planning |
|
||||
| SPRINT_20260109_009_006 | Evidence Panel UI | Planning |
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 09-Jan-2026_
|
||||
Reference in New Issue
Block a user