Files
git.stella-ops.org/docs/policy/effective-severity.md
StellaOps Bot 9f6e6f7fb3
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
up
2025-11-25 22:09:44 +02:00

3.4 KiB
Raw Permalink Blame History

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)

  1. Normalize all scores to a 010 float with 3-decimal rounding; store provenance for each signal.
  2. VEX gate
    • not_affected → effective severity None (score 0). Short-circuit unless override force_evaluate=true.
    • fixed → keep score but add mitigation note.
  3. Exploit boost: if KEV=true or EPSS >= 0.7, set exploit_boost = +1.0 (cap at 10). Record reason.
  4. Exposure clamp
    • airgap → max score 7.0 unless override allow_airgap_breakglass.
    • intranet → no cap; internet → no cap.
  5. Criticality weight: multiply by asset criticality weight (default 1.0; high=1.2, medium=1.0, low=0.8). Clamp 010.
  6. Policy override: apply explicit tenant rules (force severity, waive to None, or constrain max). Overrides always log the applied rule id.
  7. Bucket into severity bands (stable mapping):
Score range (inclusive) Band
9.010.0 Critical
7.08.9 High
4.06.9 Medium
0.13.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_affected VEX → short-circuit to None (score 0) with rationale vex:not_affected.
  • No CVSS, EPSS 0.2, exposure airgap → default score 0, band None; remains deterministic.

Observability

  • Emit stellaops.policy.effective_severity histogram (010) with tags tenant, source, vex_status, kev, epss_bucket, criticality, override_id.
  • Log structured event severity.selected containing input signals, applied steps, final score/band.
  • Traces: span attribute severity.score and severity.band; link to upstream ingest span (traceparent propagated).

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 26) and any overrides applied.
  • Waivers must reference the override id that changed the severity.