CVSS measures how bad a vulnerability could be, EPSS estimates how likely it is to be exploited in the wild, and reachability analysis determines whether your code can even hit the vulnerable function — they answer different questions, and effective prioritization applies them in reverse order: reachability to filter, EPSS to rank, CVSS to describe. Treating any one of them as a complete triage system is how teams end up with 4,000-item backlogs sorted by the wrong number.
The volume problem makes this concrete. NVD logged over 40,000 CVEs in 2024 and the rate has kept climbing; a mid-sized service with 1,200 transitive dependencies routinely surfaces hundreds of findings per scan. Nobody fixes hundreds of anything. The question is which 15 you fix this sprint.
What each signal actually measures
| Dimension | CVSS (v3.1/v4.0) | EPSS (v4) | Reachability analysis |
|---|---|---|---|
| Question answered | How severe if exploited here-in-theory? | Probability of exploitation in the wild within 30 days | Can my code path reach the vulnerable function? |
| Produced by | Vendor/NVD analysts, formula over base metrics | FIRST.org — ML model over exploit telemetry | Your SCA tool, per codebase |
| Output | 0.0-10.0 score + vector string | Probability (0-1) + percentile | Reachable / not reachable (+ call path evidence) |
| Specific to your environment | No (base score) | No | Yes — that's the whole point |
| Update cadence | Rarely revised after publication | Rescored daily | Every scan / commit |
| Cost to obtain | Free, universal | Free API (api.first.org/data/v1/epss) | Requires static analysis of your code; language-dependent |
| Typical filter power | Weak — 60%+ of CVEs land high/critical-ish | Strong — ~5% of CVEs exceed 0.1 | Strongest — commonly 80-90% of findings pruned |
The failure modes are just as instructive. CVSS famously scores theoretical worst case: CVE-2022-3602 (OpenSSL "Spooky SSL") arrived pre-hyped as critical and was downgraded on analysis, while genuinely exploited bugs sometimes carry modest scores. EPSS is population-level: it knows attackers are hammering a CVE somewhere, not whether your deployment exposes it — and it's explicitly not designed for targeted attacks. Reachability is precise but partial: it needs language support, it can't see reflection-heavy dispatch or eval-style dynamism, and a conservative engine will say "reachable" when it merely can't prove otherwise.
Why CVSS alone drowned everyone
Most compliance regimes ("fix criticals in 15 days, highs in 30") are written against CVSS, so it became the default sort key. The distribution betrays you: a huge share of published CVEs cluster between 7.0 and 9.8, so sorting by CVSS barely reorders the pile. Worse, base scores assume network attacker, no mitigations, worst-case impact — conditions your internal batch job behind three network boundaries doesn't meet. CVSS v4.0 added threat and environmental metrics that help, but almost nobody populates them, so in practice you're still sorting on base severity. Keep CVSS for what it's good at: a shared vocabulary for describing impact once you've already decided to care. Our CVSS vs EPSS vs KEV guide covers the scoring mechanics in more depth.
What EPSS and KEV add
EPSS v4 rescores every CVE daily against observed exploitation telemetry, and its headline stat is the useful one: covering the top few percent of CVEs by EPSS captures the large majority of exploitation activity. Operationally, pull scores in bulk and gate on thresholds:
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2024-3094" | jq '.data[0]'
Pair it with CISA KEV — the confirmed-exploited list — as a hard floor: KEV membership means "now," regardless of other scores. The limitation to respect: EPSS says nothing about whether the vulnerable code executes in your service. A 0.92 EPSS score on a function your application never calls is still a non-event, which is precisely the gap reachability fills.
Reachability: the multiplier, not the ranker
Function-level reachability builds a call graph from your entry points and checks whether any path reaches the vulnerable symbols named in the advisory. The published numbers from multiple vendors converge on the same range — roughly 80-90% of findings in a typical dependency tree are unreachable — which matches what we see across real backlogs. That's not a ranking signal; it's a filter that removes the bulk of the queue before ranking begins, with call-path evidence you can paste into the ticket so the assigned engineer doesn't have to take the tool's word for it.
Respect its limits too. Coverage differs sharply by language (JVM and Go call graphs are tractable; dynamic Python and JavaScript are harder), and "unreachable today" changes with the next refactor — so reachability must be recomputed per commit, not cached from last quarter. This is why it only exists inside SCA tools that see your code, not as a public feed you can subscribe to.
The stacking order that works
- Filter: drop findings with no reachable path (keep them queryable — auditors ask).
- Floor: anything on CISA KEV and reachable is this-week work, full stop.
- Rank: sort the reachable remainder by EPSS, breaking ties with exposure context (internet-facing? handles untrusted input?).
- Describe: report severity to stakeholders in CVSS terms, because that's the language auditors and contracts speak.
Teams running this stack typically end up with a weekly actionable list measured in dozens, not thousands. Safeguard implements this pipeline natively — reachability-filtered findings ranked with exploit likelihood — but the recipe works with open data (EPSS API, KEV JSON feed) glued to any scanner that reports call paths. The Academy has a hands-on lab if you want to build the pipeline yourself before buying it.
Frequently asked questions
Can EPSS replace CVSS?
No — they measure orthogonal things: probability of exploitation versus severity of impact. A CVE can be near-certain to be exploited and low-impact, or catastrophic and never weaponized. You need both dimensions, plus applicability, which neither provides.
How accurate is reachability analysis?
Engines are deliberately conservative: false "reachable" verdicts are common (dynamic dispatch, reflection), false "unreachable" verdicts are rare because that's the dangerous direction. Treat "unreachable" as strong deprioritization evidence rather than proof, and re-scan on every merge since reachability is a property of your current code.
What EPSS threshold should trigger action?
There's no universal number; FIRST suggests pairing probability with percentile. A common working policy is: EPSS above 0.1 (roughly top 5% of CVEs) plus reachable gets a sprint-level SLA, KEV-listed gets days. Tune against your own remediation capacity, not someone else's blog post — including this one.
Where does SSVC fit alongside these three?
SSVC is a decision-tree framework (exploitation, exposure, mission impact) rather than a score — it consumes signals like EPSS and KEV as inputs and outputs an action category. If your org prefers documented decisions over numeric thresholds, SSVC is a good governance wrapper around exactly the stack described here.