Idea
`/engagements/[id]/report` exists and prints reasonably (the print stylesheet was tuned for it), but it's not deliverable-grade. Three concrete gaps:
- PDF generation requires the operator to "Print → Save as PDF" themselves, which works but is uneven across browsers.
- The same finding across 10 hosts renders as 10 separate sections — clients want one finding-with-affected-hosts.
- There's no executive summary — a report opens straight into the technical detail.
Proposal
PDF export button
Render server-side via a `/api/engagements/[id]/report/pdf` route that hits the same RSC and pipes through Puppeteer / Playwright headless. Heavy dep, but it's the only way to give operators a one-click PDF that matches the on-screen render exactly. Alternative: stay browser-print-only and just polish the print stylesheet harder. Choose during planning.
Finding deduplication
Group findings by `(title, severity, cve)` — same logical issue across multiple hosts collapses into one section with an "Affected hosts" subsection listing every host:port instance. A separate per-instance evidence accordion preserves screenshots / proofs.
Already a structured affordance in the data model — findings carry `port_id` + free-text `title`; the report just needs to group them.
Executive summary auto-draft
Top-of-report block, opt-in toggle (`engagement_settings.exec_summary_enabled`):
- Total finding count by severity (`3 critical, 7 high, ...`)
- Highest-severity finding titles (top 5)
- Engagement scope summary: hosts + open ports
- Key dates: scan started, last rescan, report generated
Auto-drafted from the data; operator edits the prose freely (already has `engagement.writeup` markdown column from #14, just expand its placement).
Branding
Optional logo + report-title override via `/settings`. Operators showing the report to a client want their own letterhead — currently it just says "recon-deck". Tiny UX win.
Conditional check provenance in the report
P5 (#30) added `+conditional-id` pills in the engagement page. The report should mirror them — "this check fired because we detected PHP" is exactly the kind of context a deliverable benefits from.
Out of scope
- Multi-engagement aggregate reports ("all engagements this quarter") — separate feature
- DOCX / RTF export — PDF + Markdown is enough for v1
- Custom report templates — operators with strong opinions can fork the print stylesheet
Acceptance
- Report page renders a "Findings" section grouped by `(title, severity)` with an "Affected hosts" subsection
- Optional executive summary block at top, toggleable per engagement
- Either: a "Download PDF" button OR a polished print-stylesheet that produces a clean PDF on Cmd+P → Save as PDF
- Logo / title override surfaces from `/settings`
- Conditional-fired checks render the `+id` provenance pill in the print stream
Notes
Pairs with #40 (Burp/nuclei importer) — auto-imported findings become the bulk of the deliverable narrative; without grouping, large Burp imports turn the report into a wall.
Idea
`/engagements/[id]/report` exists and prints reasonably (the print stylesheet was tuned for it), but it's not deliverable-grade. Three concrete gaps:
Proposal
PDF export button
Render server-side via a `/api/engagements/[id]/report/pdf` route that hits the same RSC and pipes through Puppeteer / Playwright headless. Heavy dep, but it's the only way to give operators a one-click PDF that matches the on-screen render exactly. Alternative: stay browser-print-only and just polish the print stylesheet harder. Choose during planning.
Finding deduplication
Group findings by `(title, severity, cve)` — same logical issue across multiple hosts collapses into one section with an "Affected hosts" subsection listing every host:port instance. A separate per-instance evidence accordion preserves screenshots / proofs.
Already a structured affordance in the data model — findings carry `port_id` + free-text `title`; the report just needs to group them.
Executive summary auto-draft
Top-of-report block, opt-in toggle (`engagement_settings.exec_summary_enabled`):
Auto-drafted from the data; operator edits the prose freely (already has `engagement.writeup` markdown column from #14, just expand its placement).
Branding
Optional logo + report-title override via `/settings`. Operators showing the report to a client want their own letterhead — currently it just says "recon-deck". Tiny UX win.
Conditional check provenance in the report
P5 (#30) added `+conditional-id` pills in the engagement page. The report should mirror them — "this check fired because we detected PHP" is exactly the kind of context a deliverable benefits from.
Out of scope
Acceptance
Notes
Pairs with #40 (Burp/nuclei importer) — auto-imported findings become the bulk of the deliverable narrative; without grouping, large Burp imports turn the report into a wall.