Back to projects
Mar 15, 2026
4 min read

CVE Feed Dashboard

Live CVE feed dashboard built with Streamlit and the NVD API v2, enriched with EPSS exploit-prediction scores. Features a dark terminal aesthetic, log-scale EPSS vs CVSS scatter, colour-coded HTML vulnerability table, 1-hour cache, CSV export, and 27 Playwright end-to-end tests.

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

LayerTechnology
UI / AppStreamlit
VisualisationPlotly
Data processingPython, Pandas
CVE dataNVD API v2 (NIST)
Exploit predictionEPSS API (FIRST.org)
CacheStreamlit @st.cache_data (1h TTL)
TestingPlaywright (Python)
DeploymentStreamlit 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.