Initial commit: Compliance Scanner Agent
Autonomous security and compliance scanning agent for git repositories. Features: SAST (Semgrep), SBOM (Syft), CVE monitoring (OSV.dev/NVD), GDPR/OAuth pattern detection, LLM triage, issue creation (GitHub/GitLab/Jira), PR reviews, and Dioxus fullstack dashboard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
16
compliance-dashboard/src/components/app_shell.rs
Normal file
16
compliance-dashboard/src/components/app_shell.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::app::Route;
|
||||
use crate::components::sidebar::Sidebar;
|
||||
|
||||
#[component]
|
||||
pub fn AppShell() -> Element {
|
||||
rsx! {
|
||||
div { class: "app-shell",
|
||||
Sidebar {}
|
||||
main { class: "main-content",
|
||||
Outlet::<Route> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
compliance-dashboard/src/components/code_snippet.rs
Normal file
23
compliance-dashboard/src/components/code_snippet.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn CodeSnippet(
|
||||
code: String,
|
||||
#[props(default)] file_path: String,
|
||||
#[props(default)] line_number: u32,
|
||||
) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
if !file_path.is_empty() {
|
||||
div {
|
||||
style: "font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; font-family: monospace;",
|
||||
"{file_path}"
|
||||
if line_number > 0 {
|
||||
":{line_number}"
|
||||
}
|
||||
}
|
||||
}
|
||||
pre { class: "code-block", "{code}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
7
compliance-dashboard/src/components/mod.rs
Normal file
7
compliance-dashboard/src/components/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod app_shell;
|
||||
pub mod code_snippet;
|
||||
pub mod page_header;
|
||||
pub mod pagination;
|
||||
pub mod severity_badge;
|
||||
pub mod sidebar;
|
||||
pub mod stat_card;
|
||||
13
compliance-dashboard/src/components/page_header.rs
Normal file
13
compliance-dashboard/src/components/page_header.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn PageHeader(title: String, #[props(default)] description: String) -> Element {
|
||||
rsx! {
|
||||
div { class: "page-header",
|
||||
h2 { "{title}" }
|
||||
if !description.is_empty() {
|
||||
p { "{description}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
compliance-dashboard/src/components/pagination.rs
Normal file
33
compliance-dashboard/src/components/pagination.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Pagination(
|
||||
current_page: u64,
|
||||
total_pages: u64,
|
||||
on_page_change: EventHandler<u64>,
|
||||
) -> Element {
|
||||
if total_pages <= 1 {
|
||||
return rsx! {};
|
||||
}
|
||||
|
||||
rsx! {
|
||||
div { class: "pagination",
|
||||
button {
|
||||
class: "btn btn-ghost",
|
||||
disabled: current_page <= 1,
|
||||
onclick: move |_| on_page_change.call(current_page.saturating_sub(1)),
|
||||
"Previous"
|
||||
}
|
||||
span {
|
||||
style: "color: var(--text-secondary); font-size: 14px;",
|
||||
"Page {current_page} of {total_pages}"
|
||||
}
|
||||
button {
|
||||
class: "btn btn-ghost",
|
||||
disabled: current_page >= total_pages,
|
||||
onclick: move |_| on_page_change.call(current_page + 1),
|
||||
"Next"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
compliance-dashboard/src/components/severity_badge.rs
Normal file
16
compliance-dashboard/src/components/severity_badge.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn SeverityBadge(severity: String) -> Element {
|
||||
let class = match severity.to_lowercase().as_str() {
|
||||
"critical" => "badge badge-critical",
|
||||
"high" => "badge badge-high",
|
||||
"medium" => "badge badge-medium",
|
||||
"low" => "badge badge-low",
|
||||
_ => "badge badge-info",
|
||||
};
|
||||
|
||||
rsx! {
|
||||
span { class: class, "{severity}" }
|
||||
}
|
||||
}
|
||||
81
compliance-dashboard/src/components/sidebar.rs
Normal file
81
compliance-dashboard/src/components/sidebar.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
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 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: "Settings",
|
||||
route: Route::SettingsPage {},
|
||||
icon: rsx! { Icon { icon: BsGear, width: 18, height: 18 } },
|
||||
},
|
||||
];
|
||||
|
||||
rsx! {
|
||||
nav { class: "sidebar",
|
||||
div { class: "sidebar-header",
|
||||
Icon { icon: BsShieldCheck, width: 24, height: 24 }
|
||||
h1 { "Compliance Scanner" }
|
||||
}
|
||||
div { class: "sidebar-nav",
|
||||
for item in nav_items {
|
||||
{
|
||||
let is_active = match (¤t_route, &item.route) {
|
||||
(Route::FindingDetailPage { .. }, Route::FindingsPage {}) => 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}
|
||||
span { "{item.label}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
style: "padding: 16px; border-top: 1px solid var(--border); font-size: 12px; color: var(--text-secondary);",
|
||||
"v0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
compliance-dashboard/src/components/stat_card.rs
Normal file
21
compliance-dashboard/src/components/stat_card.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn StatCard(
|
||||
label: String,
|
||||
value: String,
|
||||
#[props(default)] color: String,
|
||||
) -> Element {
|
||||
let value_style = if color.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("color: {color}")
|
||||
};
|
||||
|
||||
rsx! {
|
||||
div { class: "stat-card",
|
||||
div { class: "label", "{label}" }
|
||||
div { class: "value", style: value_style, "{value}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user