Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
3.4 KiB
3.4 KiB
Effective Severity Selection
Last updated: 2025-11-25 (Docs Tasks Md.V)
Goal
Provide a deterministic, explainable way to pick the effective severity for a vulnerability or VEX observation when multiple signals exist (CVSS, KEV, exploit intel, VEX status, asset criticality, policy overrides).
Inputs
- Base scores: CVSS v3/v4 (base + temporal) and ecosystem-native severities (npm/yarn, PyPI, Maven). Missing scores are treated as
null. - Exploitability: KEV flag, EPSS probability, in-the-wild sightings, vendor exploit flags.
- VEX status:
not_affected,affected,fixed,under_investigation,unknown(per OpenVEX/CSAF/CycloneDX-VEX). - Context signals: asset criticality, exposure surface (
internet,intranet,airgap), runtime enablement flag, workload type. - Policy overrides: tenant/org rules (allow lists, waiver ids, force-upgrade requirements, SLA class).
Algorithm (deterministic)
- Normalize all scores to a 0–10 float with 3-decimal rounding; store provenance for each signal.
- VEX gate
not_affected→ effective severityNone(score 0). Short-circuit unless overrideforce_evaluate=true.fixed→ keep score but add mitigation note.
- Exploit boost: if
KEV=trueorEPSS >= 0.7, setexploit_boost = +1.0(cap at 10). Record reason. - Exposure clamp
airgap→ max score 7.0 unless overrideallow_airgap_breakglass.intranet→ no cap;internet→ no cap.
- Criticality weight: multiply by asset criticality weight (default 1.0; high=1.2, medium=1.0, low=0.8). Clamp 0–10.
- Policy override: apply explicit tenant rules (force severity, waive to
None, or constrain max). Overrides always log the applied rule id. - Bucket into severity bands (stable mapping):
| Score range (inclusive) | Band |
|---|---|
| 9.0–10.0 | Critical |
| 7.0–8.9 | High |
| 4.0–6.9 | Medium |
| 0.1–3.9 | Low |
| 0 | None |
All arithmetic uses decimal and rounds only when persisted or returned (3 decimals) to stay replayable.
Examples
- CVSS 7.5 + KEV + internet + high criticality → base 7.5 → +1.0 exploit → before clamp 8.5 → ×1.2 = 10.2 → clamp 10.0 → Critical.
- CVSS 5.0,
not_affectedVEX → short-circuit to None (score 0) with rationalevex:not_affected. - No CVSS, EPSS 0.2, exposure
airgap→ default score 0, band None; remains deterministic.
Observability
- Emit
stellaops.policy.effective_severityhistogram (0–10) with tagstenant,source,vex_status,kev,epss_bucket,criticality,override_id. - Log structured event
severity.selectedcontaining input signals, applied steps, final score/band. - Traces: span attribute
severity.scoreandseverity.band; link to upstream ingest span (traceparentpropagated).
Determinism & Offline posture
- No live network lookups during evaluation; KEV/EPSS/VEX feeds must be preloaded from frozen bundles.
- Sorting of tied severities: break ties by subject id (lexicographic) then source priority (
vex>kev>cvss>ecosystem). - All timestamps are UTC ISO-8601; caches keyed by
(tenant, subject, advisory).
Contract for consumers
- API and CLI MUST return both the raw inputs and the chosen band/score so auditors can replay decisions.
- Downstream UIs should surface the rationale chain (steps 2–6) and any overrides applied.
- Waivers must reference the override id that changed the severity.