feat: pentest onboarding — streaming, browser automation, reports, user cleanup (#16)
All checks were successful
All checks were successful
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:
@@ -7,8 +7,12 @@ use axum::Json;
|
||||
use mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
|
||||
use futures_util::StreamExt;
|
||||
|
||||
use compliance_core::models::dast::DastFinding;
|
||||
use compliance_core::models::finding::Finding;
|
||||
use compliance_core::models::pentest::*;
|
||||
use compliance_core::models::sbom::SbomEntry;
|
||||
|
||||
use crate::agent::ComplianceAgent;
|
||||
|
||||
@@ -103,6 +107,97 @@ pub async fn export_session_report(
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
|
||||
// Fetch SAST findings, SBOM, and code context for the linked repository
|
||||
let repo_id = session
|
||||
.repo_id
|
||||
.clone()
|
||||
.or_else(|| target.as_ref().and_then(|t| t.repo_id.clone()));
|
||||
|
||||
let (sast_findings, sbom_entries, code_context) = if let Some(ref rid) = repo_id {
|
||||
let sast: Vec<Finding> = match agent
|
||||
.db
|
||||
.findings()
|
||||
.find(doc! {
|
||||
"repo_id": rid,
|
||||
"status": { "$in": ["open", "triaged"] },
|
||||
})
|
||||
.sort(doc! { "severity": -1 })
|
||||
.limit(100)
|
||||
.await
|
||||
{
|
||||
Ok(mut cursor) => {
|
||||
let mut results = Vec::new();
|
||||
while let Some(Ok(f)) = cursor.next().await {
|
||||
results.push(f);
|
||||
}
|
||||
results
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
|
||||
let sbom: Vec<SbomEntry> = match agent
|
||||
.db
|
||||
.sbom_entries()
|
||||
.find(doc! {
|
||||
"repo_id": rid,
|
||||
"known_vulnerabilities": { "$exists": true, "$ne": [] },
|
||||
})
|
||||
.limit(50)
|
||||
.await
|
||||
{
|
||||
Ok(mut cursor) => {
|
||||
let mut results = Vec::new();
|
||||
while let Some(Ok(e)) = cursor.next().await {
|
||||
results.push(e);
|
||||
}
|
||||
results
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
|
||||
// Build code context from graph nodes
|
||||
let code_ctx: Vec<CodeContextHint> = match agent
|
||||
.db
|
||||
.graph_nodes()
|
||||
.find(doc! { "repo_id": rid, "is_entry_point": true })
|
||||
.limit(50)
|
||||
.await
|
||||
{
|
||||
Ok(mut cursor) => {
|
||||
let mut nodes_vec = Vec::new();
|
||||
while let Some(Ok(n)) = cursor.next().await {
|
||||
let linked_vulns: Vec<String> = sast
|
||||
.iter()
|
||||
.filter(|f| f.file_path.as_deref() == Some(&n.file_path))
|
||||
.map(|f| {
|
||||
format!(
|
||||
"[{}] {}: {} (line {})",
|
||||
f.severity,
|
||||
f.scanner,
|
||||
f.title,
|
||||
f.line_number.unwrap_or(0)
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
nodes_vec.push(CodeContextHint {
|
||||
endpoint_pattern: n.qualified_name.clone(),
|
||||
handler_function: n.name.clone(),
|
||||
file_path: n.file_path.clone(),
|
||||
code_snippet: String::new(),
|
||||
known_vulnerabilities: linked_vulns,
|
||||
});
|
||||
}
|
||||
nodes_vec
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
};
|
||||
|
||||
(sast, sbom, code_ctx)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), Vec::new())
|
||||
};
|
||||
|
||||
let config = session.config.clone();
|
||||
let ctx = crate::pentest::report::ReportContext {
|
||||
session,
|
||||
target_name,
|
||||
@@ -115,6 +210,10 @@ pub async fn export_session_report(
|
||||
body.requester_name
|
||||
},
|
||||
requester_email: body.requester_email,
|
||||
config,
|
||||
sast_findings,
|
||||
sbom_entries,
|
||||
code_context,
|
||||
};
|
||||
|
||||
let report = crate::pentest::generate_encrypted_report(&ctx, &body.password)
|
||||
|
||||
Reference in New Issue
Block a user