File size: 5,182 Bytes
214209a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | """
PDF Report Generator — Generates LUMINA Mitigation Reports as PDF.
Uses fpdf2 (pure Python, no system dependencies) to produce
professional, branded PDF reports from mitigation report data.
"""
from fpdf import FPDF
from datetime import datetime
from typing import Dict, Any
import logging
logger = logging.getLogger(__name__)
class LuminaPDF(FPDF):
"""Custom PDF class with LUMINA branding."""
def header(self):
self.set_font("Helvetica", "B", 22)
self.set_text_color(30, 41, 59)
self.cell(0, 15, "LUMINA", new_x="LMARGIN", new_y="NEXT")
self.set_font("Helvetica", "", 10)
self.set_text_color(100, 116, 139)
self.cell(0, 6, "Cyber Defense Platform | Mitigation Report", new_x="LMARGIN", new_y="NEXT")
self.set_draw_color(59, 130, 246)
self.set_line_width(0.5)
self.line(10, self.get_y() + 3, 200, self.get_y() + 3)
self.ln(10)
def footer(self):
self.set_y(-20)
self.set_font("Helvetica", "I", 8)
self.set_text_color(148, 163, 184)
self.cell(
0, 10,
f"Generated on {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')} | Page {self.page_no()}/{{nb}} | CONFIDENTIAL",
align="C",
)
def section_title(self, title: str):
self.ln(4)
self.set_font("Helvetica", "B", 13)
self.set_text_color(30, 41, 59)
self.cell(0, 10, title, new_x="LMARGIN", new_y="NEXT")
self.set_draw_color(226, 232, 240)
self.line(10, self.get_y(), 200, self.get_y())
self.ln(4)
def body_text(self, text: str):
self.set_font("Helvetica", "", 10)
self.set_text_color(51, 65, 85)
self.multi_cell(0, 6, text)
self.ln(3)
def generate_mitigation_pdf(data: Dict[str, Any]) -> bytes:
"""Generate a PDF report from mitigation report data. Returns raw PDF bytes."""
pdf = LuminaPDF()
pdf.alias_nb_pages()
pdf.add_page()
pdf.set_auto_page_break(auto=True, margin=25)
# Risk level colors
risk_colors = {
"CRITICAL": (239, 68, 68),
"HIGH": (249, 115, 22),
"MODERATE": (245, 158, 11),
"LOW": (34, 197, 94),
}
# 1. Overall Risk Score
risk_score = data.get("overall_risk_score", 0)
risk_level = data.get("overall_risk_level", "UNKNOWN")
color = risk_colors.get(risk_level, (100, 116, 139))
pdf.set_font("Helvetica", "B", 16)
pdf.set_text_color(*color)
pdf.cell(0, 12, f"Overall Risk: {risk_score}/100 [{risk_level}]", new_x="LMARGIN", new_y="NEXT")
pdf.ln(2)
generated = data.get("generated_at", datetime.utcnow().isoformat())
pdf.set_font("Helvetica", "", 9)
pdf.set_text_color(148, 163, 184)
pdf.cell(0, 6, f"Report generated: {generated}", new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
# 2. Executive Summary
pdf.section_title("Executive Summary")
pdf.body_text(data.get("executive_summary", "No summary available."))
# 3. Threat Breakdown Table
pdf.section_title("Threat Breakdown by Category")
pdf.set_font("Helvetica", "B", 9)
pdf.set_fill_color(241, 245, 249)
pdf.set_text_color(51, 65, 85)
col_widths = [35, 28, 28, 30, 28, 35]
headers = ["Category", "Total", "High Risk", "Medium Risk", "Low Risk", "Trend"]
for i, h in enumerate(headers):
pdf.cell(col_widths[i], 8, h, border=1, fill=True, align="C")
pdf.ln()
pdf.set_font("Helvetica", "", 9)
for row in data.get("threat_breakdown", []):
pdf.cell(col_widths[0], 8, str(row.get("category", "")).title(), border=1)
pdf.cell(col_widths[1], 8, str(row.get("total_incidents", 0)), border=1, align="C")
pdf.cell(col_widths[2], 8, str(row.get("high_risk_count", 0)), border=1, align="C")
pdf.cell(col_widths[3], 8, str(row.get("medium_risk_count", 0)), border=1, align="C")
pdf.cell(col_widths[4], 8, str(row.get("low_risk_count", 0)), border=1, align="C")
trend = str(row.get("trend", "stable")).title()
pdf.cell(col_widths[5], 8, trend, border=1, align="C")
pdf.ln()
pdf.ln(4)
# 4. Mitigation Recommendations
pdf.section_title("Mitigation Recommendations")
priority_colors = {
"critical": (239, 68, 68),
"high": (249, 115, 22),
"medium": (245, 158, 11),
"low": (34, 197, 94),
}
for i, rec in enumerate(data.get("recommendations", []), 1):
priority = rec.get("priority", "medium")
p_color = priority_colors.get(priority, (100, 116, 139))
pdf.set_font("Helvetica", "B", 10)
pdf.set_text_color(*p_color)
pdf.cell(0, 8, f"{i}. [{priority.upper()}] {rec.get('title', '')}", new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", "", 9)
pdf.set_text_color(71, 85, 105)
category = rec.get("category", "")
desc = rec.get("description", "")
pdf.multi_cell(0, 5, f" Category: {category} | {desc}")
pdf.ln(3)
# 5. Risk Trend Analysis
pdf.section_title("Risk Trend Analysis")
pdf.body_text(data.get("risk_trend_analysis", "Insufficient data for trend analysis."))
return pdf.output()
|