VulnCheck Target Intelligence tracks internet-facing hosts and the software, vulnerabilities, and threat classifications observed on them.
import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
import vulncheck_sdk
import pandas as pd
import plotly.express as px
from dotenv import load_dotenv
load_dotenv()
DEFAULT_HOST = "https://api.vulncheck.com"
DEFAULT_API = DEFAULT_HOST + "/v3"
TOKEN = os.environ["VULNCHECK_API_TOKEN"]
# Configure the VulnCheck API client
configuration = vulncheck_sdk.Configuration(host=DEFAULT_API)
configuration.api_key["Bearer"] = TOKENimport sys
sys.path.insert(0, "..")
from utils import load_df
raw = load_df("vulncheck-kev")
df_original = pd.DataFrame({
"CVE": raw["cve"].apply(lambda x: x[0] if x else None),
"Vendor": raw["vendor_project"],
"Product": raw["product"],
"VulnCheck KEV Added Date": raw["date_added"].apply(lambda x: x[:10] if isinstance(x, str) and x else None),
"CISA Date Added": raw["cisa_date_added"].apply(lambda x: x[:10] if isinstance(x, str) and x else None),
})
df_original = df_original.dropna(subset=["CVE"]).drop_duplicates(subset=["CVE"], ignore_index=True)
print(f"VulnCheck KEV CVEs: {len(df_original):,}")VulnCheck KEV CVEs: 5,060
from utils import target_intel_cve_count
# For each KEV CVE, ask target-intel how many host records reference it.
# total_documents > 0 means the CVE is present in Target Intelligence.
cves = df_original["CVE"].tolist()
counts = {}
start = time.time()
with vulncheck_sdk.ApiClient(configuration) as api_client:
indices_client = vulncheck_sdk.IndicesApi(api_client)
with ThreadPoolExecutor(max_workers=10) as ex:
futures = {ex.submit(target_intel_cve_count, indices_client, c): c for c in cves}
for i, fut in enumerate(as_completed(futures), 1):
counts[futures[fut]] = fut.result()
if i % 500 == 0:
print(f" {i}/{len(cves)} checked ({time.time()-start:.0f}s)")
df_original["Host Records"] = df_original["CVE"].map(counts)
df_original["In Target Intel"] = df_original["Host Records"] > 0
present = int(df_original["In Target Intel"].sum())
print(f"\nKEV CVEs present in Target Intelligence: {present:,} of {len(df_original):,}") 500/5060 checked (8s)
1000/5060 checked (15s)
1500/5060 checked (22s)
2000/5060 checked (29s)
2500/5060 checked (35s)
3000/5060 checked (42s)
3500/5060 checked (48s)
4000/5060 checked (55s)
4500/5060 checked (62s)
5000/5060 checked (70s)
KEV CVEs present in Target Intelligence: 928 of 5,060
Unique KEVs Seen in Target Intelligence¶
# Restrict to KEVs observed in the Target Intelligence dataset
in_target_intel = df_original[df_original["In Target Intel"]]
# VulnCheck KEV CVEs present in Target Intel
unique_vulncheck_kevs = in_target_intel["CVE"].nunique()
# CISA KEV CVEs (those with a CISA date added) present in Target Intel
unique_cisa_kevs = in_target_intel.loc[in_target_intel["CISA Date Added"].notna(), "CVE"].nunique()
stats_rows = [
("Total Unique VulnCheck KEVs", unique_vulncheck_kevs),
("Total Unique CISA KEVs", unique_cisa_kevs),
]
stats_df = pd.DataFrame(stats_rows, columns=["Metric", "Count"])
stats_df["Count"] = stats_df["Count"].astype(int)
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...
KEV Observed in VulnCheck Target Intelligence¶
The treemap below shows which VulnCheck KEV vendors and products have known-exploited vulnerabilities actually observed across those hosts.
df = df_original[df_original["In Target Intel"]].copy()
# Count unique KEV CVEs present in Target Intel by Vendor and Product
vendor_product_counts = df.groupby(["Vendor", "Product"]).size().reset_index(name="Counts")
# Truncate vendor and product names to 15 characters 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 a treemap with both Vendor and Product levels
fig = px.treemap(vendor_product_counts, path=["Vendor", "Product"], values="Counts",
color="Counts", color_continuous_scale="Viridis")
# Customize the title and layout for dark mode with specific width and height
fig.update_layout(
title="VulnCheck KEV by Vendor and Product Present in Target Intelligence (Source: VulnCheck KEV + Target Intel)",
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 bar by setting coloraxis_showscale to False
fig.update_coloraxes(showscale=False)
# Use default font for labels inside the treemap (Arial) by removing custom font settings
fig.update_traces(
texttemplate='%{label}<br> %{value}',
textfont_size=16,
textfont_color='white'
)
# Display the figure
fig.show()Loading...
Last 25 CVEs Added to VulnCheck KEV Present in Target Intelligence¶
from IPython.display import display, HTML
# --- Last 25 CVEs added to VulnCheck KEV that are present in Target Intel ---
recent_kev = (
df_original[df_original["In Target Intel"]]
.assign(_added=lambda d: pd.to_datetime(d["VulnCheck KEV Added Date"], errors="coerce"))
.sort_values("_added", ascending=False)
.head(25)
.copy()
)
recent_kev["Target Intel Records"] = recent_kev["Host Records"].map(lambda x: f"{int(x):,}")
display_df = recent_kev[[
"CVE",
"Vendor",
"Product",
"VulnCheck KEV Added Date",
"CISA Date Added",
"Target Intel Records",
]].rename(columns={"CISA Date Added": "CISA KEV Added Date"}).fillna("-")
table_html = display_df.to_html(index=False)
scrollable_html = f"""
<div style="max-height: 500px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px">
{table_html}
</div>
"""
display(HTML(scrollable_html))Loading...
KEVs Added in the Past 6 Months Observed in Target Intelligence¶
VulnCheck KEV vendors and products for CVEs added to the KEV catalog within the last six months that are present in Target Intelligence. Each tile is sized and colored by the number of unique KEV CVEs.
# --- KEVs added in the past 6 months that are present in Target Intel ---
df_recent = df_original[df_original["In Target Intel"]].copy()
df_recent["_added"] = pd.to_datetime(df_recent["VulnCheck KEV Added Date"], errors="coerce")
cutoff = pd.Timestamp.now().normalize() - pd.DateOffset(months=6)
df_recent = df_recent[df_recent["_added"] >= cutoff]
# Count unique KEV CVEs present in Target Intel by Vendor and Product
vendor_product_counts = df_recent.groupby(["Vendor", "Product"]).size().reset_index(name="Counts")
# Truncate vendor and product names to 15 characters 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 a treemap with both Vendor and Product levels
fig = px.treemap(vendor_product_counts, path=["Vendor", "Product"], values="Counts",
color="Counts", color_continuous_scale="Viridis")
# Customize the title and layout for dark mode with specific width and height
fig.update_layout(
title=f"VulnCheck KEV by Vendor and Product Added Since {cutoff.date()} Present in Target Intelligence (Source: VulnCheck KEV + Target Intel)",
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 bar by setting coloraxis_showscale to False
fig.update_coloraxes(showscale=False)
# Use default font for labels inside the treemap (Arial) by removing custom font settings
fig.update_traces(
texttemplate='%{label}<br> %{value}',
textfont_size=16,
textfont_color='white'
)
# Display the figure
fig.show()Loading...