A small, dependency-free Python implementation of LEV — Likely Exploited Vulnerabilities, the metric defined in NIST CSWP 41 (Mell & Spring, May 2025).
EPSS gives you a CVE's exploitation probability over the next 30 days. That's forward-looking and it decays: a vulnerability that was widely exploited 14 months ago reads near-zero today. CISA KEV is the opposite — high precision, low recall: it lists confirmed exploitation, not likely-past exploitation.
LEV fills the gap between them. It integrates a CVE's historical EPSS time series into a single number: the probability the vulnerability has already been exploited at some point in the past. A CVE that sat at EPSS 0.9 for ten months and has since cooled off will have a LEV near 1.0 even though its EPSS-current is 0.01.
pip install . # or: pip install -e ".[test]" for the test extrasFeed each function a CVE's daily EPSS history as (date, score) pairs. Order and gaps are tolerated;
EPSS is forward-filled across gaps (a score persists until the next daily snapshot).
from datetime import date
from nist_lev import lev, lev2, composite, history_start
series = [
(date(2024, 1, 1), 0.90), # hot early...
(date(2024, 10, 27), 0.01), # ...cold now
]
lev(series, through=date(2024, 11, 26)) # ≈ 0.99 — Eq. 1 (windowed, the cheap default)
lev2(series, through=date(2024, 11, 26)) # ≈ 0.99 — Eq. 2 (per-day, the precision upgrade)
# "Best single number" for prioritisation: max(epss_current, LEV, KEV).
composite(epss_current=0.01, lev_value=0.99, in_kev=False) # -> 0.99Equation 1 — windowed LEV (the cheap primary). EPSS is a 30-day probability, so tile the history into non-overlapping 30-day windows and treat each as an independent Bernoulli trial:
LEV(v, d0, dn) = 1 - PROD_i ( 1 - epss(v, di) * weight(di, dn) )
weight(di, dn) = min(dn - di, 30) / 30
Each factor (1 - epss*weight) is "not exploited in this window"; the product is "never exploited"; and
1 - product is "exploited in at least one window." weight() is 1.0 for every full window and
down-weights only the final partial one. NIST states this as a lower bound.
Equation 2 — LEV2 (per-day). Drops the windowing and compounds a per-day hazard
P1 = 1 - (1 - epss)**(1/30). It equals Eq. 1 exactly on any series constant across full windows, and
is the more faithful per-day integral where EPSS drifts inside a window or on a high-EPSS partial tail —
at ~2.7× the cost. Eq. 1 is the sensible default for bulk compute; lev2() is here for research/eval.
See the module docstring in nist_lev/__init__.py for the full derivation,
including exactly how and why Eq. 1 and Eq. 2 diverge.
pip install -e ".[test]"
pytest -qThe tests are hand-computed fixtures derived directly from the CSWP 41 formulas (no public worked example with specific EPSS values exists, so each expected value is worked out by hand in the test comments).
- NIST CSWP 41 — Likely Exploited Vulnerabilities: A Proposed Metric for Vulnerability Exploitation Probability, Peter Mell & Jonathan Spring, May 2025.
- Reference implementation — https://github.com/RiskBasedPrioritization/LEV.
MIT © 2026 Donal Scollan. Independent project — not affiliated with, sponsored by, or endorsed by NIST or any company or organization. "NIST" and "EPSS" are referenced for interoperability only.