- True SSE streaming via broadcast channels (DashMap per session) - Session pause/resume with watch channels + dashboard buttons - AES-256-GCM credential encryption at rest (PENTEST_ENCRYPTION_KEY) - Concurrency limiter (Semaphore, max 5 sessions, 429 on overflow) - Browser tool: headless Chrome CDP automation (navigate, click, fill, screenshot, evaluate) - Report code-level correlation: SAST findings, code graph, SBOM linked per DAST finding - Split html.rs (1919 LOC) into html/ module directory (8 files) - Wizard: target/repo dropdowns from existing data, SSH key display, close button on all steps - Auth: auto-register with optional registration URL (Playwright discovery), plus-addressing email, IMAP overrides - Attack chain: tool input/output in detail panel, running node pulse animation - Architecture docs with Mermaid diagrams + 8 screenshots Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
128 lines
4.5 KiB
Rust
128 lines
4.5 KiB
Rust
use super::{html_escape, tool_category};
|
|
use compliance_core::models::pentest::{AuthMode, PentestConfig, PentestSession};
|
|
|
|
pub(super) fn scope(
|
|
session: &PentestSession,
|
|
target_name: &str,
|
|
target_url: &str,
|
|
date_str: &str,
|
|
completed_str: &str,
|
|
tool_names: &[String],
|
|
config: Option<&PentestConfig>,
|
|
) -> String {
|
|
let tools_table: String = tool_names
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, t)| {
|
|
let category = tool_category(t);
|
|
format!(
|
|
"<tr><td>{}</td><td><code>{}</code></td><td>{}</td></tr>",
|
|
i + 1,
|
|
html_escape(t),
|
|
category,
|
|
)
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join("\n");
|
|
|
|
let engagement_config_section = if let Some(cfg) = config {
|
|
let mut rows = String::new();
|
|
rows.push_str(&format!(
|
|
"<tr><td>Environment</td><td>{}</td></tr>",
|
|
html_escape(&cfg.environment.to_string())
|
|
));
|
|
if let Some(ref app_type) = cfg.app_type {
|
|
rows.push_str(&format!(
|
|
"<tr><td>Application Type</td><td>{}</td></tr>",
|
|
html_escape(app_type)
|
|
));
|
|
}
|
|
let auth_mode = match cfg.auth.mode {
|
|
AuthMode::None => "No authentication",
|
|
AuthMode::Manual => "Manual credentials",
|
|
AuthMode::AutoRegister => "Auto-register",
|
|
};
|
|
rows.push_str(&format!("<tr><td>Auth Mode</td><td>{auth_mode}</td></tr>"));
|
|
if !cfg.scope_exclusions.is_empty() {
|
|
let excl = cfg
|
|
.scope_exclusions
|
|
.iter()
|
|
.map(|s| html_escape(s))
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
rows.push_str(&format!(
|
|
"<tr><td>Scope Exclusions</td><td><code>{excl}</code></td></tr>"
|
|
));
|
|
}
|
|
if !cfg.tester.name.is_empty() {
|
|
rows.push_str(&format!(
|
|
"<tr><td>Tester</td><td>{} ({})</td></tr>",
|
|
html_escape(&cfg.tester.name),
|
|
html_escape(&cfg.tester.email)
|
|
));
|
|
}
|
|
if let Some(ref ts) = cfg.disclaimer_accepted_at {
|
|
rows.push_str(&format!(
|
|
"<tr><td>Disclaimer Accepted</td><td>{}</td></tr>",
|
|
ts.format("%B %d, %Y at %H:%M UTC")
|
|
));
|
|
}
|
|
if let Some(ref branch) = cfg.branch {
|
|
rows.push_str(&format!(
|
|
"<tr><td>Git Branch</td><td>{}</td></tr>",
|
|
html_escape(branch)
|
|
));
|
|
}
|
|
if let Some(ref commit) = cfg.commit_hash {
|
|
rows.push_str(&format!(
|
|
"<tr><td>Git Commit</td><td><code>{}</code></td></tr>",
|
|
html_escape(commit)
|
|
));
|
|
}
|
|
format!("<h3>Engagement Configuration</h3>\n<table class=\"info\">\n{rows}\n</table>")
|
|
} else {
|
|
String::new()
|
|
};
|
|
|
|
format!(
|
|
r##"
|
|
<!-- ═══════════════ 2. SCOPE & METHODOLOGY ═══════════════ -->
|
|
<div class="page-break"></div>
|
|
<h2><span class="section-num">2.</span> Scope & Methodology</h2>
|
|
|
|
<p>
|
|
The assessment was performed using an AI-driven orchestrator that autonomously selects and
|
|
executes security testing tools based on the target's attack surface, technology stack, and
|
|
any available static analysis (SAST) findings and SBOM data.
|
|
</p>
|
|
|
|
<h3>Engagement Details</h3>
|
|
<table class="info">
|
|
<tr><td>Target</td><td><strong>{target_name}</strong></td></tr>
|
|
<tr><td>URL</td><td><code>{target_url}</code></td></tr>
|
|
<tr><td>Strategy</td><td>{strategy}</td></tr>
|
|
<tr><td>Status</td><td>{status}</td></tr>
|
|
<tr><td>Started</td><td>{date_str}</td></tr>
|
|
<tr><td>Completed</td><td>{completed_str}</td></tr>
|
|
<tr><td>Tool Invocations</td><td>{tool_invocations} ({tool_successes} successful, {success_rate:.1}% success rate)</td></tr>
|
|
</table>
|
|
|
|
{engagement_config_section}
|
|
|
|
<h3>Tools Employed</h3>
|
|
<table class="tools-table">
|
|
<thead><tr><th>#</th><th>Tool</th><th>Category</th></tr></thead>
|
|
<tbody>{tools_table}</tbody>
|
|
</table>"##,
|
|
target_name = html_escape(target_name),
|
|
target_url = html_escape(target_url),
|
|
strategy = session.strategy,
|
|
status = session.status,
|
|
date_str = date_str,
|
|
completed_str = completed_str,
|
|
tool_invocations = session.tool_invocations,
|
|
tool_successes = session.tool_successes,
|
|
success_rate = session.success_rate(),
|
|
)
|
|
}
|