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:
@@ -5,6 +5,100 @@ use compliance_core::models::sbom::SbomEntry;
|
||||
|
||||
use super::orchestrator::PentestOrchestrator;
|
||||
|
||||
/// Attempt to decrypt a field; if decryption fails, return the original value
|
||||
/// (which may be plaintext from before encryption was enabled).
|
||||
fn decrypt_field(value: &str) -> String {
|
||||
super::crypto::decrypt(value).unwrap_or_else(|| value.to_string())
|
||||
}
|
||||
|
||||
/// Build additional prompt sections from PentestConfig when present.
|
||||
fn build_config_sections(config: &PentestConfig) -> String {
|
||||
let mut sections = String::new();
|
||||
|
||||
// Authentication section
|
||||
match config.auth.mode {
|
||||
AuthMode::Manual => {
|
||||
sections.push_str("\n## Authentication\n");
|
||||
sections.push_str("- **Mode**: Manual credentials\n");
|
||||
if let Some(ref u) = config.auth.username {
|
||||
let decrypted = decrypt_field(u);
|
||||
sections.push_str(&format!("- **Username**: {decrypted}\n"));
|
||||
}
|
||||
if let Some(ref p) = config.auth.password {
|
||||
let decrypted = decrypt_field(p);
|
||||
sections.push_str(&format!("- **Password**: {decrypted}\n"));
|
||||
}
|
||||
sections.push_str(
|
||||
"Use these credentials to log in before testing authenticated endpoints.\n",
|
||||
);
|
||||
}
|
||||
AuthMode::AutoRegister => {
|
||||
sections.push_str("\n## Authentication\n");
|
||||
sections.push_str("- **Mode**: Auto-register\n");
|
||||
if let Some(ref url) = config.auth.registration_url {
|
||||
sections.push_str(&format!("- **Registration URL**: {url}\n"));
|
||||
} else {
|
||||
sections.push_str(
|
||||
"- **Registration URL**: Not provided — use Playwright to discover the registration page.\n",
|
||||
);
|
||||
}
|
||||
if let Some(ref email) = config.auth.verification_email {
|
||||
sections.push_str(&format!(
|
||||
"- **Verification Email**: Use plus-addressing from `{email}` \
|
||||
(e.g. `{base}+{{session_id}}@{domain}`) for email verification. \
|
||||
The system will poll the IMAP mailbox for verification links.\n",
|
||||
base = email.split('@').next().unwrap_or(email),
|
||||
domain = email.split('@').nth(1).unwrap_or("example.com"),
|
||||
));
|
||||
}
|
||||
sections.push_str(
|
||||
"Register a new test account using the registration page, then use it for testing.\n",
|
||||
);
|
||||
}
|
||||
AuthMode::None => {}
|
||||
}
|
||||
|
||||
// Custom headers
|
||||
if !config.custom_headers.is_empty() {
|
||||
sections.push_str("\n## Custom HTTP Headers\n");
|
||||
sections.push_str("Include these headers in all HTTP requests:\n");
|
||||
for (k, v) in &config.custom_headers {
|
||||
sections.push_str(&format!("- `{k}: {v}`\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Scope exclusions
|
||||
if !config.scope_exclusions.is_empty() {
|
||||
sections.push_str("\n## Scope Exclusions\n");
|
||||
sections.push_str("Do NOT test the following paths:\n");
|
||||
for path in &config.scope_exclusions {
|
||||
sections.push_str(&format!("- `{path}`\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Git context
|
||||
if config.git_repo_url.is_some() || config.branch.is_some() || config.commit_hash.is_some() {
|
||||
sections.push_str("\n## Git Context\n");
|
||||
if let Some(ref url) = config.git_repo_url {
|
||||
sections.push_str(&format!("- **Repository**: {url}\n"));
|
||||
}
|
||||
if let Some(ref branch) = config.branch {
|
||||
sections.push_str(&format!("- **Branch**: {branch}\n"));
|
||||
}
|
||||
if let Some(ref commit) = config.commit_hash {
|
||||
sections.push_str(&format!("- **Commit**: {commit}\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Environment
|
||||
sections.push_str(&format!(
|
||||
"\n## Environment\n- **Target environment**: {}\n",
|
||||
config.environment
|
||||
));
|
||||
|
||||
sections
|
||||
}
|
||||
|
||||
/// Return strategy guidance text for the given strategy.
|
||||
fn strategy_guidance(strategy: &PentestStrategy) -> &'static str {
|
||||
match strategy {
|
||||
@@ -155,6 +249,11 @@ impl PentestOrchestrator {
|
||||
let sast_section = build_sast_section(sast_findings);
|
||||
let sbom_section = build_sbom_section(sbom_entries);
|
||||
let code_section = build_code_section(code_context);
|
||||
let config_sections = session
|
||||
.config
|
||||
.as_ref()
|
||||
.map(build_config_sections)
|
||||
.unwrap_or_default();
|
||||
|
||||
format!(
|
||||
r#"You are an expert penetration tester conducting an authorized security assessment.
|
||||
@@ -178,7 +277,7 @@ impl PentestOrchestrator {
|
||||
|
||||
## Code Entry Points (Knowledge Graph)
|
||||
{code_section}
|
||||
|
||||
{config_sections}
|
||||
## Available Tools
|
||||
{tool_names}
|
||||
|
||||
@@ -186,15 +285,34 @@ impl PentestOrchestrator {
|
||||
1. Start by running reconnaissance (recon tool) to fingerprint the target and discover technologies.
|
||||
2. Run the OpenAPI parser to discover API endpoints from specs.
|
||||
3. Check infrastructure: DNS, DMARC, TLS, security headers, cookies, CSP, CORS.
|
||||
4. Based on SAST findings, prioritize testing endpoints where vulnerabilities were found in code.
|
||||
5. For each vulnerability type found in SAST, use the corresponding DAST tool to verify exploitability.
|
||||
6. If vulnerable dependencies are listed, try to trigger known CVE conditions against the running application.
|
||||
7. Test rate limiting on critical endpoints (login, API).
|
||||
8. Check for console.log leakage in frontend JavaScript.
|
||||
9. Analyze tool results and chain findings — if one vulnerability enables others, explore the chain.
|
||||
10. When testing is complete, provide a structured summary with severity and remediation.
|
||||
11. Always explain your reasoning before invoking each tool.
|
||||
12. When done, say "Testing complete" followed by a final summary.
|
||||
4. If the target requires authentication (auto-register mode), use the browser tool to:
|
||||
a. Navigate to the target — it will redirect to the login page.
|
||||
b. Click the "Register" link to reach the registration form.
|
||||
c. Fill all form fields (username, email with plus-addressing, password, name) one by one.
|
||||
d. Click submit. If a Terms & Conditions page appears, accept it.
|
||||
e. After registration, use the browser to navigate through the application pages.
|
||||
f. **Take a screenshot after each major page** for evidence in the report.
|
||||
5. Use the browser tool to explore the authenticated application — navigate to each section,
|
||||
use get_content to understand the page structure, and take screenshots.
|
||||
6. Based on SAST findings, prioritize testing endpoints where vulnerabilities were found in code.
|
||||
7. For each vulnerability type found in SAST, use the corresponding DAST tool to verify exploitability.
|
||||
8. If vulnerable dependencies are listed, try to trigger known CVE conditions against the running application.
|
||||
9. Test rate limiting on critical endpoints (login, API).
|
||||
10. Check for console.log leakage in frontend JavaScript.
|
||||
11. Analyze tool results and chain findings — if one vulnerability enables others, explore the chain.
|
||||
12. When testing is complete, provide a structured summary with severity and remediation.
|
||||
13. Always explain your reasoning before invoking each tool.
|
||||
14. When done, say "Testing complete" followed by a final summary.
|
||||
|
||||
## Browser Tool Usage
|
||||
- The browser tab **persists** between calls — cookies and login state are preserved.
|
||||
- After navigate, the response includes `elements` (links, inputs, buttons on the page).
|
||||
- Use `get_content` to see forms, links, buttons, headings, and page text.
|
||||
- Use `click` with CSS selectors to interact (e.g., `a:text('Register')`, `input[type='submit']`).
|
||||
- Use `fill` with selector + value to fill form fields (e.g., `input[name='email']`).
|
||||
- **Take screenshots** (`action: screenshot`) after important actions for evidence.
|
||||
- For SPA apps: a 200 HTTP status does NOT mean the page is accessible — check the actual
|
||||
page content with the browser tool to verify if it shows real data or a login redirect.
|
||||
|
||||
## Important
|
||||
- This is an authorized penetration test. All testing is permitted within the target scope.
|
||||
|
||||
Reference in New Issue
Block a user