270 lines
8.0 KiB
Markdown
270 lines
8.0 KiB
Markdown
# Reachability Evidence Policy Gates
|
||
|
||
> **Status:** Design v1 (Sprint 0401)
|
||
> **Owners:** Policy Guild, Signals Guild, VEX Guild
|
||
|
||
This document defines the policy gates that enforce reachability evidence requirements for VEX decisions. Gates prevent unsafe "not_affected" claims when evidence is insufficient.
|
||
|
||
---
|
||
|
||
## 1. Overview
|
||
|
||
Policy gates act as checkpoints between evidence (reachability lattice state, uncertainty tier) and VEX status transitions. They ensure that:
|
||
|
||
1. **No false safety:** "not_affected" requires strong evidence of unreachability
|
||
2. **Explicit uncertainty:** Missing evidence triggers "under_investigation" rather than silence
|
||
3. **Audit trail:** All gate decisions are logged with evidence references
|
||
|
||
---
|
||
|
||
## 2. Gate Types
|
||
|
||
### 2.1 Lattice State Gate
|
||
|
||
Guards VEX status transitions based on the v1 lattice state (see `docs/reachability/lattice.md` §9).
|
||
|
||
| Requested VEX Status | Required Lattice State | Gate Action |
|
||
|---------------------|------------------------|-------------|
|
||
| `not_affected` | `CU` (ConfirmedUnreachable) | ✅ Allow |
|
||
| `not_affected` | `SU` (StaticallyUnreachable) | ⚠️ Allow with warning, requires `justification` |
|
||
| `not_affected` | `RU` (RuntimeUnobserved) | ⚠️ Allow with warning, requires `justification` |
|
||
| `not_affected` | `U`, `SR`, `RO`, `CR`, `X` | ❌ Block |
|
||
| `affected` | `CR` (ConfirmedReachable) | ✅ Allow |
|
||
| `affected` | `SR`, `RO` | ✅ Allow |
|
||
| `affected` | `U`, `SU`, `RU`, `CU`, `X` | ⚠️ Warn (potential false positive) |
|
||
| `under_investigation` | Any | ✅ Allow (safe default) |
|
||
| `fixed` | Any | ✅ Allow (remediation action) |
|
||
|
||
### 2.2 Uncertainty Tier Gate
|
||
|
||
Guards VEX status transitions based on the uncertainty tier (see `docs/uncertainty/README.md` §1.1).
|
||
|
||
| Requested VEX Status | Uncertainty Tier | Gate Action |
|
||
|---------------------|------------------|-------------|
|
||
| `not_affected` | T1 (High) | ❌ Block |
|
||
| `not_affected` | T2 (Medium) | ⚠️ Warn, require explicit override |
|
||
| `not_affected` | T3 (Low) | ⚠️ Allow with advisory note |
|
||
| `not_affected` | T4 (Negligible) | ✅ Allow |
|
||
| `affected` | T1 (High) | ⚠️ Review required (may be false positive) |
|
||
| `affected` | T2-T4 | ✅ Allow |
|
||
|
||
### 2.3 Evidence Completeness Gate
|
||
|
||
Guards based on the presence of required evidence artifacts.
|
||
|
||
| VEX Status | Required Evidence | Gate Action if Missing |
|
||
|------------|-------------------|----------------------|
|
||
| `not_affected` | `graphHash` (DSSE-attested) | ❌ Block |
|
||
| `not_affected` | `pathAnalysis.pathLength >= 0` | ❌ Block |
|
||
| `not_affected` | `confidence >= 0.8` | ⚠️ Warn if < 0.8 |
|
||
| `affected` | `graphHash` OR `runtimeProbe` | ⚠️ Warn if neither |
|
||
| `under_investigation` | None required | ✅ Allow |
|
||
|
||
---
|
||
|
||
## 3. Gate Evaluation Order
|
||
|
||
Gates are evaluated in this order; first blocking gate stops evaluation:
|
||
|
||
```
|
||
1. Evidence Completeness Gate → Block if required evidence missing
|
||
2. Lattice State Gate → Block if state incompatible with status
|
||
3. Uncertainty Tier Gate → Block/warn based on tier
|
||
4. Confidence Threshold Gate → Warn if confidence below threshold
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Gate Decision Document
|
||
|
||
Each gate evaluation produces a decision document:
|
||
|
||
```json
|
||
{
|
||
"gateId": "gate:vex:not_affected:2025-12-13T10:00:00Z",
|
||
"requestedStatus": "not_affected",
|
||
"subject": {
|
||
"vulnId": "CVE-2025-12345",
|
||
"purl": "pkg:maven/com.example/foo@1.0.0",
|
||
"symbolId": "sym:java:..."
|
||
},
|
||
"evidence": {
|
||
"latticeState": "CU",
|
||
"uncertaintyTier": "T3",
|
||
"graphHash": "blake3:...",
|
||
"riskScore": 0.25,
|
||
"confidence": 0.92
|
||
},
|
||
"gates": [
|
||
{
|
||
"name": "EvidenceCompleteness",
|
||
"result": "pass",
|
||
"reason": "graphHash present"
|
||
},
|
||
{
|
||
"name": "LatticeState",
|
||
"result": "pass",
|
||
"reason": "CU allows not_affected"
|
||
},
|
||
{
|
||
"name": "UncertaintyTier",
|
||
"result": "pass_with_note",
|
||
"reason": "T3 allows with advisory note",
|
||
"note": "MissingPurl uncertainty at 35% entropy"
|
||
}
|
||
],
|
||
"decision": "allow",
|
||
"advisory": "VEX status allowed with note: T3 uncertainty from MissingPurl",
|
||
"decidedAt": "2025-12-13T10:00:00Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Contested State Handling
|
||
|
||
When lattice state is `X` (Contested):
|
||
|
||
1. **Block all definitive statuses:** Neither "not_affected" nor "affected" allowed
|
||
2. **Force "under_investigation":** Auto-assign until triage resolves conflict
|
||
3. **Emit triage event:** Notify VEX operators of conflict with evidence links
|
||
4. **Evidence overlay:** Show both static and runtime evidence for manual review
|
||
|
||
### Contested Resolution Workflow
|
||
|
||
```
|
||
1. System detects X state
|
||
2. VEX status locked to "under_investigation"
|
||
3. Triage event emitted to operator queue
|
||
4. Operator reviews:
|
||
a. Static evidence (graph, paths)
|
||
b. Runtime evidence (probes, hits)
|
||
5. Operator provides resolution:
|
||
a. Trust static → state becomes SU/SR
|
||
b. Trust runtime → state becomes RU/RO
|
||
c. Add new evidence → recompute lattice
|
||
6. Gate re-evaluates with new state
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Override Mechanism
|
||
|
||
Operators with `vex:gate:override` permission can bypass gates with mandatory fields:
|
||
|
||
```json
|
||
{
|
||
"override": {
|
||
"gateId": "gate:vex:not_affected:...",
|
||
"operator": "user:alice@example.com",
|
||
"justification": "Manual review confirms code path is dead code",
|
||
"evidence": {
|
||
"type": "ManualReview",
|
||
"reviewId": "review:2025-12-13:001",
|
||
"attachments": ["cas://evidence/review/..."]
|
||
},
|
||
"approvedAt": "2025-12-13T11:00:00Z",
|
||
"expiresAt": "2026-01-13T11:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
Override requirements:
|
||
- `justification` is mandatory and logged
|
||
- Overrides expire after configurable period (default: 30 days)
|
||
- All overrides are auditable and appear in compliance reports
|
||
|
||
---
|
||
|
||
## 7. Configuration
|
||
|
||
Gate thresholds are configurable via `PolicyGatewayOptions`:
|
||
|
||
```yaml
|
||
PolicyGateway:
|
||
Gates:
|
||
LatticeState:
|
||
AllowSUForNotAffected: true # Allow SU with warning
|
||
AllowRUForNotAffected: true # Allow RU with warning
|
||
RequireJustificationForWeakStates: true
|
||
UncertaintyTier:
|
||
BlockT1ForNotAffected: true
|
||
WarnT2ForNotAffected: true
|
||
EvidenceCompleteness:
|
||
RequireGraphHashForNotAffected: true
|
||
MinConfidenceForNotAffected: 0.8
|
||
MinConfidenceWarning: 0.6
|
||
Override:
|
||
DefaultExpirationDays: 30
|
||
RequireJustification: true
|
||
```
|
||
|
||
---
|
||
|
||
## 8. API Integration
|
||
|
||
### POST `/api/v1/vex/status`
|
||
|
||
Request:
|
||
```json
|
||
{
|
||
"vulnId": "CVE-2025-12345",
|
||
"purl": "pkg:maven/com.example/foo@1.0.0",
|
||
"status": "not_affected",
|
||
"justification": "vulnerable_code_not_present",
|
||
"reachabilityEvidence": {
|
||
"factDigest": "sha256:...",
|
||
"graphHash": "blake3:..."
|
||
}
|
||
}
|
||
```
|
||
|
||
Response (gate blocked):
|
||
```json
|
||
{
|
||
"success": false,
|
||
"gateDecision": {
|
||
"decision": "block",
|
||
"blockedBy": "LatticeState",
|
||
"reason": "Lattice state SR (StaticallyReachable) incompatible with not_affected",
|
||
"currentState": "SR",
|
||
"requiredStates": ["CU", "SU", "RU"],
|
||
"suggestion": "Submit runtime probe evidence or change to under_investigation"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Metrics & Alerts
|
||
|
||
The policy gateway emits metrics:
|
||
|
||
| Metric | Labels | Description |
|
||
|--------|--------|-------------|
|
||
| `stellaops_gate_decisions_total` | `gate`, `result`, `status` | Total gate decisions |
|
||
| `stellaops_gate_blocks_total` | `gate`, `reason` | Total blocked requests |
|
||
| `stellaops_gate_overrides_total` | `operator` | Total override uses |
|
||
| `stellaops_contested_states_total` | `vulnId` | Active contested states |
|
||
|
||
Alert conditions:
|
||
- `stellaops_gate_overrides_total` rate > threshold → Audit review
|
||
- `stellaops_contested_states_total` > 10 → Triage backlog alert
|
||
|
||
---
|
||
|
||
## 10. Related Documents
|
||
|
||
- [Lattice Model](./lattice.md) — v1 formal 7-state lattice
|
||
- [Uncertainty States](../uncertainty/README.md) — Tier definitions and risk scoring
|
||
- [Evidence Schema](./evidence-schema.md) — richgraph-v1 schema
|
||
- [VEX Contract](../contracts/vex-v1.md) — VEX document schema
|
||
|
||
---
|
||
|
||
## Changelog
|
||
|
||
| Version | Date | Author | Changes |
|
||
|---------|------|--------|---------|
|
||
| 1.0.0 | 2025-12-13 | Policy Guild | Initial design from Sprint 0401 |
|