Files
compliance-scanner-agent/compliance-dashboard/src/components/sidebar.rs
Sharang Parnerkar 71d8741e10 feat: AI-driven automated penetration testing system
Add a complete AI pentest system where Claude autonomously drives security
testing via tool-calling. The LLM selects from 16 tools, chains results,
and builds an attack chain DAG.

Core:
- PentestTool trait (dyn-compatible) with PentestToolContext/Result
- PentestSession, AttackChainNode, PentestMessage, PentestEvent models
- 10 new DastVulnType variants (DNS, DMARC, TLS, cookies, CSP, CORS, etc.)
- LLM client chat_with_tools() for OpenAI-compatible tool calling

Tools (16 total):
- 5 agent wrappers: SQL injection, XSS, auth bypass, SSRF, API fuzzer
- 11 new infra tools: DNS checker, DMARC checker, TLS analyzer,
  security headers, cookie analyzer, CSP analyzer, rate limit tester,
  console log detector, CORS checker, OpenAPI parser, recon
- ToolRegistry for tool lookup and LLM definition generation

Orchestrator:
- PentestOrchestrator with iterative tool-calling loop (max 50 rounds)
- Attack chain node recording per tool invocation
- SSE event broadcasting for real-time progress
- Strategy-aware system prompts (quick/comprehensive/targeted/aggressive/stealth)

API (9 endpoints):
- POST/GET /pentest/sessions, GET /pentest/sessions/:id
- POST /pentest/sessions/:id/chat, GET /pentest/sessions/:id/stream
- GET /pentest/sessions/:id/attack-chain, messages, findings
- GET /pentest/stats

Dashboard:
- Pentest dashboard with stat cards, severity distribution, session list
- Chat-based session page with split layout (chat + findings/attack chain)
- Inline tool execution indicators, auto-polling, new session modal
- Sidebar navigation item

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:23:21 +01:00

150 lines
5.5 KiB
Rust

use compliance_core::models::auth::AuthInfo;
use dioxus::prelude::*;
use dioxus_free_icons::icons::bs_icons::*;
use dioxus_free_icons::Icon;
use crate::app::Route;
struct NavItem {
label: &'static str,
route: Route,
icon: Element,
}
#[component]
pub fn Sidebar() -> Element {
let current_route = use_route::<Route>();
let mut collapsed = use_signal(|| true);
let nav_items = [
NavItem {
label: "Overview",
route: Route::OverviewPage {},
icon: rsx! { Icon { icon: BsSpeedometer2, width: 18, height: 18 } },
},
NavItem {
label: "Repositories",
route: Route::RepositoriesPage {},
icon: rsx! { Icon { icon: BsFolder2Open, width: 18, height: 18 } },
},
NavItem {
label: "Findings",
route: Route::FindingsPage {},
icon: rsx! { Icon { icon: BsShieldExclamation, width: 18, height: 18 } },
},
NavItem {
label: "SBOM",
route: Route::SbomPage {},
icon: rsx! { Icon { icon: BsBoxSeam, width: 18, height: 18 } },
},
NavItem {
label: "Issues",
route: Route::IssuesPage {},
icon: rsx! { Icon { icon: BsListTask, width: 18, height: 18 } },
},
NavItem {
label: "DAST",
route: Route::DastOverviewPage {},
icon: rsx! { Icon { icon: BsBug, width: 18, height: 18 } },
},
NavItem {
label: "Pentest",
route: Route::PentestDashboardPage {},
icon: rsx! { Icon { icon: BsLightningCharge, width: 18, height: 18 } },
},
NavItem {
label: "Settings",
route: Route::SettingsPage {},
icon: rsx! { Icon { icon: BsGear, width: 18, height: 18 } },
},
];
let docs_url = option_env!("DOCS_URL").unwrap_or("/docs");
let sidebar_class = if collapsed() {
"sidebar collapsed"
} else {
"sidebar"
};
rsx! {
nav { class: "{sidebar_class}",
div { class: "sidebar-header",
Icon { icon: BsShieldCheck, width: 24, height: 24 }
if !collapsed() {
h1 { "Compliance Scanner" }
}
}
div { class: "sidebar-nav",
for item in nav_items {
{
let is_active = match (&current_route, &item.route) {
(Route::FindingDetailPage { .. }, Route::FindingsPage {}) => true,
(Route::DastTargetsPage {}, Route::DastOverviewPage {}) => true,
(Route::DastFindingsPage {}, Route::DastOverviewPage {}) => true,
(Route::DastFindingDetailPage { .. }, Route::DastOverviewPage {}) => true,
(Route::PentestSessionPage { .. }, Route::PentestDashboardPage {}) => true,
(a, b) => a == b,
};
let class = if is_active { "nav-item active" } else { "nav-item" };
rsx! {
Link {
to: item.route.clone(),
class: class,
{item.icon}
if !collapsed() {
span { "{item.label}" }
}
}
}
}
}
}
a {
href: "{docs_url}",
target: "_blank",
class: "nav-item",
Icon { icon: BsBook, width: 18, height: 18 }
if !collapsed() {
span { "Docs" }
}
}
button {
class: "sidebar-toggle",
onclick: move |_| collapsed.set(!collapsed()),
if collapsed() {
Icon { icon: BsChevronRight, width: 14, height: 14 }
} else {
Icon { icon: BsChevronLeft, width: 14, height: 14 }
}
}
{
let auth_info = use_context::<Signal<AuthInfo>>();
let info = auth_info();
let initials = info.name.chars().next().unwrap_or('U').to_uppercase().to_string();
let user_class = if collapsed() { "sidebar-user sidebar-user-collapsed" } else { "sidebar-user" };
rsx! {
div { class: "{user_class}",
div { class: "user-avatar",
if info.avatar_url.is_empty() {
span { class: "avatar-initials", "{initials}" }
} else {
img { src: "{info.avatar_url}", alt: "avatar", class: "avatar-img" }
}
}
if !collapsed() {
span { class: "user-name", "{info.name}" }
}
a {
href: "/logout",
class: if collapsed() { "logout-btn logout-btn-collapsed" } else { "logout-btn" },
title: "Sign out",
Icon { icon: BsBoxArrowRight, width: 16, height: 16 }
}
}
}
}
}
}
}