fix: D3FEND expandable cards, System page cleanup, and multi-source improvements
- Make D3FEND defense cards clickable with expandable details and external link - Fix D3FEND URLs to use PascalCase technique names matching the ontology - Remove duplicate Import Atomic Red Team from System page (use Data Sources) - Add bulk Activate All / Deactivate All buttons with confirmation modal - Fix template admin list to show both active and inactive templates - Add PATCH /test-templates/bulk-activate backend endpoint - Auto-seed data sources on container startup via entrypoint.sh - Fix SigmaHQ, CALDERA, GTFOBins import issues - Register D3FEND sync handler in data sources router - Add CIS Controls v8 compliance framework import - Expand Test Catalog source filters (CALDERA, LOLBAS, GTFOBins) - Campaign Generate from Threat Actor now opens actor selector modal - Add coverage snapshot creation button to Comparison page - Update README with accurate data source and feature documentation
This commit is contained in:
@@ -1,17 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Loader2, AlertCircle, Download, FileText } from "lucide-react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { Loader2, AlertCircle, Download, FileText, Plus } from "lucide-react";
|
||||
import {
|
||||
getComplianceFrameworks,
|
||||
getFrameworkStatus,
|
||||
downloadComplianceCSV,
|
||||
importNistMappings,
|
||||
importCisMappings,
|
||||
type ComplianceFrameworkSummary,
|
||||
} from "../api/compliance";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import ComplianceGauge from "../components/compliance/ComplianceGauge";
|
||||
import ControlsTable from "../components/compliance/ControlsTable";
|
||||
|
||||
export default function CompliancePage() {
|
||||
const [selectedFrameworkId, setSelectedFrameworkId] = useState<string | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
const { user } = useAuth();
|
||||
const isAdmin = user?.role === "admin";
|
||||
|
||||
// Fetch available frameworks
|
||||
const {
|
||||
@@ -59,6 +65,24 @@ export default function CompliancePage() {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const importNist = useMutation({
|
||||
mutationFn: importNistMappings,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["compliance-frameworks"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["compliance-status"] });
|
||||
},
|
||||
});
|
||||
|
||||
const importCis = useMutation({
|
||||
mutationFn: importCisMappings,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["compliance-frameworks"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["compliance-status"] });
|
||||
},
|
||||
});
|
||||
|
||||
const isImporting = importNist.isPending || importCis.isPending;
|
||||
|
||||
if (isLoading && !frameworkStatus) {
|
||||
return (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
@@ -171,6 +195,35 @@ export default function CompliancePage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Import buttons for admin */}
|
||||
{isAdmin && (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-xs text-gray-500">Import frameworks:</span>
|
||||
<button
|
||||
onClick={() => importNist.mutate()}
|
||||
disabled={isImporting}
|
||||
className="flex items-center gap-1.5 rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-xs font-medium text-gray-300 hover:border-cyan-500/50 hover:text-white transition-colors disabled:opacity-50"
|
||||
>
|
||||
{importNist.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : <Plus className="h-3 w-3" />}
|
||||
NIST 800-53
|
||||
</button>
|
||||
<button
|
||||
onClick={() => importCis.mutate()}
|
||||
disabled={isImporting}
|
||||
className="flex items-center gap-1.5 rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-xs font-medium text-gray-300 hover:border-cyan-500/50 hover:text-white transition-colors disabled:opacity-50"
|
||||
>
|
||||
{importCis.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : <Plus className="h-3 w-3" />}
|
||||
CIS Controls v8
|
||||
</button>
|
||||
{(importNist.isSuccess || importCis.isSuccess) && (
|
||||
<span className="text-xs text-green-400">Import complete</span>
|
||||
)}
|
||||
{(importNist.isError || importCis.isError) && (
|
||||
<span className="text-xs text-red-400">Import failed</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Controls table */}
|
||||
{controls.length > 0 ? (
|
||||
<ControlsTable controls={controls} />
|
||||
@@ -179,7 +232,7 @@ export default function CompliancePage() {
|
||||
<div className="rounded-xl border border-gray-800 bg-gray-900 p-8 text-center">
|
||||
<AlertCircle className="mx-auto h-10 w-10 text-gray-600" />
|
||||
<p className="mt-3 text-gray-400">
|
||||
No compliance data available. Import a compliance framework from the System page.
|
||||
No compliance data available. Use the import buttons above to load a compliance framework.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user