"""Markdown report renderer.""" from __future__ import annotations from ..engine import AnalysisResult from ..findings import Severity from ._common import ( SEVERITY_LABEL, fmt_ts, affected_line, evidence_line, artifact_line, sources_list, inventory_lines, ) def render(result: AnalysisResult, extra: dict | None = None) -> str: m = result.meta s = result.summary out: list[str] = [] out.append("# IoT PreTester 4.6 Demo — Network Capture Security Analysis") out.append("") out.append(f"- **Capture:** `{m.get('capture_path', '—')}`") out.append(f"- **Packets:** {m.get('packet_count', 0)} · " f"**Duration:** {m.get('duration_seconds', 0)} s · " f"**Findings:** {m.get('finding_count', 0)}") if m.get("device_ip"): out.append(f"- **Device under test:** `{m['device_ip']}`" + (f" (MAC {', '.join(m.get('device_macs') or [])})" if m.get("device_macs") else "") + f" · device-related findings: {s.get('device_related', 0)}, other hosts: {s.get('other_hosts', 0)}") if m.get("truncated"): out.append(f"- **⚠️ Partial analysis:** {m.get('truncation_reason')}") out.append("") bysev = s.get("by_severity", {}) out.append("## Summary") out.append("") out.append("| Severity | Count |") out.append("| ----------- | ------ |") for sev in Severity: out.append(f"| {SEVERITY_LABEL[sev]} | {bysev.get(sev.value, 0)} |") out.append("") highlights = s.get("highlights", []) if highlights: out.append("## ⚠️ Prioritized Highlights") out.append("") out.append("Highest-impact credential, token, C2, and exfiltration findings:") out.append("") for f in highlights: out.append(f"- **[{SEVERITY_LABEL[f.severity]}/{f.confidence.value}]** " f"{f.title} — {affected_line(f)}") out.append("") out.append("## Findings") out.append("") if not result.findings: out.append("_No security-relevant findings detected._") for i, f in enumerate(result.findings, 1): out.append(f"### {i}. {f.title}") out.append("") out.append(f"- **Severity:** {SEVERITY_LABEL[f.severity]} " f"· **Confidence:** {f.confidence.value} " f"· **Category:** {f.category.value} " f"· **Detector:** {f.detector}") art = artifact_line(f) if art and art != "—": out.append(f"- **🔎 Observed artifact:** `{art}`") srcs = sources_list(f) if srcs: out.append("- **📍 Sources:**") for source in srcs: out.append(f" - {source}") out.append(f"- **Risk:** {f.risk_description}") out.append(f"- **Affected:** {affected_line(f)}" + (" · **involves device under test**" if f.evidence.get("device_related") else "")) out.append(f"- **Additional evidence:** {evidence_line(f)}") out.append(f"- **Recommendation:** {f.recommendation}") out.append(f"- **Finding ID:** `{f.finding_id}`") out.append("") inv_lines = inventory_lines(result.inventory, md=True) if inv_lines: out.append("") out.extend(inv_lines) # Optional URL and hash enrichment results. if extra: hashes = extra.get("hashes") or [] urls = extra.get("urls") or [] if hashes: out.append("") out.append(f"## Hashes ({len(hashes)})") out.append("") for h in hashes: if h.get("cracked"): state = f"**CRACKED** → `{h.get('cracked_value')}`" if h.get("crack_time"): state += f" ({h['crack_time']})" elif h.get("analyzed"): state = "not cracked" else: state = "analysis pending" htype = ", ".join(h.get("hash_type") or []) out.append(f"- `{h.get('hash_value')}`" f"{f' [{htype}]' if htype else ''} — {state}") if urls: out.append("") out.append(f"## Cloud URLs and Endpoints ({len(urls)})") out.append("") for u in urls: if u.get("analyzed"): ports = ", ".join(map(str, u.get("ports") or [])) or "—" details = f"Ports: {ports}" if u.get("tls_versions"): details += f"; TLS: {', '.join(u['tls_versions'])}" if u.get("cipher_suites"): details += f"; Cipher: {', '.join(u['cipher_suites'])}" else: details = "analysis pending" out.append(f"- `{u.get('url')}` — {details}") out.append("") out.append("---") out.append(f"_Generated by IoT PreTester 4.6 Demo · Detectors: " f"{', '.join(m.get('detectors_run', []))}_") return "\n".join(out)