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