18 KiB
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:
POST /internal/evaluate HTTP/1.1
Content-Type: application/json
{
"scan_id": "scan-7f3a9b2c-...",
"tenant_id": "acme-corp",
"policy_set": "production",
"image": {
"name": "docker.io/library/nginx",
"digest": "sha256:abc123..."
},
"findings": [
{
"cve": "CVE-2024-1234",
"severity": "critical",
"cvss": 9.8,
"package": "pkg:npm/lodash@4.17.20",
"fixed_version": "4.17.21"
},
{
"cve": "CVE-2024-5678",
"severity": "high",
"cvss": 7.5,
"package": "pkg:npm/express@4.18.0",
"fixed_version": "4.18.2"
}
]
}
2. Policy Loading
Policy Engine loads the policy set from storage:
# 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:
POST /internal/vex/consensus HTTP/1.1
Content-Type: application/json
{
"product": "docker.io/library/nginx:1.25",
"vulnerabilities": ["CVE-2024-1234", "CVE-2024-5678"]
}
Response with issuer consensus:
{
"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:
POST /internal/reachability/states HTTP/1.1
Content-Type: application/json
{
"image_digest": "sha256:abc123...",
"packages": [
"pkg:npm/lodash@4.17.20",
"pkg:npm/express@4.18.0"
]
}
Response with K4 lattice states:
{
"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:
{
"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
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
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 - Parent flow
- CI/CD Gate Flow - Pipeline integration
- Exception Approval Workflow - Policy exceptions
- Multi-Tenant Policy Rollout Flow - Policy distribution