# CVE severity vs exploitability: what actually matters

## TL;DR

CVSS measures worst-case technical severity, not your risk. What decides your patch order: is the vulnerable code reachable in your stack, is there a known exploit, is it on the CISA known-exploited list, what does EPSS say, and is the asset exposed to the internet. A CVSS 9.8 in a dev-only dependency you never ship is less urgent than a 7.5 with a public exploit on your public-facing box.

```text
CVE severity vs exploitability: what actually matters
```

## Use this when

- A scanner hands you 50 CVEs and you need a patch order
- Someone asks why a CVSS 9.8 is scheduled behind a CVSS 7.2
- You are setting patch SLAs and need a defensible ranking method
- A "critical" CVE turns out to be unreachable and you need to explain why it waits

## Not for this skill when

- You just want to know what CVSS numbers mean (thats a definitions question)
- You are triaging one specific new CVE end to end (use the triage workflow skill)
- You need a CVE summary written for leadership (different audience, different skill)

## Steps

### 1. Write down the CVSS score, then put it aside

Record the base score so you have it, but treat it as one input, not the answer. CVSS assumes the worst plausible deployment; yours is usually narrower.

```bash
echo "CVE-2026-12345 | CVSS 9.1 | product [name] | version [x.y.z]" | tee -a vuln-priorities.txt
```

Expected: one line per CVE in your working list. This list is what you will re-rank in the next steps.

### 2. Check the CISA known-exploited catalog

Anything on this list is being exploited in the wild right now. That single fact outweighs any score math; KEV items go to the top of the queue, period.

```bash
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json | python3 -c "import json,sys; d=json.load(sys.stdin); ids=[v['cveID'] for v in d['vulnerabilities']]; print('CVE-2026-12345' in ids)"
```

Expected: True or False. True means patch now, no further analysis needed for prioritization.

### 3. Pull the EPSS score

EPSS estimates the probability of exploitation in the next 30 days. Use it to rank everything not on the KEV list; a 7.0 with EPSS 0.8 beats a 9.5 with EPSS 0.01.

```bash
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2026-12345"
```

Expected: JSON with an EPSS score between 0 and 1. Scores above roughly 0.5 deserve same-week attention; below 0.1 can usually wait for the normal cycle.

### 4. Assess reachability and exposure

Ask two questions. Is the vulnerable code path reachable in your deployment (imported, called, feature enabled). And is the asset exposed (internet-facing, authenticated-only, internal). Reachable plus internet-facing is the top tier; unreachable plus internal is the bottom.

```bash
grep -rn "[vulnerable-import-or-endpoint]" src/ --include="*.py" | head -20
```

Expected: call sites, or nothing. Combine with your exposure knowledge: public API, internal tool, or batch job nobody can reach.

### 5. Rank with a simple matrix and patch in order

Sort by: KEV membership first, then reachable plus high EPSS, then reachable plus low EPSS, then unreachable. Write the order down and work it top to bottom. Revisit weekly; EPSS scores move.

```bash
sort -t'|' -k3 -r vuln-priorities.txt | head -20
```

Expected: your list ordered so the highest-risk items surface first. Adjust the sort key to match however you recorded EPSS and KEV flags.

### Variant: which CVE should I patch first

The short version for a single decision: check KEV, check EPSS, check reachability. Two of three saying "bad" means patch this week. This is the 5-minute answer when someone pings you with one CVE ID.

### Variant: why is a critical CVE low priority for us

The explanation template: state the CVSS, then the three downgrades (not on KEV, low EPSS, unreachable or unexposed), then the scheduled date. Leadership accepts "critical but not exploitable here" when the evidence is written down; they push back when it is a vibe.

### Variant: CVSS 10 but no exploit, do I care

Yes, but on a schedule, not as an emergency. No known exploit today doesnt mean none tomorrow, and scanners will keep flagging it. Patch it in the normal cycle and note the reasoning so nobody re-escalates it next month.

## Why this happens

CVSS was designed to be context-free so everyone scores the same bug the same way. That is its strength and its weakness: it cant know your architecture. Exploitability signals (KEV, EPSS, public exploits) plus your own reachability and exposure are the context CVSS lacks, and risk lives in that context, not in the number.

## Edge cases and pitfalls

- EPSS lags brand-new CVEs; a fresh CVE with no score yet should be treated as unknown, not safe.
- KEV is US-government scoped; something exploited only outside the US may take time to appear.
- Reachability changes: a refactor can expose a code path you previously marked unreachable; re-check after big merges.
- CVSS environmental metrics exist but almost nobody fills them in; dont wait for them.
- Chained vulnerabilities: two mediums can combine into something worse; the matrix ranks singles, use judgment for chains.
- Scanner severity inflation: some scanners bump scores with their own modifiers; always trace back to the base CVSS plus your exploitability checks.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_WAeFVLs6Jg1XkeQPBqw6Eg
