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 Range | What It Means |
|---|---|
| 9.0–10.0 | High impact: remote code execution, full data access, service disruption |
| 7.0–8.9 | Significant: privilege escalation, partial data access, auth bypass |
| 4.0–6.9 | Moderate: limited data exposure, requires some condition |
| 0.1–3.9 | Low: 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 Factor | Priority Impact |
|---|---|
| Internet-facing production | +1 priority level (CVSS 7 → P1, CVSS 5 → P2) |
| Internet-facing staging | Same as internal production |
| Internal production | Standard 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 Exposure | Medium Asset Exposure | High Asset Exposure | |
|---|---|---|---|
| CVSS 7–8.9 | P2 | P1 | P1 (urgent) |
| CVSS 4–6.9 | P3 | P2 | P1 |
| CVSS 0.1–3.9 | P4 | P3 | P2 |
(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)
| Signal | Value |
|---|---|
| CVSS | 10.0 |
| EPSS | 0.146 (p96) |
| KEV | Yes (added 2026-09-11) |
| Asset | the server (Ubuntu, GitLab in inventory, internet-facing via Caddy reverse proxy) |
| Exploit maturity | Public 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
| Signal | Value |
|---|---|
| CVSS | 9.1 |
| EPSS | 0.02 (p38) |
| KEV | No |
| Asset | Internal CI runner (Kubernetes pod, internal network only) |
| Compensating control | Network-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
| Signal | Value |
|---|---|
| CVSS | 5.3 |
| EPSS | 0.12 (p82) |
| KEV | No |
| Asset | Customer-facing REST API, internet-facing, behind WAF |
| Exploit maturity | Public 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:
- Fetch new CVEs from NVD RSS (last 7 days)
- Filter to items matching asset inventory or bug-bounty primitives
- 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)
- Calculate priority using the four-signal framework
- Emit digest to Tom with one line per item: CVE, priority score, why it matters, safe next step
- 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:
- CVSS — What’s the intrinsic impact?
- EPSS — How likely is it to be exploited?
- KEV — Is CISA watching?
- 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.