FastAPI + Kali tools UI for authorized scans of levkin.ca / LAN targets. Gitignore local data/.env; add pytest + gitleaks CI.
149 lines
5.2 KiB
Python
149 lines
5.2 KiB
Python
"""Bulk scan report rendering for Cursor and other consumers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
_SEVERITY_ORDER = ("critical", "high", "medium", "low", "info")
|
|
_PHASE_OUTPUT_MAX = 4000
|
|
|
|
|
|
def _sort_findings(findings: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
order = {s: i for i, s in enumerate(_SEVERITY_ORDER)}
|
|
return sorted(findings, key=lambda f: order.get(str(f.get("severity", "")).lower(), 99))
|
|
|
|
|
|
def _truncate(text: str, limit: int = _PHASE_OUTPUT_MAX) -> str:
|
|
text = (text or "").strip()
|
|
if len(text) <= limit:
|
|
return text
|
|
return text[: limit - 40] + "\n\n… (truncated)"
|
|
|
|
|
|
def render_bulk_markdown(payload: dict[str, Any], *, for_cursor: bool = False) -> str:
|
|
bulk = payload["bulk"]
|
|
scans = payload["scans"]
|
|
lines: list[str] = []
|
|
|
|
if for_cursor:
|
|
lines.extend(
|
|
[
|
|
"<!-- Talos bulk report — paste into Cursor for triage / remediation -->",
|
|
"",
|
|
"You are reviewing authorized security scan results from Talos.",
|
|
"Prioritize critical/high findings, then failed phases that may hide issues.",
|
|
"",
|
|
]
|
|
)
|
|
|
|
lines.extend(
|
|
[
|
|
f"# Talos bulk report",
|
|
"",
|
|
f"- **Bulk ID:** `{bulk['id']}`",
|
|
f"- **Profile:** {bulk['profile']}",
|
|
f"- **Status:** {bulk['status']}",
|
|
f"- **Progress:** {bulk['complete']}/{bulk['total']} complete"
|
|
+ (f", {bulk['failed']} failed" if bulk.get("failed") else "")
|
|
+ (f", {bulk['running']} running" if bulk.get("running") else "")
|
|
+ (f", {bulk['queued']} queued" if bulk.get("queued") else ""),
|
|
"",
|
|
]
|
|
)
|
|
|
|
# Cross-target finding rollup (severity >= low with issues)
|
|
all_findings: list[tuple[str, dict[str, Any]]] = []
|
|
for entry in scans:
|
|
target = entry["scan"]["target"]
|
|
for f in entry.get("findings") or []:
|
|
if f.get("severity") in ("critical", "high", "medium", "low"):
|
|
all_findings.append((target, f))
|
|
|
|
if all_findings:
|
|
lines.append("## Findings rollup (all targets)")
|
|
lines.append("")
|
|
for target, f in sorted(
|
|
all_findings,
|
|
key=lambda x: _SEVERITY_ORDER.index(x[1]["severity"])
|
|
if x[1]["severity"] in _SEVERITY_ORDER
|
|
else 99,
|
|
):
|
|
sev = f["severity"].upper()
|
|
lines.append(f"- **[{sev}]** `{target}` — {f['title']}")
|
|
lines.append("")
|
|
|
|
for entry in scans:
|
|
scan = entry["scan"]
|
|
phases = entry.get("phases") or []
|
|
findings = _sort_findings(entry.get("findings") or [])
|
|
lines.append(f"## {scan['target']}")
|
|
lines.append("")
|
|
lines.append(f"- Scan ID: `{scan['id']}`")
|
|
lines.append(f"- Status: **{scan['status']}**")
|
|
if scan.get("llm_unavailable"):
|
|
lines.append("- LLM analysis was unavailable for some phases")
|
|
lines.append("")
|
|
|
|
lines.append("### Phases")
|
|
lines.append("")
|
|
lines.append("| Phase | Status | Tool |")
|
|
lines.append("|-------|--------|------|")
|
|
for p in phases:
|
|
lines.append(f"| {p['name']} | {p['status']} | {p.get('tool', '')} |")
|
|
lines.append("")
|
|
|
|
failed_phases = [p for p in phases if p["status"] in ("failed", "blocked", "inconclusive")]
|
|
if failed_phases:
|
|
lines.append("### Phase notes (non-complete)")
|
|
lines.append("")
|
|
for p in failed_phases:
|
|
snippet = _truncate(p.get("raw_output") or "", 800)
|
|
lines.append(f"**{p['name']}** ({p['status']})")
|
|
if snippet:
|
|
lines.append("")
|
|
lines.append("```")
|
|
lines.append(snippet)
|
|
lines.append("```")
|
|
lines.append("")
|
|
|
|
if findings:
|
|
lines.append("### Findings")
|
|
lines.append("")
|
|
for f in findings:
|
|
sev = str(f["severity"]).upper()
|
|
lines.append(f"#### [{sev}] {f['title']}")
|
|
lines.append("")
|
|
lines.append(f["description"])
|
|
lines.append("")
|
|
if f.get("evidence"):
|
|
lines.append(f"**Evidence:** `{_truncate(str(f['evidence']), 500)}`")
|
|
lines.append("")
|
|
lines.append(f"**Recommendation:** {f['recommendation']}")
|
|
lines.append("")
|
|
else:
|
|
lines.append("_No LLM findings recorded._")
|
|
lines.append("")
|
|
|
|
lines.append("---")
|
|
lines.append("")
|
|
|
|
return "\n".join(lines).rstrip() + "\n"
|
|
|
|
|
|
def render_bulk_json(payload: dict[str, Any]) -> dict[str, Any]:
|
|
"""JSON export with truncated phase output to keep size reasonable."""
|
|
out = dict(payload)
|
|
scans_out: list[dict[str, Any]] = []
|
|
for entry in payload.get("scans") or []:
|
|
entry = dict(entry)
|
|
phases = []
|
|
for p in entry.get("phases") or []:
|
|
p = dict(p)
|
|
if p.get("raw_output"):
|
|
p["raw_output"] = _truncate(str(p["raw_output"]))
|
|
phases.append(p)
|
|
entry["phases"] = phases
|
|
scans_out.append(entry)
|
|
out["scans"] = scans_out
|
|
return out
|