feat: pentest onboarding — streaming, browser automation, reports, user cleanup (#16)
All checks were successful
CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 7s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Successful in 2s
CI / Deploy MCP (push) Successful in 2s

Complete pentest feature overhaul: SSE streaming, session-persistent browser tool (CDP), AES-256 credential encryption, auto-screenshots in reports, code-level remediation correlation, SAST triage chunking, context window optimization, test user cleanup (Keycloak/Auth0/Okta), wizard dropdowns, attack chain improvements, architecture docs with Mermaid diagrams.

Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Reviewed-on: #16
This commit was merged in pull request #16.
This commit is contained in:
2026-03-17 20:32:20 +00:00
parent 11e1c5f438
commit c461faa2fb
57 changed files with 8844 additions and 2423 deletions

View File

@@ -0,0 +1,127 @@
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 &amp; 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(),
)
}