# 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 |