Files
git.stella-ops.org/docs/reachability/policy-gate.md
StellaOps Bot 999e26a48e up
2025-12-13 02:22:15 +02:00

8.0 KiB

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:

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

  • 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:

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:

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


Changelog

Version Date Author Changes
1.0.0 2025-12-13 Policy Guild Initial design from Sprint 0401