Files
2026-06-27 21:24:57 -04:00

293 lines
12 KiB
Python
Executable File

"""
utils/embeds.py — cyberpunk / hacker-aesthetic embed builders.
Every embed in VulnForge is built here so styling stays consistent:
dark backdrops, neon severity colours, monospace code blocks, and tidy
mobile + desktop formatting. Functions accept normalised dataclasses from
utils.api and return ready-to-send nextcord.Embed objects.
"""
from __future__ import annotations
from datetime import datetime, timezone
import nextcord
from config import settings
from utils.api import CVERecord, KEVEntry
from utils.enricher import cve_links
from utils.helpers import (
discord_timestamp,
severity_color,
severity_emoji,
truncate,
)
# Visual furniture reused across embeds.
DIVIDER = "▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰"
BRAND = "VulnForge // Security Intelligence"
BRAND_ICON = "https://www.cisa.gov/profiles/cisad8_gov/themes/custom/gesso/logo.svg"
def cve_pivots(cve_id: str, *, compact: bool = False) -> str:
"""One-line markdown pivot links for a CVE — PoC search / exploit-db / etc.
Used to enrich every CVE-bearing embed (cve, weekly, kev) with quick links
to GitHub PoCs, exploit-db, Nuclei templates, trickest/cve and Vulncheck.
Pass `compact=True` for tighter rendering in list-style embeds.
"""
links = {name: url for name, url in cve_links(cve_id)}
if compact:
order = ("GitHub PoC search", "exploit-db", "Nuclei templates")
labels = {"GitHub PoC search": "PoC", "exploit-db": "ExDB", "Nuclei templates": "Nuclei"}
else:
order = ("GitHub PoC search", "exploit-db", "Nuclei templates",
"trickest/cve", "Vulncheck", "GitHub Advisory")
labels = {
"GitHub PoC search": "GH PoC",
"exploit-db": "Exploit-DB",
"Nuclei templates": "Nuclei",
"trickest/cve": "trickest/cve",
"Vulncheck": "Vulncheck",
"GitHub Advisory": "GH Advisory",
}
return " · ".join(f"[{labels[k]}]({links[k]})" for k in order if k in links)
def _footer(embed: nextcord.Embed) -> nextcord.Embed:
embed.set_footer(text=f"⛓ {BRAND}")
embed.timestamp = datetime.now(timezone.utc)
return embed
# ──────────────────────────────────────────────────────────────
# CVE detail embed
# ──────────────────────────────────────────────────────────────
def build_cve_embed(
cve: CVERecord,
kev: KEVEntry | None,
*,
show_details: bool = True,
show_kev: bool = True,
show_references: bool = True,
show_cwe: bool = True,
notes: list[dict] | None = None,
) -> nextcord.Embed:
"""Build the rich, option-driven CVE embed returned by /cve."""
sev = cve.cvss_severity
color = settings.color_kev if kev else severity_color(sev)
exploited = " ☣ ACTIVELY EXPLOITED" if kev else ""
embed = nextcord.Embed(
title=f"{severity_emoji(sev)} {cve.cve_id}{exploited}",
url=cve.nvd_url,
description=truncate(cve.description, 1024) if show_details else None,
color=color,
)
# Score line — always shown; it's the headline metric.
score_str = f"`{cve.cvss_score}`" if cve.cvss_score is not None else "`N/A`"
ver = f" (CVSS v{cve.cvss_version})" if cve.cvss_version else ""
embed.add_field(
name="◈ Severity",
value=f"{severity_emoji(sev)} **{sev}**{ver}",
inline=True,
)
embed.add_field(name="◈ Base Score", value=score_str, inline=True)
embed.add_field(name="◈ Status", value=f"`{cve.status}`", inline=True)
if show_details:
embed.add_field(
name="◈ Published",
value=discord_timestamp(cve.published, "D"),
inline=True,
)
embed.add_field(
name="◈ Modified",
value=discord_timestamp(cve.last_modified, "R"),
inline=True,
)
if cve.cvss_vector:
embed.add_field(
name="◈ Vector",
value=f"```{truncate(cve.cvss_vector, 240)}```",
inline=False,
)
if show_cwe and cve.cwes:
embed.add_field(
name="◈ Weaknesses (CWE)",
value=" ".join(f"`{c}`" for c in cve.cwes[:8]),
inline=False,
)
# KEV block — the most security-critical signal.
if show_kev:
if kev:
ransom = kev.ransomware if kev.ransomware else "Unknown"
embed.add_field(name=DIVIDER, value="**☣ CISA KEV — ACTIVE EXPLOITATION**", inline=False)
embed.add_field(name="▸ Vendor / Product", value=f"`{kev.vendor}` / `{kev.product}`", inline=True)
embed.add_field(name="▸ Added to KEV", value=discord_timestamp(kev.date_added_dt, "D"), inline=True)
embed.add_field(name="▸ Remediation Due", value=f"`{kev.due_date or 'N/A'}`", inline=True)
embed.add_field(name="▸ Ransomware Use", value=f"`{ransom}`", inline=True)
if kev.required_action:
embed.add_field(
name="▸ Required Action",
value=truncate(kev.required_action, 1024),
inline=False,
)
else:
embed.add_field(
name="◈ CISA KEV Status",
value="✅ Not currently listed in the Known Exploited Vulnerabilities catalog.",
inline=False,
)
if show_references and cve.references:
links = "\n".join(f"• [{truncate(u, 70)}]({u})" for u in cve.references[:6])
embed.add_field(name="◈ References", value=truncate(links, 1024), inline=False)
if notes:
rendered = "\n".join(
f"• <@{n['user_id']}>: {truncate(n['note'], 150)}" for n in notes[:5]
)
embed.add_field(name="◈ Researcher Notes", value=truncate(rendered, 1024), inline=False)
pivots = cve_pivots(cve.cve_id)
if pivots:
embed.add_field(name="◈ Recon Pivots", value=pivots, inline=False)
embed.add_field(
name="◈ Links",
value=f"[🛰 NVD]({cve.nvd_url}) • [🛡 CISA KEV]({settings.cisa_kev_web})",
inline=False,
)
return _footer(embed)
# ──────────────────────────────────────────────────────────────
# Weekly digest embed
# ──────────────────────────────────────────────────────────────
def build_weekly_embed(
*,
total_new: int,
in_kev_count: int,
newly_added_kev: list[KEVEntry],
top_cves: list[CVERecord],
days: int = 7,
) -> nextcord.Embed:
"""Build the automated weekly threat-intelligence digest embed."""
embed = nextcord.Embed(
title="☣ VULNFORGE WEEKLY THREAT DIGEST",
description=(
f"```ansi\n\u001b[1;31m> Intelligence sweep — last {days} days\u001b[0m\n```\n"
f"Aggregated from **NVD** and the **CISA KEV** catalog."
),
color=settings.color_critical if newly_added_kev else settings.color_info,
url=settings.cisa_kev_web,
)
# Headline metrics block.
embed.add_field(name="🆕 New CVEs", value=f"**{total_new:,}**", inline=True)
embed.add_field(name="☣ Already in KEV", value=f"**{in_kev_count:,}**", inline=True)
embed.add_field(name="⚠ Newly Added to KEV", value=f"**{len(newly_added_kev):,}**", inline=True)
embed.add_field(name=DIVIDER, value="**▶ TOP CRITICAL VULNERABILITIES**", inline=False)
if top_cves:
for cve in top_cves:
sev = cve.cvss_severity
score = cve.cvss_score if cve.cvss_score is not None else "N/A"
pivots = cve_pivots(cve.cve_id, compact=True)
link_line = f"[NVD]({cve.nvd_url})"
if pivots:
link_line += f" · {pivots}"
embed.add_field(
name=f"{severity_emoji(sev)} {cve.cve_id}{score}",
value=f"{truncate(cve.description, 140)}\n{link_line}",
inline=False,
)
else:
embed.add_field(
name="—",
value="No scored CVEs found in the window.",
inline=False,
)
if newly_added_kev:
kev_lines = "\n".join(
f"• `{e.cve_id}` — {truncate(e.product, 38)} ({e.vendor}) — {cve_pivots(e.cve_id, compact=True)}"
for e in newly_added_kev[:8]
)
embed.add_field(
name="☣ Newly Weaponized (Added to KEV)",
value=truncate(kev_lines, 1024),
inline=False,
)
embed.add_field(
name="◈ Sources",
value=(
"[🛰 NVD Feed](https://nvd.nist.gov/vuln) • "
f"[🛡 CISA KEV]({settings.cisa_kev_web})"
),
inline=False,
)
return _footer(embed)
# ──────────────────────────────────────────────────────────────
# KEV list embed
# ──────────────────────────────────────────────────────────────
def build_kev_list_embed(entries: list[KEVEntry], limit: int) -> nextcord.Embed:
"""Build the embed for the /kev command (latest KEV additions)."""
embed = nextcord.Embed(
title="☣ CISA KEV — LATEST KNOWN EXPLOITED VULNERABILITIES",
description=(
"```ansi\n\u001b[1;31m> Actively exploited in the wild\u001b[0m\n```"
f"Showing the **{min(limit, len(entries))}** most recent additions."
),
color=settings.color_kev,
url=settings.cisa_kev_web,
)
for e in entries[:limit]:
pivots = cve_pivots(e.cve_id, compact=True)
link_line = f"[NVD]({settings.nvd_cve_web}/{e.cve_id})"
if pivots:
link_line += f" · {pivots}"
embed.add_field(
name=f"🩸 {e.cve_id}{truncate(e.product, 50)}",
value=truncate(
f"**Vendor:** {e.vendor}\n"
f"**Added:** {discord_timestamp(e.date_added_dt, 'D')} • "
f"**Ransomware:** `{e.ransomware}`\n"
f"{truncate(e.short_description, 140)}\n"
f"{link_line}",
1024,
),
inline=False,
)
if not entries:
embed.description = "No KEV entries could be retrieved right now."
return _footer(embed)
# ──────────────────────────────────────────────────────────────
# Error / utility embeds
# ──────────────────────────────────────────────────────────────
def build_error_embed(title: str, message: str) -> nextcord.Embed:
embed = nextcord.Embed(
title=f"⛔ {title}",
description=message,
color=settings.color_critical,
)
return _footer(embed)
def build_info_embed(title: str, message: str) -> nextcord.Embed:
embed = nextcord.Embed(
title=f"✅ {title}",
description=message,
color=settings.color_success,
)
return _footer(embed)