# Reachability Input Contract v1.0.0 **Status:** APPROVED **Version:** 1.0.0 **Effective:** 2025-12-19 **Owner:** Policy Guild + Signals Guild **Sprint:** SPRINT_0126_0001_0001 (unblocks POLICY-ENGINE-80-001 through 80-004) --- ## 1. Purpose This contract defines the integration between the Signals service (reachability analysis) and the Policy Engine. It specifies how reachability and exploitability facts flow into policy evaluation, enabling risk-aware decisions based on static analysis, runtime observations, and exploit intelligence. ## 2. Schema Reference The canonical JSON schema is at: ``` docs/schemas/reachability-input.schema.json ``` ## 3. Data Flow ``` ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ ┌──────────────┐ │ Scanner │────▶│ Signals │────▶│ Reachability │────▶│ Policy │ │ (callgraph) │ │ Service │ │ Facts Store │ │ Engine │ └─────────────┘ └──────────────┘ └───────────────┘ └──────────────┘ │ ▲ │ │ ┌──────▼──────┐ │ │ Runtime │──────────────┘ │ Agent │ └─────────────┘ ``` ## 4. Core Types ### 4.1 ReachabilityInput The input payload submitted to Policy Engine for evaluation: ```csharp public sealed record ReachabilityInput { /// Subject being evaluated (component + vulnerability). public required Subject Subject { get; init; } /// Static reachability analysis results. public required ImmutableArray ReachabilityFacts { get; init; } /// Exploitability assessments from KEV, EPSS, vendor advisories. public ImmutableArray ExploitabilityFacts { get; init; } /// References to stored callgraphs. public ImmutableArray CallgraphRefs { get; init; } /// Runtime observation facts. public ImmutableArray RuntimeFacts { get; init; } /// Scanner entropy/trust score for confidence weighting. public EntropyScore? EntropyScore { get; init; } /// Input timestamp (UTC). public required DateTimeOffset Timestamp { get; init; } } ``` ### 4.2 Subject ```csharp public sealed record Subject { /// Package URL of the component. public required string Purl { get; init; } /// CVE identifier (e.g., CVE-2024-1234). public string? CveId { get; init; } /// GitHub Security Advisory ID. public string? GhsaId { get; init; } /// Internal vulnerability identifier. public string? VulnerabilityId { get; init; } /// Vulnerable symbols/functions in the component. public ImmutableArray AffectedSymbols { get; init; } /// Affected version range (e.g., "<1.2.3"). public string? VersionRange { get; init; } } ``` ### 4.3 ReachabilityFact ```csharp public sealed record ReachabilityFact { /// Reachability state determination. public required ReachabilityState State { get; init; } /// Confidence score (0.0-1.0). public required decimal Confidence { get; init; } /// Source of determination. public required ReachabilitySource Source { get; init; } /// Analyzer that produced this fact. public string? Analyzer { get; init; } /// Analyzer version. public string? AnalyzerVersion { get; init; } /// Call path from entry point to vulnerable symbol. public CallPath? CallPath { get; init; } /// Entry points that can reach vulnerable code. public ImmutableArray EntryPoints { get; init; } /// Supporting evidence. public ReachabilityEvidence? Evidence { get; init; } /// When this fact was evaluated. public DateTimeOffset? EvaluatedAt { get; init; } } public enum ReachabilityState { Reachable = 0, Unreachable = 1, PotentiallyReachable = 2, Unknown = 3 } public enum ReachabilitySource { StaticAnalysis = 0, DynamicAnalysis = 1, SbomInference = 2, Manual = 3, External = 4 } ``` ### 4.4 ExploitabilityFact ```csharp public sealed record ExploitabilityFact { /// Exploitability state. public required ExploitabilityState State { get; init; } /// Confidence score (0.0-1.0). public required decimal Confidence { get; init; } /// Source of determination. public required ExploitabilitySource Source { get; init; } /// EPSS probability score (0.0-1.0). public decimal? EpssScore { get; init; } /// EPSS percentile (0-100). public decimal? EpssPercentile { get; init; } /// Listed in CISA Known Exploited Vulnerabilities. public bool? KevListed { get; init; } /// KEV remediation due date. public DateOnly? KevDueDate { get; init; } /// Exploit maturity level (per CVSS). public ExploitMaturity? ExploitMaturity { get; init; } /// References to known exploits. public ImmutableArray ExploitRefs { get; init; } /// Conditions required for exploitation. public ImmutableArray Conditions { get; init; } /// When this fact was evaluated. public DateTimeOffset? EvaluatedAt { get; init; } } public enum ExploitabilityState { Exploitable = 0, NotExploitable = 1, ConditionallyExploitable = 2, Unknown = 3 } public enum ExploitabilitySource { Kev = 0, Epss = 1, VendorAdvisory = 2, InternalAnalysis = 3, ExploitDb = 4 } public enum ExploitMaturity { NotDefined = 0, Unproven = 1, Poc = 2, Functional = 3, High = 4 } ``` ### 4.5 RuntimeFact ```csharp public sealed record RuntimeFact { /// Type of runtime observation. public required RuntimeFactType Type { get; init; } /// Observed symbol/function. public string? Symbol { get; init; } /// Observed module. public string? Module { get; init; } /// Number of times called. public int? CallCount { get; init; } /// Last invocation time. public DateTimeOffset? LastCalled { get; init; } /// When observation was recorded. public required DateTimeOffset ObservedAt { get; init; } /// Observation window duration (e.g., "7d"). public string? ObservationWindow { get; init; } /// Environment where observed. public RuntimeEnvironment? Environment { get; init; } } public enum RuntimeFactType { FunctionCalled = 0, FunctionNotCalled = 1, PathExecuted = 2, PathNotExecuted = 3, ModuleLoaded = 4, ModuleNotLoaded = 5 } public enum RuntimeEnvironment { Production = 0, Staging = 1, Development = 2, Test = 3 } ``` ## 5. Policy Engine Integration ### 5.1 ReachabilityFactsJoiningService The `ReachabilityFactsJoiningService` provides efficient batch lookups with caching: ```csharp public interface IReachabilityFactsJoiningService { /// /// Gets reachability facts for a batch of component-advisory pairs. /// Uses cache-first strategy with store fallback. /// Task GetFactsBatchAsync( string tenantId, IReadOnlyList items, CancellationToken cancellationToken = default); /// /// Enriches signal context with reachability facts. /// Task EnrichSignalsAsync( string tenantId, string componentPurl, string advisoryId, IDictionary signals, CancellationToken cancellationToken = default); } ``` ### 5.2 SPL Predicates Reachability is exposed in SPL (StellaOps Policy Language) via the `reachability` scope: ```yaml # Example SPL rule using reachability predicates rules: - name: "Suppress unreachable critical CVEs" when: all: - severity >= critical - reachability.state == "unreachable" - reachability.confidence >= 0.9 then: effect: suppress justification: "Unreachable code path with high confidence" - name: "Escalate reachable with exploit" when: all: - reachability.state == "reachable" - exploitability.kev_listed == true then: effect: escalate priority: critical ``` Available predicates: | Predicate | Type | Description | |-----------|------|-------------| | `reachability.state` | string | "reachable", "unreachable", "potentially_reachable", "unknown" | | `reachability.confidence` | decimal | Confidence score 0.0-1.0 | | `reachability.score` | decimal | Computed risk score | | `reachability.has_runtime_evidence` | bool | Whether runtime facts support determination | | `reachability.is_high_confidence` | bool | Confidence >= 0.8 | | `reachability.source` | string | Source of determination | | `reachability.method` | string | Analysis method used | | `exploitability.state` | string | "exploitable", "not_exploitable", "conditionally_exploitable", "unknown" | | `exploitability.epss_score` | decimal | EPSS probability 0.0-1.0 | | `exploitability.epss_percentile` | decimal | EPSS percentile 0-100 | | `exploitability.kev_listed` | bool | In CISA KEV catalog | | `exploitability.kev_due_date` | date | KEV remediation deadline | | `exploitability.maturity` | string | "not_defined", "unproven", "poc", "functional", "high" | ### 5.3 ReachabilityOutput Policy evaluation produces enriched output: ```csharp public sealed record ReachabilityOutput { /// Subject evaluated. public required Subject Subject { get; init; } /// Effective reachability state after policy rules. public required ReachabilityState EffectiveState { get; init; } /// Effective exploitability after policy rules. public ExploitabilityState? EffectiveExploitability { get; init; } /// Risk adjustment from policy evaluation. public required RiskAdjustment RiskAdjustment { get; init; } /// Policy rule trace. public ImmutableArray PolicyTrace { get; init; } /// When evaluation occurred. public required DateTimeOffset EvaluatedAt { get; init; } } public sealed record RiskAdjustment { /// Risk multiplier (0=suppress, 1=neutral, >1=amplify). public required decimal Factor { get; init; } /// Severity override if rules dictate. public Severity? SeverityOverride { get; init; } /// Justification for adjustment. public string? Justification { get; init; } } ``` ## 6. API Endpoints ### 6.1 Signals Service Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `POST /signals/reachability/recompute` | POST | Recompute reachability for a subject | | `GET /signals/facts/{subjectKey}` | GET | Get reachability facts for a subject | | `POST /signals/runtime-facts` | POST | Ingest runtime observations | ### 6.2 Policy Engine Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `POST /api/policy/evaluate` | POST | Evaluate with reachability enrichment | | `POST /api/policy/simulate` | POST | Simulate with reachability overrides | | `GET /api/policy/reachability/stats` | GET | Get reachability integration metrics | ## 7. Caching Strategy ### 7.1 Cache Layers 1. **L1: In-Memory Overlay Cache** - Per-request deduplication - TTL: Request lifetime - Key: `{tenantId}:{componentPurl}:{advisoryId}` 2. **L2: Redis Distributed Cache** - Shared across Policy Engine instances - TTL: 5 minutes (configurable) - Key: `rf:{tenantId}:{sha256(purl+advisoryId)}` 3. **L3: Postgres Facts Store** - Authoritative source - Indexed by `(tenant_id, component_purl, advisory_id)` ### 7.2 Cache Invalidation - Facts are invalidated when: - New callgraph is ingested - Runtime facts are updated - Manual override is applied - TTL expires ## 8. Telemetry ### 8.1 Metrics | Metric | Type | Labels | Description | |--------|------|--------|-------------| | `policy_reachability_applied_total` | counter | `state` | Facts applied to evaluations | | `policy_reachability_cache_hits_total` | counter | - | Cache hits | | `policy_reachability_cache_misses_total` | counter | - | Cache misses | | `policy_reachability_cache_hit_ratio` | gauge | - | Hit ratio (0.0-1.0) | | `policy_reachability_lookups_total` | counter | `outcome` | Lookup attempts | | `policy_reachability_lookup_seconds` | histogram | - | Lookup latency | ### 8.2 Traces Activity: `reachability_facts.batch_lookup` Tags: - `tenant`: Tenant ID - `batch_size`: Number of items requested - `cache_hits`: Items found in cache - `cache_misses`: Items not in cache - `store_hits`: Items fetched from store ## 9. Configuration ```yaml # etc/policy-engine.yaml PolicyEngine: Reachability: Enabled: true CacheTtlSeconds: 300 MaxBatchSize: 1000 DefaultConfidenceThreshold: 0.7 HighConfidenceThreshold: 0.9 ReachabilityCache: Type: "redis" # or "memory" RedisConnectionString: "${REDIS_URL}" KeyPrefix: "rf:" ``` ## 10. Validation Rules 1. `Subject.Purl` must be a valid Package URL 2. `ReachabilityFact.Confidence` must be 0.0-1.0 3. `ReachabilityFact.State` must be a valid enum value 4. `Timestamp` must be valid UTC ISO-8601 5. At least one of `CveId`, `GhsaId`, or `VulnerabilityId` must be present --- ## Changelog | Version | Date | Changes | |---------|------|---------| | 1.0.0 | 2025-12-19 | Initial release |