Article · Vulnerability prioritisation
Severity is a bad prioritiser — KEV and EPSS as a CI gate
Almost every dependency-scanning setup starts the same way:
fail-on: high. It is the obvious default, and it quietly
makes two mistakes at once. It stops builds for a large slice of
everything ever published, and it
still lets through roughly one in nine vulnerabilities that are
confirmed to be exploited in the wild. Here is the arithmetic,
and what to gate on instead.
Updated 21 Aug 2026 · by the depproof team
The number that makes the case
CISA’s Known Exploited Vulnerabilities catalog is the closest thing the industry has to a list of vulnerabilities that are definitely being attacked. Nothing goes on it because it looks dangerous. CISA’s three criteria are that the vulnerability has a CVE ID assigned, that “there is reliable evidence that the vulnerability has been actively exploited in the wild”, and that there is a clear remediation action for it.
So: how many of those confirmed-exploited vulnerabilities would a
fail-on: high gate actually stop? Joining the KEV catalogue
against NVD’s severity data on 21 August 2026 —
1,673 entries — gives this:
| NVD base severity | KEV entries | Share | Caught by fail-on: high |
|---|---|---|---|
| Critical | 598 | 35.7% | yes |
| High | 884 | 52.8% | yes |
| Medium | 187 | 11.2% | no |
| Low | 4 | 0.2% | no |
191 confirmed-exploited vulnerabilities — 11.4% of the catalogue — are rated below high. Every one of them is a vulnerability someone has been observed exploiting, and every one of them merges cleanly through a high-and-above gate. That is not a rounding error at the bottom of a long tail; it is one in nine.
Check it yourself. Both sources are public and
neither needs an account. Where NVD holds more than one score for a
CVE, this takes NVD’s own Primary assessment rather than
the CNA’s — take the first score instead and 208 entries land below
high rather than 191, which moves the number the wrong way for our
argument, not the right one. The counts also move as CISA adds
entries, so expect slightly different totals on a later date.
# every KEV entry, straight from CISA
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json \
| jq '.count, .catalogVersion'
# the same CVEs with NVD severity attached, newest CVSS version first,
# preferring NVD's own Primary score over a CNA-supplied one
curl -s 'https://services.nvd.nist.gov/rest/json/cves/2.0?hasKev&resultsPerPage=2000' \
| jq '[.vulnerabilities[].cve.metrics
| (.cvssMetricV40 // .cvssMetricV31 // .cvssMetricV30) as $m
| (($m | map(select(.type == "Primary")) | first) // ($m | first))
| .cvssData.baseSeverity] | group_by(.) | map({(.[0]): length}) | add' And it is too wide at the same time
The mirror-image problem is the one your team feels every week. FIRST, which maintains both CVSS and EPSS, puts numbers on it: acting on CVSS High and above means acting on “approximately the top 48% of published vulnerabilities”, while “in any given 30 day period, we are tracking anywhere from 1.5% to 3% of published vulnerabilities with exploitation activity” (FIRST, Using EPSS).
Put the two halves together and the default gate looks worse than arbitrary. It stops builds on something close to half of everything published, which is why teams end up raising the threshold, adding blanket ignores, or clicking through the failure — and it still misses one in nine of the vulnerabilities that are known to be under attack. The effort is real and the coverage is not.
None of this is CVSS being broken. It is CVSS being used for a job it was never built to do. A base score describes intrinsic properties — how the vulnerability is reached, what it takes to exploit, what an attacker gains if they do — and it is deliberately stable over time and across environments. That stability is exactly what makes it a poor queue: the score cannot move when the world does. A proof-of-concept lands on GitHub, a module for it turns up in an off-the-shelf attack toolkit, a botnet picks it up — and the number is the same number it was on the day it was assigned.
Three scores, three different questions
The fix is not to replace CVSS. It is to stop asking it a question it cannot answer, and to add the two public signals that can.
| CVSS | EPSS | CISA KEV | |
|---|---|---|---|
| Question | How bad if exploited? | How likely to be exploited? | Was it exploited? |
| Direction | Intrinsic property | Forecast, next 30 days | Historical record |
| Shape | 0–10 ordinal ranking | 0–1 calibrated probability | Binary — on the list or not |
| Changes | Rarely, if ever | Daily | When CISA adds an entry |
| Coverage | Most CVEs, when scored | Every published CVE | 1,673 entries total |
EPSS is, in FIRST’s words, “a data-driven machine-learning model that estimates the probability that a published CVE will be exploited in the wild in the next 30 days”, published daily for every CVE as a 0–1 probability with a percentile ranking, free and openly accessible. The property that matters operationally is that it is calibrated: CVEs scoring near 5% are exploited approximately 5% of the time, and CVEs scoring near 30% approximately 30% of the time. That means a score is a statement about frequency you can reason with, not a vibe rendered as a decimal.
And because it updates daily, movement is itself a signal. A finding that goes from 2% to 40% over a week is telling you something changed in the threat environment — new exploit code, new tooling, new interest. A static score assigned at disclosure cannot detect that, no matter how carefully it was assigned.
CISA already made this change — in June
If this still sounds like a vendor argument, note who else has stopped prioritising on severity. On 10 June 2026 CISA issued Binding Operational Directive 26-04, “Prioritizing Security Updates Based on Risk”, which supersedes and revokes both BOD 19-02 and BOD 22-01 — the directive that created the KEV catalogue in the first place. Its stated purpose is to focus “patching efforts on the areas of highest risk rather than treating all vulnerabilities and systems equally”.
Remediation urgency for US federal civilian agencies is now determined by four variables. It is worth reading the list twice:
- Asset Exposure — is the vulnerable asset publicly exposed?
- KEV Status — is the CVE on the KEV catalogue?
- Exploit Automation — can an adversary automate every step needed to exploit it?
- Technical Impact — does an adversary gain partial or total control of the asset?
A CVSS severity band is not one of the four. The closest thing to it is Technical Impact, which the directive itself describes as “similar to the Common Vulnerability Scoring System (CVSS) base score’s concept of ‘severity’” — and then reduces to a binary question of partial versus total control. Exploitation status, exposure and automatability carry the rest.
BOD 26-04 binds federal civilian agencies, not you — agencies must begin remediating on its timelines by 7 December 2026. But CISA encourages all organisations to adopt the same risk-based approach, and if you are writing a vulnerability-management policy for an audit or a regulated customer, “our gate follows the model in BOD 26-04” is a much better sentence than “we fail on high”.
Reading EPSS without fooling yourself
EPSS is easy to adopt and easy to misuse, and the misuses are predictable enough that FIRST documents them. Three worth knowing before you put a number in a config file.
- 0.5 is not “the top half”. The base rate of exploitation is roughly 2–3%, so the mean EPSS score sits around 2.8% and the median around 0.7%. Setting a threshold at 0.5 because it feels like a midpoint deprioritises almost the entire published population. This is the single most common mistake.
- Anchor on effort you already accept. FIRST publishes the translation: a programme that acts on CVSS Critical is spending roughly what the 90th EPSS percentile (≈0.04) costs, and one that acts on CVSS High and above is spending roughly what ≈0.008 costs. Same workload, better selection. Start there and tune against your own capacity.
- Do not multiply EPSS by CVSS. FIRST calls this score laundering: EPSS is a calibrated probability, CVSS is an ordinal ranking with no empirical calibration, and their product “has no interpretable meaning”. Keep them as separate rules that each retain their meaning — which also means you can explain precisely why a build failed.
One more subtlety, because it looks like a contradiction the first time you hit it: a KEV-listed vulnerability can carry a low EPSS score. KEV records that exploitation happened; EPSS forecasts the next 30 days across the whole population. A KEV entry from years ago with no ongoing activity drifts back toward what its features predict. FIRST’s rule of thumb is the sensible one — when something appears on KEV, treat it as actively exploited regardless of EPSS, and let EPSS carry everything KEV does not cover, which is the overwhelming majority of the population at any moment.
What this means for dependencies specifically
Here is the part most articles on this subject leave out, and it matters if you are gating an SCA scan rather than a fleet of servers.
KEV is not primarily a dependency catalogue. It is dominated by vendor products and operating systems — Microsoft alone accounts for 385 of the 1,673 entries, followed by Cisco (96), Apple (94), Adobe (80), Google (72) and Oracle (45). A large majority of the catalogue will never appear in a lockfile, because it is firmware, appliances, browsers and enterprise software rather than libraries you compile against.
What does appear is the category that has caused every
dependency fire drill of the last five years. Apache Log4j2’s
CVE-2021-44228 was added on 10 December 2021, the Spring
Framework’s CVE-2022-22965 on 4 April 2022, Apache Struts’
CVE-2017-5638 on 3 November 2021 — and Apache is the
seventh-largest vendor in the catalogue with 40 entries. So the honest
characterisation for an SCA gate is this:
- KEV fires rarely on a dependency tree — and on the day it does, it is the most important line in the report, whatever severity band it landed in. That is exactly the profile you want in a build-stopping rule: near-zero false-alarm cost, enormous value when it triggers.
- EPSS carries the everyday load. It scores every published CVE, including the long tail of library advisories that will never be famous enough for a KEV entry, and it reorders your queue continuously rather than annotating it once.
- Severity still has a job. Keep it — as the floor under the other two, not as the whole gate.
A gate built from all three
Concretely, in a GitHub Actions workflow. Each rule is independent and OR-ed with the others, so a finding that matches any one of them stops the build:
# .github/workflows/depproof.yml
name: depproof
on: [pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: depproof/depproof-action@v1
with:
fail-on: 'high' # the floor, not the whole gate
fail-on-kev: 'true' # known-exploited, at any severity
fail-on-epss: '0.04' # ≈ 90th percentile — CVSS-Critical effort
fail-on-unknown: 'true' # ungraded is not the same as low
enrich-online: 'true' # exploitation data from your own hub
enrich-mode: 'targeted' # required for EPSS scores
report-to: ${{ secrets.DEPPROOF_HUB_URL }}
report-token: ${{ secrets.DEPPROOF_HUB_TOKEN }} Four things about that block are deliberate, and each one is a decision you should make consciously rather than inherit:
-
fail-on-kevignores severity by design, which is the entire point — it is the rule that catches the 191 entries the severity threshold drops. -
fail-on-unknowncloses the ungraded gap. An advisory with no score is not a low-severity advisory; it is an advisory nobody has graded yet. That gap has widened since NIST stopped enriching most CVEs in April 2026, so treating “absent” as “low” now fails open on a growing share of findings. - Exploitation data comes from your own hub. depproof
runs on your own infrastructure; your source and findings never leave
it, and there is no vendor API in the path — the data the gate needs
is served by your self-hosted hub. The two
enrich-modevalues differ in how much that hub is told about an individual scan: the default tells it less, whiletargetedis what EPSS scores require and gives you a precise per-scan record. The trade between them is privacy, not quality. - It is fail-closed. If the hub cannot be reached and a rule depends on it, the build stops rather than reporting a clean result on data it never received. A pass you did not earn is the failure mode worth designing against.
Two adjustments worth knowing. If your team is drowning, add
fail-only-if-fix-available: 'true' so the gate reflects what
can actually be acted on today — it narrows every rule except
the known-exploited one, because a confirmed-exploited finding with no
fix available is the most urgent thing in the report, not the least. And
if you would rather ease in, run with
fail-on: 'critical' and fail-on-kev: 'true'
first: that combination is less noisy than the
fail-on: high you have today and still catches the
exploited-but-medium findings.
These inputs landed in the August 2026 scanner release. Full input reference is in the docs, and the same rules are available on the container and in GitLab CI.
What none of this tells you
Three honest limits, because a prioritisation article that oversells its signals is just a different kind of noise.
Neither signal knows your code. KEV says exploitation was observed somewhere in the world; EPSS estimates a population-level probability. Neither knows whether the vulnerable code path is reachable from your application’s entry points, or whether a control you already run neutralises it. That judgement is yours, and it is worth writing down — a waiver with a justification, an owner and an expiry, and VEX for telling downstream consumers — rather than being re-derived by whoever is on triage next quarter.
A better gate cannot rescue a worse input. Every rule here operates on findings, and findings come from a resolved dependency graph. If the graph was never fully resolved, the most sophisticated prioritisation in the world runs over a list that was short for the wrong reason. That is why the same release added a fidelity rule that fails a build when a graph could not be resolved — the only rule that can fail on an empty findings list, which is precisely the case worth failing on. Prioritisation is the second problem; completeness is the first.
Every prioritisation scheme is a prediction. FIRST’s own framing is the fairest summary available: “patch all KEV” and “patch all CVSS Critical” are both making a predictive claim about future exploitation — EPSS simply states its claim explicitly and measures it. There is no perfect approach here, only ones that are more or less wrong, and the ones that publish their error rate are the ones you can argue with.
Frequently asked questions
What is the difference between CVSS, EPSS and KEV?
They answer three different questions. CVSS is a severity score: how bad the vulnerability would be if someone exploited it. EPSS is a probability from FIRST.org: how likely it is that this CVE will be exploited in the wild in the next 30 days. CISA KEV is a catalogue of vulnerabilities with reliable evidence of past exploitation in the wild. Severity is a property of the vulnerability, exploitation probability is a forecast about the threat environment, and KEV membership is a historical record. Only the last two are about whether anyone is actually attacking it.
Is CVSS a measure of risk?
No, and it does not claim to be. A CVSS base score describes the intrinsic characteristics of a vulnerability — how it is reached, what it takes to exploit, what an attacker gains — and it is deliberately constant over time and across environments. Risk depends on things a base score cannot see: whether the vulnerable code path is reachable in your application, whether the asset is exposed, and whether anyone is exploiting it today. Using the base score alone as a priority queue substitutes a severity ranking for a risk judgement.
Does a CVSS high-and-above gate catch actively exploited vulnerabilities?
Mostly, but not all of them. As of 21 August 2026 the CISA KEV catalog held 1,673 entries, of which 191 — about 11% — carry an NVD base severity below high (187 medium, 4 low). Those are vulnerabilities with confirmed, observed exploitation that a fail-on-high threshold would let through. In the other direction, FIRST reports that CVSS high and above covers roughly the top 48% of published vulnerabilities, while only 1.5–3% of published vulnerabilities show exploitation activity in any given 30-day window. A severity gate is therefore both too wide and too narrow at the same time.
What EPSS threshold should I use?
Start from the effort level you already accept rather than from a number that feels right. FIRST publishes two translations: if your programme currently acts on CVSS Critical, the equivalent effort level is roughly the 90th EPSS percentile, about 0.04; if you act on CVSS High and above, the equivalent is around 0.008. Both look surprisingly low because the base rate is low — the mean EPSS score is around 2.8% and the median around 0.7%, so a threshold of 0.5 does not select "the top half", it selects a tiny sliver. Treat any threshold as a starting point to tune against your own remediation capacity.
Should I multiply EPSS by CVSS to get a risk score?
No. FIRST explicitly warns against this and calls it score laundering: EPSS is a calibrated probability, while CVSS is an ordinal ranking with no empirical calibration, so their product has no interpretable meaning. Combine them as separate rules instead — fail on known-exploited status, fail above an exploitation probability, fail above a severity — so each rule keeps the meaning it was built with, and you can explain to an auditor exactly why a given build stopped.
Is CISA KEV useful for open-source dependency scanning?
Yes, but as a rare and decisive signal rather than an everyday one. The catalogue is dominated by vendor products and operating systems — Microsoft, Cisco, Apple, Adobe and Google alone account for more than four in ten entries — and most of it will never appear in a lockfile. What does appear is the Log4Shell class of event: CVE-2021-44228 in Apache Log4j2 was added on 10 December 2021, CVE-2022-22965 in the Spring Framework on 4 April 2022, CVE-2017-5638 in Apache Struts on 3 November 2021. For a dependency scan, KEV membership is uncommon, and on the day it fires it is the most important line in the report.
Do KEV and EPSS tell me whether my application is actually exploitable?
No. Both are statements about a vulnerability in the world, not about your application. KEV says exploitation was observed somewhere; EPSS estimates population-level probability. Neither knows whether the vulnerable code path is reachable from your entry points, or whether a control you already run blocks it. That determination is engineering judgement, and the responsible thing is to record it — a waiver with a justification, an owner and an expiry, and VEX when downstream consumers need to know whether you are affected.
Does gating on exploitation data mean sending my findings to a vendor?
It does not have to. depproof gates on exploitation data served by your own self-hosted hub, which runs on your infrastructure — your source and findings never leave it, and there is no vendor API in the path. Two enrichment modes are available, and they differ in how much the hub is told about an individual scan: the default tells it less, while targeted mode is what EPSS scores require and gives you a precise per-scan record on the hub. Both run inside your own network; the trade between them is privacy, not quality.
Sources
- CISA — Known Exploited Vulnerabilities catalog (JSON), catalogVersion 2026.08.21 — the 1,673 entries, vendor counts and the dates for CVE-2021-44228, CVE-2022-22965 and CVE-2017-5638. The catalogue criteria are published alongside it.
- NVD API — CVEs flagged
hasKev— the severity bands joined against the KEV set on 21 August 2026. - FIRST — Using EPSS — the 1.5–3% exploitation base rate, the mean and median scores, the CVSS-Critical (≈0.04) and CVSS-High (≈0.008) effort translations, the “top 48%” figure, the KEV-plus-EPSS rule of thumb, and the score-laundering warning.
- FIRST — EPSS and Why EPSS? — the model’s definition and 30-day window, daily publication, and the calibration property.
- CISA — BOD 26-04, “Prioritizing Security Updates Based on Risk” (10 June 2026) — the four variables, the revocation of BOD 19-02 and BOD 22-01, the Technical Impact framing, and the 7 December 2026 date.
- FIRST — CVSS v4.0 specification — what a base score measures and why it is constant across time and environments.
The KEV and EPSS figures are dated because they move. Counts last computed 21 August 2026; if you are reading this much later, re-run the two commands above before quoting them.
Keep reading
- What software composition analysis actually is — where prioritisation sits in the wider SCA picture.
- OSV vs CVE for dependency scanning — why the NVD stopped grading most CVEs, and what that does to a severity-based gate.
- Zero findings is not zero risk — the input problem that no prioritisation scheme can fix.
- VEX and vulnerability waivers explained — how to record the findings you have decided not to fix today.
- End-of-life dependencies — the risk none of these three scores, because it has no CVE to score.
- Dependency governance beyond per-repo scans — what changes when the gate has to hold across an org rather than a repo.
General information for engineering and security teams, not legal or compliance advice. depproof is not affiliated with CISA, NIST, FIRST or the CVE Program; names are used nominatively.