Reachability Evidence Policy Gates

Status: Design v1 (Sprint 0401) + D5 lattice gate defaults IMPLEMENTED 2026-06-11 (§1a below; ratified decision D5 of docs/modules/scanner/reachability-sink-strategy.md §10, sprint SPRINT_20260610_002 POLICY-VEX). Owners: Policy Guild · Signals Guild · VEX Guild. Audience: Policy and VEX engineers defining how reachability evidence gates VEX status transitions.

This document defines the policy gates that enforce reachability-evidence requirements for VEX decisions. Gates prevent unsafe not_affected claims when the supporting evidence is insufficient.


1a. D5 lattice gate defaults (implemented)

The ratified D5 defaults bind the public reachability lattice (reachable:proven / reachable:likely / not-observed / unknown, strategy doc §7.0) to gate behavior. These are hard rules of the evaluators, not configuration; only the downgrade threshold is configurable, and it rides policy packs.

Lattice verdictGate behavior (default and only)
reachable:provenMay BLOCK a release. The path witness is machine-checked before the verdict object can exist, so a block is witness-backed by construction.
reachable:likelyNever auto-blocks. Feeds evidence-weighted scoring (FeedsScoring).
unknownNever auto-blocks. Feeds scoring / unknowns budgets.
not-observedMay only DRAFT a downgrade/suppression, and only when its mandatory coverage statement clears the pack threshold; the resulting VEX not_affected statement stays pending operator countersign (ADR-025) and is never auto-applied. Below threshold: refused, nothing drafted.

Implementation surfaces:

Relationship to the rest of this document: §2–§9 below describe the richer designed gate set (lattice-state/uncertainty-tier/evidence-completeness) from Sprint 0401; the D5 defaults above are the ratified, shipped baseline those gates must remain consistent with (in particular: nothing below reachable:proven may block, and no not_affected may publish uncountersigned).


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/modules/reach-graph/guides/lattice.md §9).

Requested VEX StatusRequired Lattice StateGate Action
not_affectedCU (ConfirmedUnreachable)✅ Allow
not_affectedSU (StaticallyUnreachable)⚠️ Allow with warning, requires justification
not_affectedRU (RuntimeUnobserved)⚠️ Allow with warning, requires justification
not_affectedU, SR, RO, CR, X❌ Block
affectedCR (ConfirmedReachable)✅ Allow
affectedSR, RO✅ Allow
affectedU, SU, RU, CU, X⚠️ Warn (potential false positive)
under_investigationAny✅ Allow (safe default)
fixedAny✅ Allow (remediation action)

2.2 Uncertainty Tier Gate

Guards VEX status transitions based on the uncertainty tier (entropy-derived riskScore; see the roadmap note in lattice.md §8).

Requested VEX StatusUncertainty TierGate Action
not_affectedT1 (High)❌ Block
not_affectedT2 (Medium)⚠️ Warn, require explicit override
not_affectedT3 (Low)⚠️ Allow with advisory note
not_affectedT4 (Negligible)✅ Allow
affectedT1 (High)⚠️ Review required (may be false positive)
affectedT2-T4✅ Allow

2.3 Evidence Completeness Gate

Guards based on the presence of required evidence artifacts.

VEX StatusRequired EvidenceGate Action if Missing
not_affectedgraphHash (DSSE-attested)❌ Block
not_affectedpathAnalysis.pathLength >= 0❌ Block
not_affectedconfidence >= 0.8⚠️ Warn if < 0.8
affectedgraphHash OR runtimeProbe⚠️ Warn if neither
under_investigationNone 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:

{
  "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:

{
  "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:


7. Configuration

Gate thresholds are configurable via PolicyGatewayOptions:

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:

{
  "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):

{
  "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:

MetricLabelsDescription
stellaops_gate_decisions_totalgate, result, statusTotal gate decisions
stellaops_gate_blocks_totalgate, reasonTotal blocked requests
stellaops_gate_overrides_totaloperatorTotal override uses
stellaops_contested_states_totalvulnIdActive contested states

Alert conditions:



Changelog

VersionDateAuthorChanges
1.1.02026-06-11Scanner/Policy GuildD5 lattice gate defaults implemented (§1a): proven-only blocking, pack-owned downgrade threshold, drafted-pending-countersign VEX (sprint SPRINT_20260610_002 POLICY-VEX)
1.0.02025-12-13Policy GuildInitial design from Sprint 0401