A public-facing vulnerability intelligence dashboard that pulls live CVE data from the NVD API v2 and enriches each entry with an EPSS (Exploit Prediction Scoring System) score — showing not just how severe a vulnerability is theoretically, but how likely it is to be exploited in the wild.
The Problem
CVSS score alone is a poor triage signal. A CVSS 9.8 vulnerability with a 0.1% EPSS score is very different from a CVSS 7.5 with a 40% EPSS score. Security teams that sort by CVSS only are working the wrong list. This dashboard puts both dimensions on screen together, making prioritisation decisions visible and defensible.
Architecture
NVD API v2 Client
Fetches CVEs published in the last N days via the NVD REST API, handling pagination (2000-result pages) and rate limiting. With an API key the 6-second inter-request delay is reduced, cutting full-load time from ~33s to ~3s for 500 CVEs.
EPSS Enrichment
Each CVE ID is sent to the FIRST.org EPSS API in batches of up to 100. The enrichment layer joins EPSS score and percentile back onto the NVD dataset. Both APIs are public and require no authentication for standard usage.
Caching
@st.cache_data with a 1-hour TTL holds the enriched DataFrame in memory. Filter changes, sidebar interactions, and reruns return the cached object instantly — no repeat API calls during a session.
Key Features
- Log-scale scatter — EPSS (y, log scale) vs CVSS (x, linear) with severity colour coding; log scale prevents low-EPSS entries from collapsing into a flat line at zero
- Colour-coded HTML table — custom-rendered table with per-row severity colouring and direct NVD links for each CVE, overriding Streamlit’s BaseWeb CSS defaults via
st.markdowninjection - KPI cards — total CVEs, high/critical count, mean CVSS, max EPSS, rendered as metric tiles
- Sidebar controls — adjustable lookback window (1–30 days), CVSS and EPSS threshold sliders, severity multiselect
- CSV export — one-click download of the filtered dataset
Tech Stack
| Layer | Technology |
|---|---|
| UI / App | Streamlit |
| Visualisation | Plotly |
| Data processing | Python, Pandas |
| CVE data | NVD API v2 (NIST) |
| Exploit prediction | EPSS API (FIRST.org) |
| Cache | Streamlit @st.cache_data (1h TTL) |
| Testing | Playwright (Python) |
| Deployment | Streamlit Community Cloud |
Engineering Highlights
- Log scale y-axis: EPSS scores span four orders of magnitude (0.001 to 0.9+). Linear scale renders 90% of points as a flat line at zero. Log scale reveals the true distribution and makes outliers — the real priorities — visually obvious.
- BaseWeb CSS overrides: Streamlit’s default dataframe renderer does not support per-row background colours. The table is rendered as raw HTML via
st.markdown(..., unsafe_allow_html=True)with inline styles, working around the framework constraint entirely. - Playwright e2e tests: 27 tests covering app load, sidebar control interaction, KPI card rendering, chart presence, data table output, CSV export trigger, and zero-error state. Tests run against a live Streamlit dev server spun up in the test fixture.
- NVD pagination + rate limiting: the NVD API returns at most 2000 results per request and enforces a 6 requests/30s limit without an API key. The client paginates automatically and sleeps between requests to stay within limits.
- Batch EPSS enrichment: rather than one request per CVE, IDs are chunked into batches of 100 and sent as comma-separated query parameters, reducing EPSS enrichment from N round trips to N/100.
Open Source
Public repository — built as a portfolio project demonstrating applied vulnerability management domain knowledge, Python API client development, and end-to-end test coverage on a Streamlit application.