491 lines
18 KiB
Markdown
491 lines
18 KiB
Markdown
# Policy Evaluation Flow
|
||
|
||
## Overview
|
||
|
||
The Policy Evaluation Flow describes how StellaOps applies K4 lattice logic to vulnerability findings, incorporating VEX statements, reachability analysis, and confidence scoring to produce deterministic pass/fail verdicts. This is the core decision-making flow that determines whether a container image meets security requirements.
|
||
|
||
**Business Value**: Consistent, explainable security verdicts with full audit trail for compliance and governance.
|
||
|
||
## Actors
|
||
|
||
| Actor | Type | Role |
|
||
|-------|------|------|
|
||
| Scanner | Service | Submits findings for evaluation |
|
||
| Policy Engine | Service | Applies policy rules |
|
||
| VexLens | Service | Provides VEX consensus |
|
||
| ReachGraph | Service | Provides reachability state |
|
||
| Policy Store | Component | Stores policy definitions |
|
||
|
||
## Prerequisites
|
||
|
||
- Policy set configured for the tenant
|
||
- Scan findings generated with SBOM
|
||
- VEX statements loaded (optional)
|
||
- Reachability analysis completed (optional)
|
||
|
||
## K4 Lattice Model
|
||
|
||
StellaOps uses a 7-state K4 lattice for vulnerability reachability:
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ ConfirmedReachable │ (Highest certainty)
|
||
└──────────┬──────────┘
|
||
│
|
||
┌──────────────────────┼──────────────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌───────────────┐ ┌────────────────┐ ┌─────────────────┐
|
||
│RuntimeObserved│ │StaticallyReach.│ │ Contested │
|
||
└───────┬───────┘ └────────┬───────┘ └────────┬────────┘
|
||
│ │ │
|
||
└──────────────────────┼──────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Unknown │ (Default state)
|
||
└──────────┬──────────┘
|
||
│
|
||
┌──────────────────────┼──────────────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌───────────────┐ ┌────────────────┐ ┌─────────────────┐
|
||
│RuntimeUnobserv│ │Statically Unr. │ │ │
|
||
└───────┬───────┘ └────────┬───────┘ └─────────────────┘
|
||
│ │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ConfirmedUnreachable │ (Lowest risk)
|
||
└─────────────────────┘
|
||
```
|
||
|
||
### State Definitions
|
||
|
||
| State | Code | Description |
|
||
|-------|------|-------------|
|
||
| Unknown | `U` | No reachability data available |
|
||
| StaticallyReachable | `SR` | Static analysis shows potential call path |
|
||
| StaticallyUnreachable | `SU` | Static analysis shows no call path |
|
||
| RuntimeObserved | `RO` | Runtime telemetry confirmed execution |
|
||
| RuntimeUnobserved | `RU` | Runtime telemetry shows no execution |
|
||
| ConfirmedReachable | `CR` | Both static and runtime confirm reachability |
|
||
| ConfirmedUnreachable | `CU` | Both static and runtime confirm unreachable |
|
||
| Contested | `X` | Conflicting evidence (requires review) |
|
||
|
||
## Flow Diagram
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||
│ Policy Evaluation Flow │
|
||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ ┌─────────────┐
|
||
│ Scanner │ │ Policy │ │ VexLens │ │ ReachGraph│ │Policy Store │
|
||
└────┬────┘ └────┬────┘ └────┬────┘ └─────┬─────┘ └──────┬──────┘
|
||
│ │ │ │ │
|
||
│ Evaluate │ │ │ │
|
||
│ request │ │ │ │
|
||
│────────────>│ │ │ │
|
||
│ │ │ │ │
|
||
│ │ Load policy │ │ │
|
||
│ │ set │ │ │
|
||
│ │─────────────────────────────────────────────>
|
||
│ │ │ │ │
|
||
│ │ Policy │ │ │
|
||
│ │ rules │ │ │
|
||
│ │<─────────────────────────────────────────────
|
||
│ │ │ │ │
|
||
│ │ Get VEX │ │ │
|
||
│ │ consensus │ │ │
|
||
│ │────────────>│ │ │
|
||
│ │ │ │ │
|
||
│ │ │ Query issuers│ │
|
||
│ │ │──────┐ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │<─────┘ │ │
|
||
│ │ │ │ │
|
||
│ │ VEX status │ │ │
|
||
│ │ per CVE │ │ │
|
||
│ │<────────────│ │ │
|
||
│ │ │ │ │
|
||
│ │ Get reach │ │ │
|
||
│ │ states │ │ │
|
||
│ │─────────────────────────────> │
|
||
│ │ │ │ │
|
||
│ │ │ │ Query call │
|
||
│ │ │ │ graph │
|
||
│ │ │ │───────┐ │
|
||
│ │ │ │ │ │
|
||
│ │ │ │<──────┘ │
|
||
│ │ │ │ │
|
||
│ │ K4 states │ │ │
|
||
│ │<───────────────────────────── │
|
||
│ │ │ │ │
|
||
│ │ Apply rules │ │ │
|
||
│ │──────┐ │ │ │
|
||
│ │ │ │ │ │
|
||
│ │<─────┘ │ │ │
|
||
│ │ │ │ │
|
||
│ │ Compute │ │ │
|
||
│ │ confidence │ │ │
|
||
│ │──────┐ │ │ │
|
||
│ │ │ │ │ │
|
||
│ │<─────┘ │ │ │
|
||
│ │ │ │ │
|
||
│ Verdict + │ │ │ │
|
||
│ explain │ │ │ │
|
||
│<────────────│ │ │ │
|
||
│ │ │ │ │
|
||
```
|
||
|
||
## Step-by-Step
|
||
|
||
### 1. Evaluation Request
|
||
|
||
Scanner submits findings for policy evaluation:
|
||
|
||
```http
|
||
POST /internal/evaluate HTTP/1.1
|
||
Content-Type: application/json
|
||
|
||
```
|
||
|
||
### 2. Policy Loading
|
||
|
||
Policy Engine loads the policy set from storage:
|
||
|
||
```yaml
|
||
# Policy Set: production
|
||
version: "stella-dsl@1"
|
||
name: production
|
||
description: Production deployment policy
|
||
|
||
rules:
|
||
- name: no-critical-reachable
|
||
description: Block critical CVEs with reachable code
|
||
condition: |
|
||
severity == 'critical' AND
|
||
reachability IN ['SR', 'RO', 'CR'] AND
|
||
vex_status != 'not_affected'
|
||
action: FAIL
|
||
|
||
- name: no-critical-unfixed
|
||
description: Block critical CVEs without fixes
|
||
condition: |
|
||
severity == 'critical' AND
|
||
fixed_version == null
|
||
action: FAIL
|
||
|
||
- name: warn-high-reachable
|
||
description: Warn on high CVEs with reachable code
|
||
condition: |
|
||
severity == 'high' AND
|
||
reachability IN ['SR', 'RO', 'CR']
|
||
action: WARN
|
||
|
||
- name: allow-vex-not-affected
|
||
description: Allow CVEs marked not affected by trusted issuer
|
||
condition: |
|
||
vex_status == 'not_affected' AND
|
||
vex_issuer_trust >= 0.8
|
||
action: PASS
|
||
|
||
defaults:
|
||
action: PASS
|
||
confidence_threshold: 0.7
|
||
```
|
||
|
||
### 3. VEX Consensus Query
|
||
|
||
Policy Engine queries VexLens for VEX statements:
|
||
|
||
```http
|
||
POST /internal/vex/consensus HTTP/1.1
|
||
Content-Type: application/json
|
||
|
||
```
|
||
|
||
Response with issuer consensus:
|
||
|
||
```json
|
||
{
|
||
"statements": [
|
||
{
|
||
"vulnerability": "CVE-2024-1234",
|
||
"status": "affected",
|
||
"issuers": [
|
||
{"name": "vendor-psirt", "trust": 0.95, "status": "affected"},
|
||
{"name": "osv", "trust": 0.7, "status": "affected"}
|
||
],
|
||
"consensus": "affected",
|
||
"confidence": 0.92
|
||
},
|
||
{
|
||
"vulnerability": "CVE-2024-5678",
|
||
"status": "not_affected",
|
||
"justification": "vulnerable_code_not_in_execute_path",
|
||
"issuers": [
|
||
{"name": "vendor-psirt", "trust": 0.95, "status": "not_affected"}
|
||
],
|
||
"consensus": "not_affected",
|
||
"confidence": 0.95
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 4. Reachability State Query
|
||
|
||
Policy Engine queries ReachGraph for K4 states:
|
||
|
||
```http
|
||
POST /internal/reachability/states HTTP/1.1
|
||
Content-Type: application/json
|
||
|
||
```
|
||
|
||
Response with K4 lattice states:
|
||
|
||
```json
|
||
{
|
||
"states": [
|
||
{
|
||
"package": "pkg:npm/lodash@4.17.20",
|
||
"state": "StaticallyReachable",
|
||
"evidence": {
|
||
"static": {"call_paths": 3, "entry_points": ["src/api/handler.js:45"]},
|
||
"runtime": null
|
||
}
|
||
},
|
||
{
|
||
"package": "pkg:npm/express@4.18.0",
|
||
"state": "RuntimeObserved",
|
||
"evidence": {
|
||
"static": {"call_paths": 12},
|
||
"runtime": {"invocations": 1547, "last_seen": "2024-12-29T09:00:00Z"}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 5. Rule Evaluation
|
||
|
||
Policy Engine evaluates each finding against rules:
|
||
|
||
```
|
||
Finding: CVE-2024-1234 in pkg:npm/lodash@4.17.20
|
||
- severity: critical
|
||
- reachability: StaticallyReachable (SR)
|
||
- vex_status: affected
|
||
- fixed_version: 4.17.21
|
||
|
||
Rule: no-critical-reachable
|
||
- condition: severity == 'critical' AND reachability IN ['SR', 'RO', 'CR'] AND vex_status != 'not_affected'
|
||
- evaluation: critical == 'critical' ✓ AND SR IN ['SR', 'RO', 'CR'] ✓ AND 'affected' != 'not_affected' ✓
|
||
- result: MATCH → FAIL
|
||
```
|
||
|
||
### 6. Confidence Scoring
|
||
|
||
Policy Engine computes confidence score based on 5 factors:
|
||
|
||
| Factor | Weight | Description |
|
||
|--------|--------|-------------|
|
||
| Reachability | 0.30 | K4 state certainty |
|
||
| Runtime | 0.25 | Runtime observation freshness |
|
||
| VEX | 0.20 | VEX issuer trust level |
|
||
| Provenance | 0.15 | SBOM completeness |
|
||
| Policy | 0.10 | Rule specificity |
|
||
|
||
```
|
||
Confidence = Σ(factor_weight × factor_score)
|
||
|
||
For CVE-2024-1234:
|
||
- Reachability: 0.30 × 0.7 (SR state) = 0.21
|
||
- Runtime: 0.25 × 0.0 (no runtime data) = 0.00
|
||
- VEX: 0.20 × 0.92 (affected consensus) = 0.18
|
||
- Provenance: 0.15 × 1.0 (complete SBOM) = 0.15
|
||
- Policy: 0.10 × 1.0 (exact rule match) = 0.10
|
||
|
||
Total Confidence: 0.64
|
||
```
|
||
|
||
### 7. Verdict Assembly
|
||
|
||
Policy Engine assembles final verdict:
|
||
|
||
```json
|
||
{
|
||
"verdict": "FAIL",
|
||
"confidence": 0.64,
|
||
"summary": {
|
||
"total_findings": 2,
|
||
"blocked": 1,
|
||
"warned": 0,
|
||
"passed": 1
|
||
},
|
||
"violations": [
|
||
{
|
||
"finding": {
|
||
"cve": "CVE-2024-1234",
|
||
"package": "pkg:npm/lodash@4.17.20",
|
||
"severity": "critical"
|
||
},
|
||
"rule": "no-critical-reachable",
|
||
"action": "FAIL",
|
||
"explain": {
|
||
"reason": "Critical CVE with reachable code path",
|
||
"factors": {
|
||
"reachability": "StaticallyReachable - 3 call paths from entry points",
|
||
"vex": "Marked as 'affected' by vendor-psirt (trust: 0.95)",
|
||
"remediation": "Upgrade lodash to 4.17.21"
|
||
}
|
||
}
|
||
}
|
||
],
|
||
"passed": [
|
||
{
|
||
"finding": {
|
||
"cve": "CVE-2024-5678",
|
||
"package": "pkg:npm/express@4.18.0",
|
||
"severity": "high"
|
||
},
|
||
"rule": "allow-vex-not-affected",
|
||
"action": "PASS",
|
||
"explain": {
|
||
"reason": "VEX statement confirms not affected",
|
||
"factors": {
|
||
"vex": "Not affected - vulnerable_code_not_in_execute_path",
|
||
"issuer": "vendor-psirt (trust: 0.95)"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## Data Contracts
|
||
|
||
### Policy Rule Schema
|
||
|
||
```typescript
|
||
interface PolicyRule {
|
||
name: string;
|
||
description?: string;
|
||
condition: string; // stella-dsl@1 expression
|
||
action: 'PASS' | 'WARN' | 'FAIL';
|
||
priority?: number;
|
||
exceptions?: Array<{
|
||
id: string;
|
||
expires?: string;
|
||
justification: string;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
### Verdict Schema
|
||
|
||
```typescript
|
||
interface PolicyVerdict {
|
||
verdict: 'PASS' | 'WARN' | 'FAIL';
|
||
confidence: number; // 0.0-1.0
|
||
summary: {
|
||
total_findings: number;
|
||
blocked: number;
|
||
warned: number;
|
||
passed: number;
|
||
};
|
||
violations: Array<ViolationDetail>;
|
||
warnings: Array<ViolationDetail>;
|
||
passed: Array<PassedDetail>;
|
||
metadata: {
|
||
policy_set: string;
|
||
policy_version: string;
|
||
evaluated_at: string;
|
||
evaluation_ms: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
## Error Handling
|
||
|
||
| Error | Recovery |
|
||
|-------|----------|
|
||
| Policy set not found | Use default policy or return 400 |
|
||
| VexLens timeout | Continue without VEX data, reduce confidence |
|
||
| ReachGraph timeout | Use Unknown state, reduce confidence |
|
||
| Invalid rule syntax | Skip rule, log error, continue |
|
||
| Conflicting rules | Apply highest priority rule |
|
||
|
||
## Observability
|
||
|
||
### Metrics
|
||
|
||
| Metric | Type | Labels |
|
||
|--------|------|--------|
|
||
| `policy_evaluation_total` | Counter | `policy_set`, `verdict` |
|
||
| `policy_evaluation_duration_ms` | Histogram | `policy_set` |
|
||
| `policy_rule_matches_total` | Counter | `rule`, `action` |
|
||
| `policy_confidence_score` | Histogram | `policy_set` |
|
||
|
||
### Trace Context
|
||
|
||
```
|
||
policy-evaluation
|
||
├── policy-load
|
||
├── vexlens-query
|
||
├── reachgraph-query
|
||
├── rule-evaluation
|
||
│ ├── rule-no-critical-reachable
|
||
│ ├── rule-no-critical-unfixed
|
||
│ └── rule-allow-vex-not-affected
|
||
├── confidence-scoring
|
||
└── verdict-assembly
|
||
```
|
||
|
||
## Related Flows
|
||
|
||
- [Scan Submission Flow](02-scan-submission-flow.md) - Parent flow
|
||
- [CI/CD Gate Flow](10-cicd-gate-flow.md) - Pipeline integration
|
||
- [Exception Approval Workflow](17-exception-approval-workflow.md) - Policy exceptions
|
||
- [Multi-Tenant Policy Rollout Flow](14-multi-tenant-policy-rollout-flow.md) - Policy distribution
|