Initial Configuration¶
import os
import vulncheck_sdk
import matplotlib.pyplot as plt
import pandas as pd
import plotly.express as px
import calplot
from dotenv import load_dotenv
load_dotenv()
DEFAULT_HOST = "https://api.vulncheck.com"
DEFAULT_API = DEFAULT_HOST + "/v3"
TOKEN = os.environ["VULNCHECK_API_TOKEN"]
YEAR = 2025
# Configure the VulnCheck API client
configuration = vulncheck_sdk.Configuration(host=DEFAULT_API)
configuration.api_key["Bearer"] = TOKENPull Data¶
with vulncheck_sdk.ApiClient(configuration) as api_client:
indices_client = vulncheck_sdk.IndicesApi(api_client)
limit = 2000
# Initialize lists to store vendor and CVE data
cve = []
vendor = []
product = []
ransomware = []
date_added = []
cisa_date_added = []
canary = []
# Make the initial request to start pagination
api_response = indices_client.index_vulncheck_kev_get(start_cursor="true", limit=limit)
# Process the first page of results
for entry in api_response.data:
# Directly access the first element of the list
cve.append(entry.cve[0]) # Since there is always one CVE in the list
vendor.append(entry.vendor_project)
product.append(entry.product)
ransomware.append(entry.known_ransomware_campaign_use)
date_added.append(entry.date_added[:10])
canary.append(entry.reported_exploited_by_vulncheck_canaries)
# Handle cisa_date_added when it's None or has a date
if entry.cisa_date_added is None:
cisa_date_added.append("none")
else:
cisa_date_added.append(entry.cisa_date_added[:10])
# Continue fetching data while there's a next cursor
while api_response.meta.next_cursor is not None:
# Fetch the next page using the cursor
api_response = indices_client.index_vulncheck_kev_get(
cursor=api_response.meta.next_cursor, limit=limit
)
# Append the new data from the next page
for entry in api_response.data:
cve.append(entry.cve[0])
vendor.append(entry.vendor_project)
product.append(entry.product)
ransomware.append(entry.known_ransomware_campaign_use)
date_added.append(entry.date_added[:10])
canary.append(entry.reported_exploited_by_vulncheck_canaries)
# Handle cisa_date_added when it's None or has a date
if entry.cisa_date_added is None:
cisa_date_added.append("none")
else:
cisa_date_added.append(entry.cisa_date_added[:10])
# Create a DataFrame from the accumulated data
df_original = pd.DataFrame({
'CVE': cve,
'Vendor': vendor,
'Product': product,
'Ransomware': ransomware,
'Date Added': date_added,
'CISA Date Added': cisa_date_added,
'Canary': canary
})
VulnCheck Canary Exploitation Detection Metrics¶
import pandas as pd
df = df_original.copy()
# --- Clean up data types ---
df['Date Added'] = pd.to_datetime(df['Date Added'], errors='coerce')
df['CISA Date Added'] = df['CISA Date Added'].replace('none', pd.NaT)
df['CISA Date Added'] = pd.to_datetime(df['CISA Date Added'], format='%Y-%m-%d', errors='coerce')
# Ensure Canary is boolean
df['Canary'] = df['Canary'].astype(bool)
# --- Filter for Canary detections ---
df_canary = df[df['Canary'] == True]
# --- Metrics ---
total_cves_canary = df_canary['CVE'].nunique()
# Handle "Known"/"Unknown" ransomware values
ransomware_related = df_canary[df_canary['Ransomware'].str.lower() == 'known']['CVE'].nunique()
# CISA KEV (CISA Date Added present)
cisa_present = df_canary[df_canary['CISA Date Added'].notnull()]['CVE'].nunique()
# VulnCheck KEV (Date Added present)
vulncheck_present = df_canary[df_canary['Date Added'].notnull()]['CVE'].nunique()
# --- Assemble the metrics table ---
stats_rows = [
("Detected Exploitation", total_cves_canary),
("On VulnCheck KEV", vulncheck_present),
("On CISA KEV", cisa_present),
("Associated with Ransomware", ransomware_related),
]
stats_df = pd.DataFrame(stats_rows, columns=["VulnCheck Canary Metrics", "CVEs"])
stats_df["CVEs"] = stats_df["CVEs"].fillna(0).astype(int)
# --- Apply the same simple clean styling as your KEV summary ---
styled_stats_df = (
stats_df.style
.set_properties(**{'text-align': 'center'})
.set_table_styles([{'selector': 'th', 'props': [('text-align', 'center')]}])
.set_table_attributes('style="width:100%; border-collapse: collapse;"')
.hide(axis="index")
)
styled_stats_dfLoading...
Known Exploited Vulnerabilities Reported by VulnCheck Canaries (Source: VulnCheck KEV)¶
import plotly.express as px
import pandas as pd
df = df_original.copy()
# Ensure 'Canary' column is boolean
df['Canary'] = df['Canary'].astype(bool)
# Filter only entries where Canary == True
canary_df = df[df['Canary'] == True]
# Count occurrences by Vendor and Product for Canary = True
vendor_product_counts = canary_df.groupby(['Vendor', 'Product']).size().reset_index(name='Counts')
# Truncate vendor and product names for readability
vendor_product_counts['Vendor'] = vendor_product_counts['Vendor'].str.slice(0, 15)
vendor_product_counts['Product'] = vendor_product_counts['Product'].str.slice(0, 15)
# Create treemap
fig = px.treemap(
vendor_product_counts,
path=['Vendor', 'Product'],
values='Counts',
color='Counts',
color_continuous_scale='Viridis'
)
# Customize layout for dark mode
fig.update_layout(
title="Known Exploited Vulnerabilities Reported by VulnCheck Canaries (Source: VulnCheck KEV)",
title_font=dict(size=20, color='white'),
title_x=0.5,
paper_bgcolor='black',
plot_bgcolor='black',
margin=dict(t=50, l=25, r=25, b=25),
width=1800,
height=1000
)
# Remove color scale and style labels
fig.update_coloraxes(showscale=False)
fig.update_traces(
texttemplate='%{label}<br>%{value}',
textfont_size=16,
textfont_color='white'
)
fig.show()
Loading...