// frontend/components/AdminTabs/SecurityTab.jsx import React, { useState } from "react"; import { scanWorkspace } from "../../utils/api.js"; /** * Security tab — runs a workspace scan via /api/security/scan-workspace * and renders findings grouped by severity. * * Best practices applied: * - Custom path input (defaults to ".") * - Loading spinner while scanning * - Error state with retry * - Empty state ("No findings") with green checkmark * - Findings grouped by severity (critical → info) * - Each finding shows file, line, CWE, recommendation * - Color-coded severity badges */ const SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"]; const SEVERITY_COLORS = { critical: { bg: "#7f1d1d", text: "#fecaca", border: "#991b1b" }, high: { bg: "#9a3412", text: "#fed7aa", border: "#c2410c" }, medium: { bg: "#78350f", text: "#fde68a", border: "#a16207" }, low: { bg: "#164e63", text: "#a5f3fc", border: "#0e7490" }, info: { bg: "#1e3a5f", text: "#93c5fd", border: "#3B82F6" }, }; function SeverityBadge({ severity }) { const c = SEVERITY_COLORS[severity] || SEVERITY_COLORS.info; return ( {severity} ); } export default function SecurityTab({ showToast }) { const [path, setPath] = useState("."); const [scanning, setScanning] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); const handleScan = async () => { setScanning(true); setError(null); setResult(null); try { const data = await scanWorkspace(path.trim() || "."); setResult(data); const findingsCount = data.findings?.length || 0; showToast?.( "Scan complete", findingsCount === 0 ? "No security findings." : `Found ${findingsCount} issue${findingsCount !== 1 ? "s" : ""}.` ); } catch (err) { setError(err?.message || "Scan failed"); } finally { setScanning(false); } }; // Group findings by severity const grouped = React.useMemo(() => { const out = {}; if (result?.findings) { for (const f of result.findings) { const sev = f.severity || "info"; if (!out[sev]) out[sev] = []; out[sev].push(f); } } return out; }, [result]); const totalFindings = result?.findings?.length || 0; return (
Scan your workspace for vulnerabilities, secrets, and insecure patterns (OWASP Top 10).
{/* Scan controls */}
{f.snippet}
)}
{f.recommendation && (