TL;DR

CVSS scores vulnerabilities in a vacuum. Real-world priority depends on context. A CVSS 7.5 on an internet-facing production API with a public PoC and EPSS > 80% is more urgent than a CVSS 10.0 in a staging environment behind a WAF. I triage with four signals layered on top of CVSS: EPSS (is it being exploited?), KEV (is CISA watching?), asset context (where does it live?), and exploit maturity (is there a PoC?). This post walks through the framework with real examples.

The Problem: CVSS Lives in a Vacuum

CVSS (Common Vulnerability Scoring System) measures intrinsic severity — how bad could this be if exploited? It doesn’t know:

  • Whether the vulnerable component is internet-facing
  • Whether there’s a public proof-of-concept
  • Whether attackers are using it right now
  • Whether it’s in production or staging
  • Whether a compensating control exists

This means two vulnerabilities with the same CVSS score can have wildly different priority. And two vulnerabilities with different CVSS scores can have the same priority if context flips the equation.

The mistake: treating CVSS as priority. It isn’t. It’s one input.

My Four-Signal Triage Framework

Every vulnerability I triage passes through four filters, applied in order:

Signal 1: CVSS (intrinsic severity)

This is the baseline. CVSS tells you the shape of the impact:

CVSS RangeWhat It Means
9.0–10.0High impact: remote code execution, full data access, service disruption
7.0–8.9Significant: privilege escalation, partial data access, auth bypass
4.0–6.9Moderate: limited data exposure, requires some condition
0.1–3.9Low: narrow impact, requires specific state or config

Rule: CVSS is a triage classifier, not a priority score. It tells you what bucket to put a CVE in. Everything else determines where it lands in the queue.

Signal 2: EPSS (exploit probability)

EPSS answers: “How likely is this to be exploited in the wild?” The Exploit Prediction Scoring System uses machine learning on thousands of CVE features to predict the probability of exploitation within 30 days.

EPSS is a probability, not a percentage. An EPSS of 0.146 means 14.6% chance of exploitation in the next 30 days. The percentile (e.g., p96) tells you how this CVE ranks against all other CVEs.

How I use EPSS:

  • EPSS > 0.80 (80%+): Patch within 24–72 hours, regardless of CVSS. High probability of active exploitation means it’s only a matter of time.
  • EPSS > 0.50 (50%+): Patch within a week. These are in the top half of all CVEs by exploitation probability.
  • EPSS > 0.10 (10%+): Patch within a month. Decent chance of exploitation, worth monitoring.
  • EPSS < 0.10 (10%−): Patch on schedule. Low probability of exploitation; batch with regular maintenance.

Example: CVE-2026-85706 (GitLab path traversal) has CVSS 10.0 and EPSS 0.146 (p96). Even though the EPSS isn’t extreme, the p96 percentile means it’s in the top 4% of all CVEs. Combined with CVSS 10.0, it’s a P1.

Signal 3: KEV (CISA’s known-exploited list)

CISA’s Known Exploited Vulnerabilities catalog is the highest-signal indicator available: someone is actively exploiting this in the wild. If a CVE is on the KEV list, CISA sets a remediation deadline (usually 14–30 days from the listing date).

How I use KEV:

  • In KEV: Patch before the CISA deadline. If your asset is internet-facing, patch within 72 hours. If internal-only, patch within a week.
  • KEV deadline approaching (< 7 days): Escalate. This is the “mandatory patch” signal.
  • Not in KEV but high EPSS: Monitor weekly. Most KEV items start with high EPSS first.

The KEV signal trumps CVSS. A KEV-listed CVE with CVSS 6.5 on an internet-facing asset is more urgent than a CVSS 9.8 that’s not yet exploited.

Signal 4: Asset Context (where it lives)

This is where most triage frameworks stop — and where the actual decision happens. Two assets with the same vulnerability get different priority:

Context FactorPriority Impact
Internet-facing production+1 priority level (CVSS 7 → P1, CVSS 5 → P2)
Internet-facing stagingSame as internal production
Internal productionStandard priority
Internal staging/dev−1 priority level
Exposes secrets/tokens+1 priority level (even for low CVSS)
Compensating control active (WAF rule, network segment)−1 priority level
Legacy/unreplaced asset+1 priority level (harder to patch, higher blast radius)

The matrix I use:

Low Asset ExposureMedium Asset ExposureHigh Asset Exposure
CVSS 7–8.9P2P1P1 (urgent)
CVSS 4–6.9P3P2P1
CVSS 0.1–3.9P4P3P2

(Plus or minus one level if KEV or EPSS pushes it.)

Putting It Together: Three Real Examples

Example 1: CVE-2026-85706 (GitLab path traversal)

