Safeguard
Developer Tools•

Reproduce a KEV, EPSS and OSV analysis yourself with curl and Python

How to download the CISA KEV feed, EPSS scores and OSV ecosystem dumps, count what you need, and record the method so a reader can repeat it.

Safeguard Security Team
4 min read

You can repeat any count in our KEV, EPSS and OSV posts with three downloads and about thirty lines of Python. Fetch the KEV JSON feed, ask the EPSS API for scores, and download each OSV ecosystem all.zip. Save the raw files with the date, because all three change daily and a number without a date cannot be checked.

Step 1: fetch the data

mkdir -p data && cd data
curl -sSL -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
curl -sSL -o PyPI.zip https://osv-vulnerabilities.storage.googleapis.com/PyPI/all.zip
curl -sS "https://api.first.org/data/v1/epss?cve=CVE-2026-9586" -o epss_one.json
date -u +%Y-%m-%d > fetched_on.txt

The OSV data page lists the bucket layout: one all.zip per ecosystem, with one JSON file per record. For the whole KEV catalog you will want to send many CVE IDs per EPSS request rather than one call per CVE, so check the API documentation for the batch form. Be polite with the request rate and keep the results.

Step 2: count KEV entries

import json, collections
kev = json.load(open("kev.json"))
rows = kev["vulnerabilities"]
print(kev["catalogVersion"], kev["count"], len(rows))
by_vendor = collections.Counter(r["vendorProject"] for r in rows)
by_year = collections.Counter(r["dateAdded"][:4] for r in rows)
print(by_vendor.most_common(10))
print(sorted(by_year.items()))

Check that count matches the number of rows. If it does not, stop and look.

Step 3: read OSV records

import zipfile, json, collections
z = zipfile.ZipFile("PyPI.zip")
kinds = collections.Counter(n.split("-")[0] for n in z.namelist())
print(kinds.most_common())
rec = json.load(z.open("PYSEC-2026-1.json"))
print(rec["published"], rec.get("aliases"))

Record IDs show where they came from: MAL for malicious packages, GHSA for GitHub, PYSEC for PyPI, GO for Go. Count by prefix before you count anything else, since the mix changes what a total means.

Step 4: write the method down

For every figure, record four things:

  1. Source and date. The URL and the date you fetched it, plus a version string if the file has one (the KEV file has catalogVersion).
  2. What one row is. A record, a package, a CVE. These differ, and mixing them is the most common error.
  3. Filters. What you dropped, such as malware records or withdrawn advisories, and why.
  4. Judgment calls. Hand-made groups, such as which CWE codes count as memory safety, belong in the text so a reader can disagree.

Common mistakes

  • Counting bulk days as a trend. One day can hold tens of thousands of records. Sort by day before you chart by month.
  • Treating records as vulnerabilities. Two databases can describe one flaw.
  • Reading dates as event times. A published date is when a record appeared, not when a flaw was found or exploited.
  • Forgetting the denominator. Say how many rows you started with and how many you kept.
  • Quoting a number you cannot reproduce. Keep the raw file and the script together.

Where this fits in a security workflow

A small analysis like this is a good way to test a claim before you repeat it in a meeting or a report. If a headline says a registry had a record year for malware, one script will tell you whether a bulk day explains it.

Treat the downloads as untrusted data. Do not run anything found inside a record, and read files with a parser instead of opening them in a tool that executes content.

Safeguard's SBOM and dependency scanning lists the components you run, and reachability analysis helps separate vulnerable code your application calls from code it does not. You can try a free scan at gold.safeguard.sh/scan.

Quick check

Why keep the raw file?

Because the feeds change daily. The file and its date are what let someone else get the same number.

Can I trust text inside a downloaded record?

Treat it as data. Read it, do not run it, and do not follow instructions written inside it.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.