feat: AI-driven automated penetration testing (#12)
Some checks failed
CI / Clippy (push) Failing after 1m51s
CI / Security Audit (push) Successful in 2m1s
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Format (push) Failing after 42s
CI / Deploy MCP (push) Has been skipped
Some checks failed
CI / Clippy (push) Failing after 1m51s
CI / Security Audit (push) Successful in 2m1s
CI / Tests (push) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Format (push) Failing after 42s
CI / Deploy MCP (push) Has been skipped
This commit was merged in pull request #12.
This commit is contained in:
@@ -11,6 +11,11 @@ use crate::infrastructure::dast::fetch_dast_findings;
|
||||
pub fn DastFindingsPage() -> Element {
|
||||
let findings = use_resource(|| async { fetch_dast_findings().await.ok() });
|
||||
|
||||
let mut filter_severity = use_signal(|| "all".to_string());
|
||||
let mut filter_vuln_type = use_signal(|| "all".to_string());
|
||||
let mut filter_exploitable = use_signal(|| "all".to_string());
|
||||
let mut search_text = use_signal(String::new);
|
||||
|
||||
rsx! {
|
||||
div { class: "back-nav",
|
||||
button {
|
||||
@@ -26,14 +31,105 @@ pub fn DastFindingsPage() -> Element {
|
||||
description: "Vulnerabilities discovered through dynamic application security testing",
|
||||
}
|
||||
|
||||
// Filter bar
|
||||
div { style: "display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; align-items: center;",
|
||||
// Search
|
||||
div { style: "flex: 1; min-width: 180px;",
|
||||
input {
|
||||
class: "chat-input",
|
||||
style: "width: 100%; padding: 6px 10px; font-size: 0.85rem;",
|
||||
placeholder: "Search title or endpoint...",
|
||||
value: "{search_text}",
|
||||
oninput: move |e| search_text.set(e.value()),
|
||||
}
|
||||
}
|
||||
// Severity
|
||||
select {
|
||||
style: "padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-secondary); color: var(--text-primary); font-size: 0.85rem;",
|
||||
value: "{filter_severity}",
|
||||
onchange: move |e| filter_severity.set(e.value()),
|
||||
option { value: "all", "All Severities" }
|
||||
option { value: "critical", "Critical" }
|
||||
option { value: "high", "High" }
|
||||
option { value: "medium", "Medium" }
|
||||
option { value: "low", "Low" }
|
||||
option { value: "info", "Info" }
|
||||
}
|
||||
// Vuln type
|
||||
select {
|
||||
style: "padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-secondary); color: var(--text-primary); font-size: 0.85rem;",
|
||||
value: "{filter_vuln_type}",
|
||||
onchange: move |e| filter_vuln_type.set(e.value()),
|
||||
option { value: "all", "All Types" }
|
||||
option { value: "sql_injection", "SQL Injection" }
|
||||
option { value: "xss", "XSS" }
|
||||
option { value: "auth_bypass", "Auth Bypass" }
|
||||
option { value: "ssrf", "SSRF" }
|
||||
option { value: "api_misconfiguration", "API Misconfiguration" }
|
||||
option { value: "open_redirect", "Open Redirect" }
|
||||
option { value: "idor", "IDOR" }
|
||||
option { value: "information_disclosure", "Information Disclosure" }
|
||||
option { value: "security_misconfiguration", "Security Misconfiguration" }
|
||||
option { value: "broken_auth", "Broken Auth" }
|
||||
option { value: "dns_misconfiguration", "DNS Misconfiguration" }
|
||||
option { value: "email_security", "Email Security" }
|
||||
option { value: "tls_misconfiguration", "TLS Misconfiguration" }
|
||||
option { value: "cookie_security", "Cookie Security" }
|
||||
option { value: "csp_issue", "CSP Issue" }
|
||||
option { value: "cors_misconfiguration", "CORS Misconfiguration" }
|
||||
option { value: "rate_limit_absent", "Rate Limit Absent" }
|
||||
option { value: "console_log_leakage", "Console Log Leakage" }
|
||||
option { value: "security_header_missing", "Security Header Missing" }
|
||||
option { value: "known_cve_exploit", "Known CVE Exploit" }
|
||||
option { value: "other", "Other" }
|
||||
}
|
||||
// Exploitable
|
||||
select {
|
||||
style: "padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-secondary); color: var(--text-primary); font-size: 0.85rem;",
|
||||
value: "{filter_exploitable}",
|
||||
onchange: move |e| filter_exploitable.set(e.value()),
|
||||
option { value: "all", "All" }
|
||||
option { value: "yes", "Exploitable" }
|
||||
option { value: "no", "Unconfirmed" }
|
||||
}
|
||||
}
|
||||
|
||||
div { class: "card",
|
||||
match &*findings.read() {
|
||||
Some(Some(data)) => {
|
||||
let finding_list = &data.data;
|
||||
if finding_list.is_empty() {
|
||||
rsx! { p { "No DAST findings yet. Run a scan to discover vulnerabilities." } }
|
||||
let sev_filter = filter_severity.read().clone();
|
||||
let vt_filter = filter_vuln_type.read().clone();
|
||||
let exp_filter = filter_exploitable.read().clone();
|
||||
let search = search_text.read().to_lowercase();
|
||||
|
||||
let filtered: Vec<_> = data.data.iter().filter(|f| {
|
||||
let severity = f.get("severity").and_then(|v| v.as_str()).unwrap_or("info");
|
||||
let vuln_type = f.get("vuln_type").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let exploitable = f.get("exploitable").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let title = f.get("title").and_then(|v| v.as_str()).unwrap_or("").to_lowercase();
|
||||
let endpoint = f.get("endpoint").and_then(|v| v.as_str()).unwrap_or("").to_lowercase();
|
||||
|
||||
(sev_filter == "all" || severity == sev_filter)
|
||||
&& (vt_filter == "all" || vuln_type == vt_filter)
|
||||
&& match exp_filter.as_str() {
|
||||
"yes" => exploitable,
|
||||
"no" => !exploitable,
|
||||
_ => true,
|
||||
}
|
||||
&& (search.is_empty() || title.contains(&search) || endpoint.contains(&search))
|
||||
}).collect();
|
||||
|
||||
if filtered.is_empty() {
|
||||
if data.data.is_empty() {
|
||||
rsx! { p { style: "padding: 16px;", "No DAST findings yet. Run a scan to discover vulnerabilities." } }
|
||||
} else {
|
||||
rsx! { p { style: "padding: 16px; color: var(--text-secondary);", "No findings match the current filters." } }
|
||||
}
|
||||
} else {
|
||||
rsx! {
|
||||
div { style: "padding: 8px 16px; font-size: 0.8rem; color: var(--text-secondary);",
|
||||
"Showing {filtered.len()} of {data.data.len()} findings"
|
||||
}
|
||||
table { class: "table",
|
||||
thead {
|
||||
tr {
|
||||
@@ -46,7 +142,7 @@ pub fn DastFindingsPage() -> Element {
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
for finding in finding_list {
|
||||
for finding in filtered {
|
||||
{
|
||||
let id = finding.get("_id").and_then(|v| v.get("$oid")).and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let severity = finding.get("severity").and_then(|v| v.as_str()).unwrap_or("info").to_string();
|
||||
|
||||
Reference in New Issue
Block a user