Files

113 lines
4.3 KiB
Python

"""Plain-text report renderer."""
from __future__ import annotations
from ..engine import AnalysisResult
from ..findings import Severity
from ._common import (
SEVERITY_LABEL, 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] = []
line = "=" * 70
out.append(line)
out.append("IoT PreTester 4.6 Demo - Network Capture Security Analysis")
out.append(line)
out.append(f"Capture : {m.get('capture_path', '-')}")
out.append(f"Packets : {m.get('packet_count', 0)} Duration: {m.get('duration_seconds', 0)}s")
out.append(f"Findings: {m.get('finding_count', 0)}")
if m.get("device_ip"):
out.append(f"Device : {m['device_ip']} (device-related findings: {s.get('device_related', 0)}, "
f"other hosts: {s.get('other_hosts', 0)})")
if m.get("truncated"):
out.append(f"WARNING : partial analysis - {m.get('truncation_reason')}")
out.append("")
bysev = s.get("by_severity", {})
out.append("SUMMARY")
out.append("-" * 70)
for sev in Severity:
out.append(f" {SEVERITY_LABEL[sev]:<8}: {bysev.get(sev.value, 0)}")
out.append("")
highlights = s.get("highlights", [])
if highlights:
out.append("PRIORITIZED HIGHLIGHTS")
out.append("-" * 70)
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("-" * 70)
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(f" Severity : {SEVERITY_LABEL[f.severity]} / Confidence: {f.confidence.value}")
out.append(f" Category : {f.category.value} (Detector: {f.detector})")
art = artifact_line(f)
if art and art != "—":
out.append(f" 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)}")
out.append(f" 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=False)
if inv_lines:
out.append("-" * 70)
out.extend(inv_lines)
out.append("")
if extra:
hashes = extra.get("hashes") or []
urls = extra.get("urls") or []
if hashes:
out.append(f"HASHES ({len(hashes)})")
out.append("-" * 70)
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' [{htype}]' if htype else ''}")
out.append(f" {state}")
out.append("")
if urls:
out.append(f"CLOUD URLS & ENDPOINTS ({len(urls)})")
out.append("-" * 70)
for u in urls:
out.append(f" {u.get('url')}")
if u.get("analyzed"):
ports = ", ".join(map(str, u.get("ports") or [])) or "-"
out.append(f" Ports: {ports}")
if u.get("tls_versions"):
out.append(f" TLS: {', '.join(u['tls_versions'])}")
if u.get("cipher_suites"):
out.append(f" Cipher: {', '.join(u['cipher_suites'])}")
else:
out.append(" analysis pending")
out.append("")
return "\n".join(out)