Skip to content

Instantly share code, notes, and snippets.

@quinn-dougherty
Created March 10, 2025 08:25
Show Gist options
  • Select an option

  • Save quinn-dougherty/5f92b50e017bfbc3c44c42d9cf0f6cdf to your computer and use it in GitHub Desktop.

Select an option

Save quinn-dougherty/5f92b50e017bfbc3c44c42d9cf0f6cdf to your computer and use it in GitHub Desktop.
Count CVEs pertaining to docker
#!/usr/bin/env python3
"""
Docker CVE Counter - Counts Docker-related CVEs by year
This script queries the National Vulnerability Database (NVD) API to find
Docker-related vulnerabilities and provides a yearly count breakdown.
"""
import requests
import json
import time
from collections import Counter, defaultdict
import re
from datetime import datetime
import argparse
class DockerCVECounter:
def __init__(self, api_key=None):
self.api_key = api_key
self.base_url = "https://services.nvd.nist.gov/rest/json/cves/2.0"
self.headers = {"User-Agent": "DockerCVECounter/1.0"}
if api_key:
self.headers["apiKey"] = api_key
def search_cves(self, keyword, start_index=0):
"""Search for CVEs with the given keyword."""
params = {
"keywordSearch": keyword,
"startIndex": start_index,
"resultsPerPage": 2000, # Maximum allowed value
}
try:
response = requests.get(self.base_url, params=params, headers=self.headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error fetching CVEs: {e}")
return None
def get_all_docker_cves(self):
"""Retrieve all Docker-related CVEs from the NVD database."""
all_cves = []
start_index = 0
while True:
print(f"Fetching CVEs starting from index {start_index}...")
result = self.search_cves("docker", start_index)
if (
not result
or "vulnerabilities" not in result
or not result["vulnerabilities"]
):
break
vulns = result["vulnerabilities"]
all_cves.extend(vulns)
if len(vulns) < 2000:
break
start_index += 2000
# Respect rate limits
print("Waiting to respect API rate limits...")
time.sleep(
6
) # NVD recommends at least 6 seconds between requests without API key
return all_cves
def filter_relevant_cves(self, cves):
"""
Filter CVEs to ensure they're truly Docker-related.
This helps exclude false positives where 'docker' is mentioned but not the main subject.
"""
docker_cves = []
docker_patterns = [
re.compile(r"\bdocker\b", re.IGNORECASE),
re.compile(
r"\bmoby\b", re.IGNORECASE
), # Docker's open-source engine project
re.compile(r"\bdocker engine\b", re.IGNORECASE),
re.compile(r"\bdocker daemon\b", re.IGNORECASE),
re.compile(r"\bdockerd\b", re.IGNORECASE),
]
for cve_entry in cves:
cve_item = cve_entry.get("cve", {})
cve_id = cve_item.get("id", "Unknown")
descriptions = cve_item.get("descriptions", [])
# Get the English description
description = ""
for desc in descriptions:
if desc.get("lang") == "en":
description = desc.get("value", "")
break
# Check if the description indicates this is primarily about Docker
if any(pattern.search(description) for pattern in docker_patterns):
# Further filter to reduce false positives
# Skip entries that are primarily about other software that just happens to mention Docker
skip_patterns = [
re.compile(
r"when using docker", re.IGNORECASE
), # Often about other software used with Docker
re.compile(
r"in a docker", re.IGNORECASE
), # Often about software running in Docker, not Docker itself
re.compile(
r"docker image", re.IGNORECASE
), # Often about vulnerable images, not Docker itself
]
# Skip if the CVE is primarily about another product
if description and any(
skip_pattern.search(description) for skip_pattern in skip_patterns
):
# But keep it if it explicitly mentions Docker engine/daemon components
if not description or not re.search(
r"\bdocker engine\b|\bdockerd\b|\bmoby\b",
description,
re.IGNORECASE,
):
continue
docker_cves.append(cve_entry)
return docker_cves
def categorize_by_year(self, cves):
"""Categorize CVEs by the year they were published."""
yearly_counts = Counter()
cve_details_by_year = defaultdict(list)
for cve_entry in cves:
cve_item = cve_entry.get("cve", {})
cve_id = cve_item.get("id", "Unknown")
published_date = cve_item.get("published", "")
try:
# Parse the published date (format: 2023-01-01T00:00:00.000)
year = datetime.fromisoformat(
published_date.replace("Z", "+00:00")
).year
yearly_counts[year] += 1
# Store CVE details for later reference
cve_details_by_year[year].append(
{
"id": cve_id,
"published": published_date,
"base_score": self.get_base_score(cve_item),
}
)
except (ValueError, TypeError):
print(f"Could not parse published date for {cve_id}: {published_date}")
return yearly_counts, cve_details_by_year
def get_base_score(self, cve_item):
"""Extract the CVSS base score from a CVE item."""
metrics = cve_item.get("metrics", {})
# Try to get CVSS 3.x score first
cvss_v3 = metrics.get("cvssMetricV31", []) or metrics.get("cvssMetricV30", [])
if cvss_v3:
return cvss_v3[0].get("cvssData", {}).get("baseScore", "N/A")
# Fall back to CVSS 2.0
cvss_v2 = metrics.get("cvssMetricV2", [])
if cvss_v2:
return cvss_v2[0].get("cvssData", {}).get("baseScore", "N/A")
return "N/A"
def display_results(self, yearly_counts, cve_details):
"""Display the results in a readable format."""
print("\n===== Docker-related CVEs by Year =====")
# Sort years in descending order (newest first)
for year, count in sorted(yearly_counts.items(), reverse=True):
print(f"{year}: {count} CVEs")
print(f"\nTotal Docker-related CVEs found: {sum(yearly_counts.values())}")
# Calculate average per year
if yearly_counts:
avg_per_year = sum(yearly_counts.values()) / len(yearly_counts)
print(f"Average CVEs per year: {avg_per_year:.2f}")
# Detailed analysis option
detailed = input(
"\nShow detailed breakdown of CVEs for a specific year? (Enter year or 'n' to skip): "
)
if detailed.lower() != "n" and detailed.isdigit():
year_to_show = int(detailed)
if year_to_show in cve_details:
print(f"\n===== Detailed Docker CVEs for {year_to_show} =====")
for cve in cve_details[year_to_show]:
print(
f"{cve['id']} - CVSS Score: {cve['base_score']} - Published: {cve['published']}"
)
else:
print(f"No data available for {year_to_show}")
def main():
parser = argparse.ArgumentParser(description="Count Docker-related CVEs by year")
parser.add_argument(
"--api-key", help="NVD API key (optional, allows higher rate limits)"
)
args = parser.parse_args()
print(
"Docker CVE Counter - Fetching and analyzing Docker-related vulnerabilities..."
)
counter = DockerCVECounter(api_key=args.api_key)
all_cves = counter.get_all_docker_cves()
if not all_cves:
print("No CVEs found or error connecting to the NVD database.")
return
print(
f"Found {len(all_cves)} potential CVEs mentioning Docker. Filtering for relevance..."
)
docker_cves = counter.filter_relevant_cves(all_cves)
print(f"After filtering: {len(docker_cves)} Docker-related CVEs.")
yearly_counts, cve_details = counter.categorize_by_year(docker_cves)
counter.display_results(yearly_counts, cve_details)
if __name__ == "__main__":
main()
@quinn-dougherty
Copy link
Author

╰─>$ ./count_docker_cves.py
Docker CVE Counter - Fetching and analyzing Docker-related vulnerabilities...
Fetching CVEs starting from index 0...
Found 354 potential CVEs mentioning Docker. Filtering for relevance...
After filtering: 260 Docker-related CVEs.

===== Docker-related CVEs by Year =====
2025: 14 CVEs
2024: 58 CVEs
2023: 44 CVEs
2022: 24 CVEs
2021: 39 CVEs
2020: 26 CVEs
2019: 16 CVEs
2018: 14 CVEs
2017: 11 CVEs
2016: 4 CVEs
2015: 5 CVEs
2014: 5 CVEs

Total Docker-related CVEs found: 260
Average CVEs per year: 21.67

Show detailed breakdown of CVEs for a specific year? (Enter year or 'n' to skip): 2024

===== Detailed Docker CVEs for 2024 =====
CVE-2023-31001 - CVSS Score: 5.1 - Published: 2024-01-11T03:15:09.413
CVE-2023-31003 - CVSS Score: 8.4 - Published: 2024-01-11T03:15:09.617
CVE-2023-38267 - CVSS Score: 6.2 - Published: 2024-01-11T03:15:09.803
CVE-2024-23055 - CVSS Score: 6.1 - Published: 2024-01-25T22:15:08.623
CVE-2024-24557 - CVSS Score: 6.9 - Published: 2024-02-01T17:15:10.953
CVE-2024-24756 - CVSS Score: 7.5 - Published: 2024-02-01T23:15:11.210
CVE-2024-24760 - CVSS Score: 8.8 - Published: 2024-02-02T16:15:56.163
CVE-2023-30999 - CVSS Score: 7.5 - Published: 2024-02-03T01:15:07.850
CVE-2023-31004 - CVSS Score: 8.3 - Published: 2024-02-03T01:15:08.060
CVE-2023-31005 - CVSS Score: 6.2 - Published: 2024-02-03T01:15:08.283
CVE-2023-31006 - CVSS Score: 6.5 - Published: 2024-02-03T01:15:08.467
CVE-2023-32327 - CVSS Score: 7.1 - Published: 2024-02-03T01:15:08.653
CVE-2023-32329 - CVSS Score: 6.2 - Published: 2024-02-03T01:15:08.847
CVE-2023-43016 - CVSS Score: 7.3 - Published: 2024-02-03T01:15:09.030
CVE-2024-23054 - CVSS Score: 9.8 - Published: 2024-02-05T16:15:55.437
CVE-2024-23756 - CVSS Score: 7.5 - Published: 2024-02-08T21:15:08.380
CVE-2024-2215 - CVSS Score: 6.1 - Published: 2024-03-06T17:15:11.593
CVE-2024-2216 - CVSS Score: 8.8 - Published: 2024-03-06T17:15:11.640
CVE-2024-29018 - CVSS Score: 5.9 - Published: 2024-03-20T21:15:31.113
CVE-2024-30270 - CVSS Score: 6.2 - Published: 2024-04-04T21:15:16.577
CVE-2024-31204 - CVSS Score: 6.1 - Published: 2024-04-04T21:15:16.773
CVE-2024-1961 - CVSS Score: 8.8 - Published: 2024-04-16T00:15:10.867
CVE-2024-32473 - CVSS Score: 4.7 - Published: 2024-04-18T22:15:10.400
CVE-2024-29963 - CVSS Score: 1.9 - Published: 2024-04-19T04:15:10.793
CVE-2024-29964 - CVSS Score: 5.7 - Published: 2024-04-19T05:15:49.217
CVE-2024-29967 - CVSS Score: 4.4 - Published: 2024-04-19T05:15:49.737
CVE-2024-4159 - CVSS Score: 4.3 - Published: 2024-04-25T06:16:00.560
CVE-2024-35140 - CVSS Score: 7.7 - Published: 2024-05-31T17:15:08.837
CVE-2024-35142 - CVSS Score: 8.4 - Published: 2024-05-31T17:15:09.080
CVE-2024-4680 - CVSS Score: 8.8 - Published: 2024-06-08T20:15:52.347
CVE-2020-27352 - CVSS Score: 9.3 - Published: 2024-06-21T20:15:10.630
CVE-2023-38371 - CVSS Score: 5.9 - Published: 2024-06-27T18:15:12.880
CVE-2023-30997 - CVSS Score: 7.8 - Published: 2024-06-27T19:15:10.800
CVE-2023-30998 - CVSS Score: 7.8 - Published: 2024-06-27T19:15:11.187
CVE-2023-38368 - CVSS Score: 5.5 - Published: 2024-06-27T19:15:11.460
CVE-2023-38370 - CVSS Score: 7.5 - Published: 2024-06-27T19:15:11.720
CVE-2024-35137 - CVSS Score: 6.2 - Published: 2024-06-28T16:15:04.150
CVE-2024-35139 - CVSS Score: 6.2 - Published: 2024-06-28T16:15:04.380
CVE-2024-5652 - CVSS Score: 6.1 - Published: 2024-07-09T17:15:48.770
CVE-2024-6222 - CVSS Score: 7.0 - Published: 2024-07-09T18:15:12.510
CVE-2024-41110 - CVSS Score: 9.9 - Published: 2024-07-24T17:15:11.053
CVE-2024-41958 - CVSS Score: 6.6 - Published: 2024-08-05T20:15:36.063
CVE-2024-41959 - CVSS Score: 7.6 - Published: 2024-08-05T20:15:36.270
CVE-2024-41960 - CVSS Score: 3.8 - Published: 2024-08-05T20:15:36.477
CVE-2024-42364 - CVSS Score: 6.5 - Published: 2024-08-23T16:15:06.510
CVE-2024-45313 - CVSS Score: 5.4 - Published: 2024-09-02T18:15:37.850
CVE-2024-45310 - CVSS Score: 3.6 - Published: 2024-09-03T19:15:15.243
CVE-2024-20483 - CVSS Score: 7.2 - Published: 2024-09-11T17:15:13.213
CVE-2024-8695 - CVSS Score: 9.8 - Published: 2024-09-12T18:15:11.333
CVE-2024-8696 - CVSS Score: 9.8 - Published: 2024-09-12T18:15:11.490
CVE-2024-47180 - CVSS Score: 8.8 - Published: 2024-09-26T20:15:07.310
CVE-2024-47182 - CVSS Score: 4.8 - Published: 2024-09-27T14:15:04.620
CVE-2024-41997 - CVSS Score: 6.6 - Published: 2024-10-14T16:15:03.640
CVE-2024-9348 - CVSS Score: N/A - Published: 2024-10-16T15:15:18.100
CVE-2024-41968 - CVSS Score: 5.4 - Published: 2024-11-18T09:15:05.410
CVE-2024-35141 - CVSS Score: 7.8 - Published: 2024-12-19T02:15:22.810
CVE-2024-53182 - CVSS Score: 7.8 - Published: 2024-12-27T14:15:25.643
CVE-2024-45497 - CVSS Score: 7.6 - Published: 2024-12-31T03:15:05.543

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment