"""
Report generation for NeuroSploit v3.3.0.
Produces a polished **HTML** report from a run's validated findings, plus a
**PDF** via the Typst engine when the `typst` binary is available (it is the
intended report engine; HTML is always emitted as a fallback/companion).
from neurosploit_agent.report import generate
paths = generate(target, findings, out_dir) # -> {"html":..., "pdf":..., "typ":...}
"""
import datetime
import html
import os
import shutil
import subprocess
from typing import Dict, List, Optional
SEV_ORDER = {"Critical": 0, "High": 1, "Medium": 2, "Low": 3, "Info": 4}
SEV_COLOR = {"Critical": "#c0392b", "High": "#e67e22", "Medium": "#f1c40f",
"Low": "#3498db", "Info": "#7f8c8d"}
def _sorted(findings: List[Dict]) -> List[Dict]:
return sorted(findings, key=lambda f: SEV_ORDER.get(f.get("severity", "Info"), 9))
def _counts(findings: List[Dict]) -> Dict[str, int]:
c = {}
for f in findings:
c[f.get("severity", "Info")] = c.get(f.get("severity", "Info"), 0) + 1
return c
def typst_available() -> bool:
return shutil.which("typst") is not None
# --------------------------------------------------------------------------- HTML
def render_html(target: str, findings: List[Dict], when: str) -> str:
counts = _counts(findings)
chips = "".join(
f'{s}: {n}'
for s, n in sorted(counts.items(), key=lambda kv: SEV_ORDER.get(kv[0], 9))
) or 'No validated findings'
rows = []
for i, f in enumerate(_sorted(findings), 1):
sev = f.get("severity", "Info")
rows.append(f"""