""" 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)