Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Pause/Resume timer:
- Add paused_at, red_paused_seconds, blue_paused_seconds fields to Test model
- Add pause_timer/resume_timer workflow functions with accumulated pause tracking
- Auto-resume on phase submit; subtract paused time from worklog duration
- Add POST /tests/{id}/pause-timer and resume-timer endpoints
- Update LiveTimer component with pause/resume button and paused visual state
- Wire pause/resume mutations through TestDetailPage and TestDetailHeader
Professional Reporting Engine - Fase 2:
- Add ReportEngine service with Jinja2 HTML rendering, WeasyPrint PDF, and docxtpl DOCX
- Add corporate CSS stylesheet with cover page, data tables, stats grid, findings
- Create purple_campaign, coverage_report, and executive_summary HTML templates
- Add report_generation_service collecting domain data for each report type
- Add professional_reports router: GET /reports/generate/purple-campaign/{id}, coverage-summary, executive-summary
- Add analytics router with flat JSON endpoints for PowerBI: /coverage, /tests, /trends, /operators
- Add advanced_metrics router: /coverage-by-tactic, /never-tested, /avg-validation-time, /detection-rate-trend
- Add weasyprint and docxtpl to requirements.txt
- Add REPORT_TEMPLATES_DIR, REPORT_OUTPUT_DIR, COMPANY_NAME, COMPANY_LOGO_PATH to config
131 lines
4.5 KiB
HTML
131 lines
4.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<link rel="stylesheet" href="styles/report.css">
|
|
<title>Purple Team Assessment Report — {{ campaign.name }}</title>
|
|
</head>
|
|
<body>
|
|
<section class="cover-page">
|
|
<img src="assets/logo.png" class="logo" alt="Logo">
|
|
<h1>Purple Team Assessment Report</h1>
|
|
<h2>{{ campaign.name }}</h2>
|
|
<p class="date">{{ generated_at }}</p>
|
|
<p class="classification">{{ classification | default('INTERNAL') }}</p>
|
|
</section>
|
|
|
|
<section class="toc">
|
|
<h2>Table of Contents</h2>
|
|
<ul>
|
|
<li>1. Executive Summary</li>
|
|
<li>2. Scope & Methodology</li>
|
|
<li>3. Techniques Tested</li>
|
|
<li>4. Critical Findings</li>
|
|
<li>5. Coverage Evolution</li>
|
|
<li>6. Recommendations</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>1. Executive Summary</h2>
|
|
<p>Campaign <strong>{{ campaign.name }}</strong> tested
|
|
{{ tests | length }} techniques across {{ tactics | length }} tactics.
|
|
Overall organization coverage score: <strong>{{ org_score }}%</strong>.</p>
|
|
<div class="stats-grid">
|
|
<div class="stat">
|
|
<span class="number">{{ tests_validated }}</span>
|
|
<span class="label">Validated</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="number">{{ tests_detected }}</span>
|
|
<span class="label">Detected</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="number">{{ tests_not_detected }}</span>
|
|
<span class="label">Not Detected</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>2. Scope & Methodology</h2>
|
|
<p>{{ campaign.description or 'No description provided.' }}</p>
|
|
{% if campaign.scheduled_at and campaign.completed_at %}
|
|
<p>Period: {{ campaign.scheduled_at }} — {{ campaign.completed_at }}</p>
|
|
{% endif %}
|
|
{% if threat_actors %}
|
|
<p>Threat actors modeled:
|
|
{% for actor in threat_actors %}{{ actor.name }}{% if not loop.last %}, {% endif %}{% endfor %}
|
|
</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section>
|
|
<h2>3. Techniques Tested</h2>
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>MITRE ID</th>
|
|
<th>Name</th>
|
|
<th>Tactic</th>
|
|
<th>State</th>
|
|
<th>Detection</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for test in tests %}
|
|
<tr class="result-{{ test.detection_result }}">
|
|
<td>{{ test.technique_mitre_id }}</td>
|
|
<td>{{ test.name }}</td>
|
|
<td>{{ test.tactic }}</td>
|
|
<td>{{ test.state }}</td>
|
|
<td>{{ test.detection_result }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>4. Critical Findings</h2>
|
|
{% if critical_findings %}
|
|
{% for finding in critical_findings %}
|
|
<div class="finding {{ finding.severity }}">
|
|
<h3>{{ finding.technique_id }}: {{ finding.name }}</h3>
|
|
<p>{{ finding.description }}</p>
|
|
<p><strong>Recommendation:</strong> {{ finding.recommendation }}</p>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p>No critical findings — all tested techniques were detected.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section>
|
|
<h2>5. Coverage Evolution</h2>
|
|
{% if previous_campaign %}
|
|
<p>Compared to previous campaign (<em>{{ previous_campaign.name }}</em>):
|
|
Coverage changed from {{ previous_score }}% to {{ org_score }}%.</p>
|
|
{% else %}
|
|
<p>This is the first campaign run — no historical comparison available.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section>
|
|
<h2>6. Recommendations</h2>
|
|
<ul>
|
|
{% for finding in critical_findings %}
|
|
<li><strong>{{ finding.technique_id }}</strong>: {{ finding.recommendation }}</li>
|
|
{% endfor %}
|
|
{% if not critical_findings %}
|
|
<li>Continue periodic purple team exercises to maintain coverage.</li>
|
|
{% endif %}
|
|
</ul>
|
|
</section>
|
|
|
|
<footer>
|
|
<p>{{ company_name }} — Confidential</p>
|
|
</footer>
|
|
</body>
|
|
</html>
|