| """Bugcrowd report builder β auto-draft submissions from findings. |
| |
| Generates structured reports in Bugcrowd format with evidence links, |
| severity justification, and remediation suggestions. |
| """ |
|
|
| from datetime import datetime |
| from typing import Any, Dict, List |
|
|
| from infj_bot.core.plugins.findings_db import Finding |
|
|
|
|
| SEVERITY_MAP = { |
| "P1": "critical", |
| "P2": "high", |
| "P3": "medium", |
| "P4": "low", |
| "P5": "informational", |
| } |
|
|
|
|
| class ReportBuilder: |
| """Build Bugcrowd-ready reports from findings.""" |
|
|
| def build(self, finding: Finding, evidence_list: List[Dict[str, Any]]) -> str: |
| """Return a full Bugcrowd markdown report.""" |
| lines = [ |
| f"# {finding.title}", |
| "", |
| f"**Severity:** {finding.severity} ({SEVERITY_MAP.get(finding.severity, 'unknown')})", |
| f"**Asset:** {finding.asset or 'N/A'}", |
| f"**Confidence:** {finding.confidence}", |
| f"**Vulnerability Type:** {finding.vuln_type or 'TBD'}", |
| "", |
| "## Summary", |
| finding.description or "_No description provided._", |
| "", |
| "## Impact", |
| finding.impact or "_Impact not yet assessed._", |
| "", |
| "## Steps to Reproduce", |
| finding.reproduction or "1. _(reproduction steps not yet documented)_", |
| "", |
| "## Evidence", |
| ] |
| if evidence_list: |
| for ev in evidence_list: |
| lines.append( |
| f"- **{ev.get('type', 'file')}**: `{ev.get('path', 'N/A')}` β {ev.get('description', '')}" |
| ) |
| else: |
| lines.append("_No evidence attached yet._") |
|
|
| lines.extend( |
| [ |
| "", |
| "## Suggested Fix", |
| finding.fix or "_No fix suggested yet._", |
| "", |
| "## Notes", |
| finding.notes or "_None._", |
| "", |
| f"---\n*Report generated by DRIFT Bug Bot at {datetime.now().isoformat(timespec='seconds')}*", |
| ] |
| ) |
| return "\n".join(lines) |
|
|
| def build_summary(self, findings: List[Finding]) -> str: |
| """Build a summary table of multiple findings.""" |
| if not findings: |
| return "No findings to summarize." |
| lines = [ |
| "# Bug Hunt Summary", |
| "", |
| "| ID | Title | Severity | Status | Asset |", |
| "|----|-------|----------|--------|-------|", |
| ] |
| for f in findings: |
| title = (f.title[:40] + "...") if len(f.title) > 40 else f.title |
| asset = ( |
| (f.asset[:30] + "...") |
| if f.asset and len(f.asset) > 30 |
| else (f.asset or "N/A") |
| ) |
| lines.append(f"| {f.id} | {title} | {f.severity} | {f.status} | {asset} |") |
| return "\n".join(lines) |
|
|
| def severity_justification(self, severity: str, impact: str) -> str: |
| """Return a pre-written severity justification block.""" |
| justifications = { |
| "P1": ( |
| "**P1 β Critical**\n" |
| "- Direct compromise of confidentiality, integrity, or availability.\n" |
| "- No user interaction required or trivial to trigger.\n" |
| "- Affects production data or core infrastructure." |
| ), |
| "P2": ( |
| "**P2 β High**\n" |
| "- Significant security impact with limited prerequisites.\n" |
| "- May require authenticated access or specific conditions.\n" |
| "- Could lead to data exposure or unauthorized actions." |
| ), |
| "P3": ( |
| "**P3 β Medium**\n" |
| "- Noticeable security weakness with moderate exploit complexity.\n" |
| "- Requires multiple steps or specific context to abuse.\n" |
| "- Impact is contained or requires chaining with other issues." |
| ), |
| "P4": ( |
| "**P4 β Low**\n" |
| "- Minor security concern or best-practice deviation.\n" |
| "- Exploitation is difficult or impact is negligible.\n" |
| "- Defense-in-depth issue rather than direct vulnerability." |
| ), |
| "P5": ( |
| "**P5 β Informational**\n" |
| "- No direct security impact.\n" |
| "- Useful for hardening or awareness.\n" |
| "- Sandbox-only or documentation-level finding." |
| ), |
| } |
| return justifications.get(severity, "Severity not classified.") |
|
|