feat: pentest onboarding — streaming, browser automation, reports, user cleanup (#16)
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
@@ -7,6 +7,7 @@ use crate::components::attack_chain::AttackChainView;
use crate::components::severity_badge::SeverityBadge;
use crate::infrastructure::pentest::{
export_pentest_report, fetch_attack_chain, fetch_pentest_findings, fetch_pentest_session,
pause_pentest_session, resume_pentest_session,
};
#[component]
@@ -87,11 +88,13 @@ pub fn PentestSessionPage(session_id: String) -> Element {
};
let is_running = session_status == "running";
let is_paused = session_status == "paused";
let is_active = is_running || is_paused;
// Poll while running
// Poll while running or paused
use_effect(move || {
let _gen = *poll_gen.read();
if is_running {
if is_active {
spawn(async move {
#[cfg(feature = "web")]
gloo_timers::future::TimeoutFuture::new(3_000).await;
@@ -226,9 +229,55 @@ pub fn PentestSessionPage(session_id: String) -> Element {
" Running..."
}
}
if is_paused {
span { style: "font-size: 0.8rem; color: #d97706;",
Icon { icon: BsPauseCircle, width: 12, height: 12 }
" Paused"
}
}
}
}
div { style: "display: flex; gap: 8px;",
if is_running {
{
let sid_pause = session_id.clone();
rsx! {
button {
class: "btn btn-ghost",
style: "font-size: 0.85rem; color: #d97706; border-color: #d97706;",
onclick: move |_| {
let sid = sid_pause.clone();
spawn(async move {
let _ = pause_pentest_session(sid).await;
session.restart();
});
},
Icon { icon: BsPauseCircle, width: 14, height: 14 }
" Pause"
}
}
}
}
if is_paused {
{
let sid_resume = session_id.clone();
rsx! {
button {
class: "btn btn-ghost",
style: "font-size: 0.85rem; color: #16a34a; border-color: #16a34a;",
onclick: move |_| {
let sid = sid_resume.clone();
spawn(async move {
let _ = resume_pentest_session(sid).await;
session.restart();
});
},
Icon { icon: BsPlayCircle, width: 14, height: 14 }
" Resume"
}
}
}
}
button {
class: "btn btn-primary",
style: "font-size: 0.85rem;",