Skip to content
scolly2683Public

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

nist-lev

A small, dependency-free Python implementation of LEV — Likely Exploited Vulnerabilities, the metric defined in NIST CSWP 41 (Mell & Spring, May 2025).

Why LEV

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.

Install

pip install .            # or: pip install -e ".[test]" for the test extras

Usage

Feed 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.99

The math

Equation 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.

Tests

pip install -e ".[test]"
pytest -q

The 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).

References

  • 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.

License

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages