use dioxus::prelude::*; use crate::components::page_header::PageHeader; use crate::components::severity_badge::SeverityBadge; use crate::infrastructure::dast::fetch_dast_finding_detail; #[component] pub fn DastFindingDetailPage(id: String) -> Element { let finding = use_resource(move || { let fid = id.clone(); async move { fetch_dast_finding_detail(fid).await.ok() } }); rsx! { PageHeader { title: "DAST Finding Detail", description: "Full evidence and details for a dynamic security finding", } div { class: "card", match &*finding.read() { Some(Some(resp)) => { let f = resp.data.clone(); let severity = f.get("severity").and_then(|v| v.as_str()).unwrap_or("info").to_string(); rsx! { div { class: "flex items-center gap-4 mb-4", SeverityBadge { severity: severity } h2 { "{f.get(\"title\").and_then(|v| v.as_str()).unwrap_or(\"Unknown Finding\")}" } } div { class: "grid grid-cols-2 gap-4 mb-4", div { strong { "Vulnerability Type: " } span { class: "badge", "{f.get(\"vuln_type\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } } div { strong { "CWE: " } span { "{f.get(\"cwe\").and_then(|v| v.as_str()).unwrap_or(\"N/A\")}" } } div { strong { "Endpoint: " } code { "{f.get(\"endpoint\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } } div { strong { "Method: " } span { "{f.get(\"method\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } } div { strong { "Parameter: " } code { "{f.get(\"parameter\").and_then(|v| v.as_str()).unwrap_or(\"N/A\")}" } } div { strong { "Exploitable: " } if f.get("exploitable").and_then(|v| v.as_bool()).unwrap_or(false) { span { class: "badge badge-danger", "Confirmed" } } else { span { class: "badge", "Unconfirmed" } } } } h3 { "Description" } p { "{f.get(\"description\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } if let Some(remediation) = f.get("remediation").and_then(|v| v.as_str()) { h3 { class: "mt-4", "Remediation" } p { "{remediation}" } } h3 { class: "mt-4", "Evidence" } if let Some(evidence_list) = f.get("evidence").and_then(|v| v.as_array()) { for (i, evidence) in evidence_list.iter().enumerate() { div { class: "card mb-3", h4 { "Evidence #{i + 1}" } div { class: "grid grid-cols-2 gap-2", div { strong { "Request: " } code { "{evidence.get(\"request_method\").and_then(|v| v.as_str()).unwrap_or(\"-\")} {evidence.get(\"request_url\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } } div { strong { "Response Status: " } span { "{evidence.get(\"response_status\").and_then(|v| v.as_u64()).unwrap_or(0)}" } } } if let Some(payload) = evidence.get("payload").and_then(|v| v.as_str()) { div { class: "mt-2", strong { "Payload: " } code { class: "block bg-gray-900 text-green-400 p-2 rounded mt-1", "{payload}" } } } if let Some(snippet) = evidence.get("response_snippet").and_then(|v| v.as_str()) { div { class: "mt-2", strong { "Response Snippet: " } pre { class: "block bg-gray-900 text-gray-300 p-2 rounded mt-1 overflow-x-auto text-sm", "{snippet}" } } } } } } else { p { "No evidence collected." } } } }, Some(None) => rsx! { p { "Finding not found." } }, None => rsx! { p { "Loading..." } }, } } } }