up
This commit is contained in:
269
docs/reachability/policy-gate.md
Normal file
269
docs/reachability/policy-gate.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user