Download src/report_generator.py from imperat0/strike: direct link, hf CLI and curl.
- Browser
- Download file 4.43 kB
-
https://huggingface.co/spaces/imperat0/strike/resolve/main/src/report_generator.py
- Command line
-
hf download hf://spaces/imperat0/strike/src/report_generator.py
-
curl -L -o report_generator.py https://huggingface.co/spaces/imperat0/strike/resolve/main/src/report_generator.py
4.43 kB
| from datetime import datetime | |
| from collections import Counter | |
| from typing import List | |
| from threat_kb import get_threats_for_component | |
| from detector import Detection | |
| SEVERIDADE_ORDEM = {"Crítica": 0, "Alta": 1, "Média": 2, "Baixa": 3} | |
| def _dedup_components(detections: List[Detection]) -> Counter: | |
| """Conta ocorrências únicas de cada tipo de componente (ignora bounding boxes duplicadas).""" | |
| return Counter(d.label for d in detections) | |
| def build_markdown_report(detections: List[Detection], demo_mode: bool = False, | |
| sistema_nome: str = "Sistema Analisado") -> str: | |
| counts = _dedup_components(detections) | |
| data_geracao = datetime.now().strftime("%d/%m/%Y %H:%M") | |
| linhas = [] | |
| linhas.append(f"# Relatório de Modelagem de Ameaças (STRIDE)") | |
| linhas.append(f"**Sistema:** {sistema_nome} ") | |
| linhas.append(f"**Data de geração:** {data_geracao} ") | |
| linhas.append(f"**Gerado por:** pipeline de detecção de componentes + base de conhecimento STRIDE ") | |
| if demo_mode: | |
| linhas.append("\n> ⚠️ **Modo demonstração**: nenhum modelo treinado foi encontrado em " | |
| "`models/best.pt`. Os componentes abaixo foram estimados por uma " | |
| "heurística de cor/contorno apenas para fins de demonstração da interface. " | |
| "Execute `train.py` para obter detecções reais do modelo supervisionado.\n") | |
| linhas.append("\n---\n") | |
| if not counts: | |
| linhas.append("Nenhum componente foi identificado no diagrama enviado. " | |
| "Verifique a qualidade/resolução da imagem e tente novamente.") | |
| return "\n".join(linhas) | |
| linhas.append("## 1. Componentes Identificados\n") | |
| linhas.append("| Componente | Ocorrências |") | |
| linhas.append("|---|---|") | |
| for label, qtd in sorted(counts.items(), key=lambda x: -x[1]): | |
| linhas.append(f"| {label.replace('_', ' ').title()} | {qtd} |") | |
| linhas.append("\n## 2. Ameaças por Componente (STRIDE)\n") | |
| total_ameacas = 0 | |
| for label in counts: | |
| threats = get_threats_for_component(label) | |
| if not threats: | |
| continue | |
| linhas.append(f"### {label.replace('_', ' ').title()}\n") | |
| threats_ordenadas = sorted(threats, key=lambda t: SEVERIDADE_ORDEM.get(t.severidade, 9)) | |
| for t in threats_ordenadas: | |
| total_ameacas += 1 | |
| linhas.append(f"**[{t.categoria}] {t.nome_categoria} — Severidade: {t.severidade}**") | |
| linhas.append(f"- *Ameaça:* {t.descricao}") | |
| linhas.append(f"- *Contramedida recomendada:* {t.contramedida}\n") | |
| linhas.append("## 3. Resumo Executivo\n") | |
| n_criticas = sum(1 for label in counts for t in get_threats_for_component(label) if t.severidade == "Crítica") | |
| n_altas = sum(1 for label in counts for t in get_threats_for_component(label) if t.severidade == "Alta") | |
| linhas.append(f"- Componentes identificados: **{len(counts)}**") | |
| linhas.append(f"- Ameaças mapeadas: **{total_ameacas}**") | |
| linhas.append(f"- Ameaças de severidade **Crítica**: **{n_criticas}**") | |
| linhas.append(f"- Ameaças de severidade **Alta**: **{n_altas}**") | |
| linhas.append("\n> Este relatório é gerado automaticamente por um MVP do fluxo e deve ser " | |
| "revisado por um especialista em segurança antes de decisões de mitigação.") | |
| return "\n".join(linhas) | |
| def save_pdf_report(markdown_text: str, output_path: str = "relatorio_stride.pdf"): | |
| """Converte o relatório (texto simples, sem renderização rica de markdown) para PDF.""" | |
| from fpdf import FPDF | |
| pdf = FPDF() | |
| pdf.set_auto_page_break(auto=True, margin=15) | |
| pdf.add_page() | |
| pdf.set_font("Helvetica", size=11) | |
| for raw_line in markdown_text.split("\n"): | |
| line = raw_line.replace("**", "").replace("### ", "").replace("## ", "").replace("# ", "") | |
| line = line.replace("*Ameaça:*", "Ameaca:").replace("*Contramedida recomendada:*", "Contramedida:") | |
| # A fonte core (Helvetica) do fpdf2 só suporta latin-1; removemos | |
| # caracteres fora dessa faixa (ex.: emojis) para evitar falhas de render. | |
| line = line.encode("latin-1", errors="ignore").decode("latin-1") | |
| if line.strip() == "": | |
| pdf.ln(4) | |
| continue | |
| pdf.set_x(pdf.l_margin) | |
| pdf.multi_cell(0, 6, line) | |
| pdf.output(output_path) | |
| return output_path | |