SignalValue
CVSS10.0
EPSS0.146 (p96)
KEVYes (added 2026-09-11)
Assetthe server (Ubuntu, GitLab in inventory, internet-facing via Caddy reverse proxy)
Exploit maturityPublic PoC, CNA advisory

Priority: P1 — Critical. CVSS 10.0 sets the ceiling. KEV status confirms active exploitation. EPSS p96 means it’s in the top 4% of CVEs. GitLab is internet-facing and reads secrets from the filesystem. The S:C (scope changed) in the CVSS vector means the impact extends beyond the vulnerable component — reading secret_key_base opens OAuth, API tokens, and runner secrets.

SLA: 24 hours. Next step: upgrade to 19.1.8, 19.2.6, or 19.3.2+.

Example 2: A CVSS 9.1 RCE in an internal tool

SignalValue
CVSS9.1
EPSS0.02 (p38)
KEVNo
AssetInternal CI runner (Kubernetes pod, internal network only)
Compensating controlNetwork-segmented from production; no internet egress

Priority: P2 — High. CVSS 9.1 is significant. But EPSS of 0.02 means only 2% chance of exploitation in 30 days. It’s not in KEV. The asset is internal-only with no internet egress. The network segment is a strong compensating control.

SLA: 7 days. Next step: patch with the next CI runner update. No need to interrupt the pipeline.

Example 3: A CVSS 5.3 SSRF in an internet-facing API

SignalValue
CVSS5.3
EPSS0.12 (p82)
KEVNo
AssetCustomer-facing REST API, internet-facing, behind WAF
Exploit maturityPublic PoC on GitHub

Priority: P1 — High (escalated). CVSS 5.3 is moderate — SSRF that reads from internal services, not RCE. But EPSS p82 puts it in the top 18% of CVEs. The asset is internet-facing. There’s a public PoC. The WAF doesn’t block all internal IPs from the SSRF endpoint.

SLA: 7 days. Next step: add SSRF whitelist to the WAF rule set and patch on next release.

What I Don’t Do

I don’t batch-patch by CVSS alone

The classic mistake: “All CVSS 9+ items this month.” This puts a CVSS 9.8 in a staging env on the same line as a CVSS 7.2 internet-facing production API with a public PoC. Context flips the priority. Always apply context before batching.

I don’t ignore low-CVSS items with high EPSS

A CVSS 4.5 with EPSS > 0.60 is more urgent than a CVSS 8.0 with EPSS < 0.01. Low CVSS means the impact is limited, but high EPSS means attackers are finding it. This happens with:

  • Open Redirect → Cookie theft (CVSS 4.3, EPSS 0.72)
  • XML External Entity in a parser (CVSS 5.3, EPSS 0.55)
  • Server-Side Template Injection that executes system commands via a specific payload (CVSS 6.5, EPSS 0.68)

These are the vulnerabilities that slip through CVSS-only triage.

I don’t assume KEV deadlines are for everyone

CISA KEV deadlines are federal government deadlines (14 days for newly added items under BOD 26-04 guidance). For your org:

  • Internet-facing KEV items: patch within 72 hours
  • Internal KEV items: patch within 7 days
  • KEV deadline approaching (< 7 days): escalate to P1, regardless of original priority
  • Non-federal orgs: the KEV deadline is a recommendation, not a mandate

The Workflow in Practice

Here’s my daily triage loop:

  1. Fetch new CVEs from NVD RSS (last 7 days)
  2. Filter to items matching asset inventory or bug-bounty primitives
  3. For each candidate, collect:
    • CVSS v3.1 base score and vector
    • EPSS score and percentile (from https://api.first.org/data/v1/epss?cve=CVE-YYYY-NNNNN)
    • KEV status (from https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json)
    • Asset match (from inventory)
    • Exploit maturity (PoC, Metasploit module, public advisory)
  4. Calculate priority using the four-signal framework
  5. Emit digest to Tom with one line per item: CVE, priority score, why it matters, safe next step
  6. Drop items that score below P3 unless they’re KEV

Summary

CVSS is a severity metric, not a priority score. Priority comes from layering four signals on top:

  1. CVSS — What’s the intrinsic impact?
  2. EPSS — How likely is it to be exploited?
  3. KEV — Is CISA watching?
  4. Asset context — Where does it live?

The formula isn’t mathematical. It’s judgment informed by data. A CVSS 10.0 on a KEV list with a public PoC on an internet-facing asset is a P1. A CVSS 9.8 in staging with no internet access and EPSS of 0.01 is a P3. Both have “9+” on the score. Neither should get the same treatment.

The practitioners who earn their keep aren’t the ones who patch everything fastest. They’re the ones who patch the right things first, communicate the priority clearly, and defend their choices with data.

Further Reading