Every publicly known software vulnerability gets a CVE ID (Common
Vulnerabilities and Exposures, the industry's universal name for a specific
flaw, like CVE-2021-44228 for Log4Shell). The official record for each one is
published as JSON, and there are roughly 340,000 of them. This repo turns
that pile into a handful of plain CSV files — one row per CVE — and refreshes
them every day so you can open the whole history of disclosed vulnerabilities in
Excel, pandas, or DuckDB without writing a single API call.
If you're learning vulnerability management or detection engineering, this is meant to be a low-friction place to start poking at real data.
The authoritative vulnerability data is free, but it's awkward to get at if you just want to look around:
- The CVE Program publishes every record as an individual JSON file in one giant git repository, CVEProject/cvelistV5. Cloning it is several gigabytes and ~360,000 files, and each file follows the nested CVE JSON 5.x schema where the interesting fields (severity, weakness, affected products) live in different places depending on who filled them in.
- The National Vulnerability Database (NVD) — NIST's enriched view of the same CVEs — has a JSON API, but it's rate-limited, it paginates, and it sometimes lags behind the source.
So a simple question like "show me every actively-exploited CVE with a critical
CVSS score that was published in 2024" normally means cloning gigabytes,
learning the JSON shape, reconciling two different data publishers inside each
record, de-duplicating, and only then filtering. This repo does that work once a
day and hands you the answer-ready table instead. No API key, no rate limits, no
JSON parsing — just read_csv.
A CVE record is assembled by a few different organizations, and a few standards bodies score and classify it. You'll see all of these in the columns:
- CNA — CVE Numbering Authority. The organization that assigned the ID and
wrote the base record (a vendor like Microsoft, a project like the Linux
kernel, or a coordinator like MITRE). It's in
assigner_short_name. - CVSS — Common Vulnerability Scoring System.
A 0.0–10.0 severity score plus a "vector" string describing how the attack
works. Columns
cvss_version,cvss_base_score,cvss_vector. - CWE — Common Weakness Enumeration. The type of
flaw, as a catalog ID — e.g.
CWE-79is cross-site scripting,CWE-416is a use-after-free. Columncwe_ids_all. Look any ID up at cwe.mitre.org. - CPE — Common Platform Enumeration. A
structured identifier for an affected product and version, like
cpe:2.3:o:linux:linux_kernel:6.1:*:*:*:*:*:*:*. It's what scanners and asset inventories match against. Columncpes. - KEV — CISA's Known Exploited Vulnerabilities catalog.
The short list of CVEs that are known to be exploited in the wild — in other
words, the "patch these first" list. Columns
cisa_kev,kev_date_added. - SSVC — Stakeholder-Specific Vulnerability Categorization.
CISA's prioritization signals: is it being exploited, can it be automated, how
bad is the impact. Columns
ssvc_exploitation,ssvc_automatable,ssvc_technical_impact. - ADP / Vulnrichment. Many records arrive from the CNA without a score or a
CPE, so CISA's Vulnrichment program
back-fills the CVSS, CWE, CPE, and SSVC. Where a value came from the CNA versus
this enrichment is recorded in
cvss_source(cnaorcisa-adp).
Total rows: 357,300 · Shards: 3 · generated 2026-08-11T08:07:56+00:00
| shard | rows | size | years |
|---|---|---|---|
cve_2022_and_before |
203,605 | 80.06 MB | 1999–2022 |
cve_2023_to_2025 |
112,372 | 71.51 MB | 2023–2025 |
cve_2026_to_now |
41,323 | 30.79 MB | 2026 |
One row per CVE, 19 columns. Everything is text (it's CSV), so cast scores and
the KEV flag to numbers before you compare them. Multi-valued fields (several
products, several CWEs) are packed into one cell separated by |.
| group | columns | what it tells you |
|---|---|---|
| identity | cve_id, assigner_short_name |
which CVE, and who wrote it |
| timing | date_published, date_updated |
when it went public and when it last changed (calendar dates, UTC; date_updated is blank if unchanged since publish) |
| description | title, description_en |
the human-readable summary of the flaw |
| how bad | cvss_version, cvss_base_score, cvss_vector, cvss_source |
the severity score, the vector, and where the score came from |
| what kind | cwe_ids_all |
the weakness type(s), e.g. CWE-787 | CWE-125 |
| being exploited? | cisa_kev, kev_date_added, ssvc_exploitation, ssvc_automatable, ssvc_technical_impact |
is it on the KEV list (1/0), and CISA's SSVC triage |
| what it affects | vendors, products, cpes |
affected vendor/product names, plus machine-matchable CPE 2.3 strings |
A note on severity: there's no cvss_base_severity column because the word
(LOW/MEDIUM/HIGH/CRITICAL) is just a band of the number — 0.1–3.9 is Low,
4.0–6.9 Medium, 7.0–8.9 High, 9.0–10.0 Critical — so you derive it from
cvss_base_score.
A note on date_updated: it's the last time a publisher (the CNA, or CISA via
Vulnrichment) actually changed the record, read from the record's own provider
metadata rather than the CVE Program's cveMetadata.dateUpdated — which a 2024
bulk migration stamped onto ~46% of records, burying their real dates. It's left
blank when the record hasn't changed since publication.
These three are where most of the prioritization signal lives, so they're worth understanding rather than skimming past.
cvss_vector — how the attack works, in shorthand. A vector like
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H (that's Log4Shell) is a
slash-separated list of the metrics that produced the score. The ones worth
reading at a glance:
- AV — Attack Vector: where the attacker has to be.
Network (reachable over the internet — the dangerous one),Adjacent (same local network),Local (needs local access),Physical. - AC — Attack Complexity:
Low orHigh — how much has to line up for it to work. - PR — Privileges Required:
None /Low /High — does the attacker need an account first. - UI — User Interaction:
None orRequired — does a victim have to click something. - C / I / A — impact to Confidentiality, Integrity, Availability, each
High /Low /None.
The thing to watch for is AV:N/AC:L/PR:N/UI:N with high C/I/A — remotely
reachable, easy, no login, no click, total compromise. That combination is why
Log4Shell is a 10.0. You don't have to decode it by hand: paste the whole vector
into the
FIRST CVSS calculator to see it
expanded, and read the full
CVSS v3.1 spec (or
v4.0, which adds more letters but works the
same way) for what each metric means.
SSVC — CISA's "what should I do about it" triage. Three decision points, straight from the record:
| column | values |
|---|---|
ssvc_exploitation |
none · poc (proof-of-concept exists) · active (in the wild) |
ssvc_automatable |
yes · no (can an attacker automate it at scale) |
ssvc_technical_impact |
partial · total (how much control it gives) |
Read together they tell you how hard to run: active + yes + total is a
drop-everything bug. Background and the full decision tree are in
CISA's SSVC guide. (Empty means the record hasn't
been triaged — most non-enriched CVEs.)
KEV — the authoritative "exploited in the wild" list. cisa_kev = 1 means
the CVE is on
CISA's Known Exploited Vulnerabilities catalog,
and kev_date_added is when CISA added it. US federal civilian agencies are
legally required to remediate KEV entries by a deadline, which is why it's the
de-facto "patch this first, argue later" list for everyone. It's small (~1,600
of the 340k) and high-signal — a good first filter.
Clone the repo (or download the three CSVs) and point any tool at
data/shards/*.csv. They share the same columns, so reading all three together
gives you the full dataset.
DuckDB is the easiest because it reads the CSVs directly and globs all three at once:
-- Every actively-exploited, critical-severity CVE from 2024, newest first
SELECT cve_id, cvss_base_score, products, description_en
FROM 'data/shards/*.csv'
WHERE cisa_kev = '1'
AND TRY_CAST(cvss_base_score AS DOUBLE) >= 9.0
AND date_published >= '2024-01-01'
ORDER BY date_published DESC;pandas:
import glob, pandas as pd
df = pd.concat(pd.read_csv(f, dtype=str) for f in glob.glob("data/shards/*.csv"))
# all use-after-free bugs (CWE-416) in the Linux kernel
kernel_uaf = df[df["cwe_ids_all"].str.contains("CWE-416", na=False)
& df["vendors"].str.contains("Linux", na=False)]
# things on CISA's KEV list, sorted by when they were added
kev = df[df["cisa_kev"] == "1"].sort_values("kev_date_added")A few questions a junior analyst can answer in one line with this data: which
CVEs are known to be exploited right now (cisa_kev = '1'); what the most
common weakness types are this year (group by cwe_ids_all); every vulnerability
in a product you run (filter products or cpes); how a specific CVE was scored
and by whom (cvss_base_score + cvss_source). For Excel, see the next section.
excel/merge-shards.pq is a Power Query (M) script that
pulls the three shards straight from this repo and stacks them into one table you
can refresh on demand. On Windows Excel:
- Data → Get Data → From Other Sources → Blank Query
- Home → Advanced Editor, clear it, and paste the contents of
excel/merge-shards.pq - Done → Close & Load — you get a worksheet table of every CVE, with the score, KEV flag, and dates already typed.
- Data → Refresh All any time to pull the latest daily build.
(Mac Excel doesn't have Power Query — use the DuckDB or pandas snippets above.)
A GitHub Actions workflow runs every day at 07:00 UTC. Most days it only fetches
the handful of records that changed upstream (it follows the CVE Program's own
deltaLog.json change feed) and commits a small update; the daily diff in
data/changes/ tells you exactly which CVEs were added, updated, or removed. The
KEV list is re-checked every run, so a CVE's cisa_kev flag flips from 0 to
1 the day CISA adds it, even if the vulnerability record itself didn't change.
If the change feed ever rolls past where we left off, the build rebuilds the
whole dataset from scratch automatically, so it's self-healing.
To keep the files small and readable, the schema is trimmed to what's useful at a
glance. If you need more, the full record is always one click away at
https://www.cve.org/CVERecord?id=<CVE-ID> or in the
cvelistV5 JSON.
- Reference links aren't a column — a record can have 50 advisory/patch/PoC URLs, which doesn't belong in a CSV cell. Go to the CVE record for those.
- Per-version ranges and git commit hashes aren't kept — kernel CVEs alone
can list dozens, and they'd swamp the row.
cpescarries version info where it's available. - Several fields are dropped because they're derivable or redundant: the
CVSS severity word (from the score), the CWE name (look the
CWE-####up at cwe.mitre.org), the year (from thecve_id), and a few plumbing fields.cpeswas kept on purpose: it's the only machine-matchable product identifier, and only ~14% of CVEs carry one, so it can't be reconstructed fromvendors/products.
The data is split into three CSVs by age (cve_2022_and_before,
cve_2023_to_2025, cve_2026_to_now) only because GitHub caps a single file
at 100 MB and the full dataset is ~180 MB. There's nothing clever to it — read
all three and you have everything. The split points are tunable
(--band-uppers) if you rebuild it yourself, and the build warns if any file
creeps toward the cap.
- PUBLISHED records only. CVEs that are RESERVED (an ID exists but no details yet) or REJECTED (withdrawn) aren't here. A CVE that gets rejected later is removed from the dataset.
- A daily snapshot, not real-time. Upstream updates roughly every 7 minutes; this refreshes once a day.
- Compact encodings:
cisa_kevis1/0, the date columns are calendar dates (YYYY-MM-DD, no time of day), and thecpes/multi-value lists are capped at 50 entries with a…(+N)marker. - English descriptions only.
- Not a replacement for NVD's full CPE configurations. If you're doing
serious automated asset matching with complex version ranges, treat
cpeshere as a starting filter and confirm against NVD. - This is an independent project that re-packages public CVE data; it isn't affiliated with MITRE, NIST/NVD, or CISA.
pip install -r requirements.txt # runtime needs only: requests
python -m src.build --mode full --limit 2000 --no-commit # quick dry run
pip install -r requirements-dev.txt && python -m pytest -q # the test suiteThe daily refresh is .github/workflows/daily-update.yml. To force a full
rebuild from scratch: Actions → Daily CVE refresh → Run workflow → mode =
full. The build uses only the Python standard library plus requests; pandas
is only needed for the tests.