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:
@@ -13,7 +13,6 @@ import {
|
||||
Shield,
|
||||
Search,
|
||||
FlaskConical,
|
||||
Download,
|
||||
Plus,
|
||||
ToggleLeft,
|
||||
ToggleRight,
|
||||
@@ -28,12 +27,11 @@ import {
|
||||
type IntelScanResponse,
|
||||
} from "../api/system";
|
||||
import {
|
||||
importAtomicTests,
|
||||
getTemplateStats,
|
||||
getAllTemplates,
|
||||
createTemplate,
|
||||
toggleTemplateActive,
|
||||
type ImportAtomicResponse,
|
||||
bulkActivateTemplates,
|
||||
type TemplateStats,
|
||||
type CreateTemplatePayload,
|
||||
} from "../api/test-templates";
|
||||
@@ -43,8 +41,8 @@ export default function SystemPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const [syncResult, setSyncResult] = useState<SyncMitreResponse | null>(null);
|
||||
const [intelResult, setIntelResult] = useState<IntelScanResponse | null>(null);
|
||||
const [importResult, setImportResult] = useState<ImportAtomicResponse | null>(null);
|
||||
const [showCreateForm, setShowCreateForm] = useState(false);
|
||||
const [bulkConfirm, setBulkConfirm] = useState<"activate" | "deactivate" | null>(null);
|
||||
|
||||
// ── Existing queries ─────────────────────────────────────────────
|
||||
const {
|
||||
@@ -71,7 +69,7 @@ export default function SystemPage() {
|
||||
isLoading: templatesLoading,
|
||||
} = useQuery({
|
||||
queryKey: ["templates-admin"],
|
||||
queryFn: () => getAllTemplates({ limit: 100 }),
|
||||
queryFn: () => getAllTemplates({ limit: 200 }),
|
||||
});
|
||||
|
||||
// ── Mutations ────────────────────────────────────────────────────
|
||||
@@ -92,12 +90,12 @@ export default function SystemPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const importAtomicMutation = useMutation({
|
||||
mutationFn: importAtomicTests,
|
||||
onSuccess: (data) => {
|
||||
setImportResult(data);
|
||||
queryClient.invalidateQueries({ queryKey: ["template-stats"] });
|
||||
const bulkActivateMutation = useMutation({
|
||||
mutationFn: (activate: boolean) => bulkActivateTemplates(activate),
|
||||
onSuccess: () => {
|
||||
setBulkConfirm(null);
|
||||
queryClient.invalidateQueries({ queryKey: ["templates-admin"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["template-stats"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["test-templates"] });
|
||||
},
|
||||
});
|
||||
@@ -281,70 +279,8 @@ export default function SystemPage() {
|
||||
TEMPLATE ADMINISTRATION (T-124)
|
||||
──────────────────────────────────────────────────────────────── */}
|
||||
|
||||
{/* Import Atomic Red Team + Stats */}
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
{/* Import Atomic Red Team */}
|
||||
<div className="rounded-xl border border-gray-800 bg-gray-900 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="rounded-lg bg-red-500/10 p-3">
|
||||
<Download className="h-6 w-6 text-red-400" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h2 className="text-lg font-semibold text-white">Import Atomic Red Team</h2>
|
||||
<p className="mt-1 text-sm text-gray-400">
|
||||
Import test templates from the Atomic Red Team repository by Red Canary, mapped to MITRE ATT&CK techniques.
|
||||
</p>
|
||||
|
||||
{importResult && (
|
||||
<div className="mt-4 rounded-lg border border-green-500/30 bg-green-900/20 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle className="h-4 w-4 text-green-400" />
|
||||
<span className="text-sm font-medium text-green-400">Import Complete</span>
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-3 gap-2 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-400">Imported:</span>
|
||||
<span className="ml-1 font-medium text-white">{importResult.imported}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Skipped:</span>
|
||||
<span className="ml-1 font-medium text-white">{importResult.skipped}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Parsed:</span>
|
||||
<span className="ml-1 font-medium text-white">{importResult.total_parsed}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{importAtomicMutation.isError && (
|
||||
<div className="mt-4 rounded-lg border border-red-500/30 bg-red-900/20 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<XCircle className="h-4 w-4 text-red-400" />
|
||||
<span className="text-sm text-red-400">
|
||||
Import failed: {(importAtomicMutation.error as Error)?.message}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => importAtomicMutation.mutate()}
|
||||
disabled={importAtomicMutation.isPending}
|
||||
className="mt-4 flex items-center gap-2 rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-500 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{importAtomicMutation.isPending ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Download className="h-4 w-4" />
|
||||
)}
|
||||
{importAtomicMutation.isPending ? "Importing..." : "Import Now"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Template Catalog Stats */}
|
||||
<div className="grid gap-6 lg:grid-cols-1">
|
||||
{/* Template Catalog Stats */}
|
||||
<div className="rounded-xl border border-gray-800 bg-gray-900 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
@@ -433,20 +369,85 @@ export default function SystemPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Bulk Activate Confirmation Modal */}
|
||||
{bulkConfirm && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div className="w-full max-w-md rounded-xl border border-gray-700 bg-gray-900 p-6 shadow-xl">
|
||||
<h3 className="text-lg font-semibold text-white">
|
||||
{bulkConfirm === "activate" ? "Activate All Templates" : "Deactivate All Templates"}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm text-gray-400">
|
||||
{bulkConfirm === "activate"
|
||||
? "This will activate ALL templates in the catalog, including previously deactivated ones. All templates will become available for test creation."
|
||||
: "This will deactivate ALL templates in the catalog. No templates will be available for test creation until reactivated."}
|
||||
</p>
|
||||
<p className="mt-2 text-sm font-medium text-yellow-400">
|
||||
This action affects all {templateStats?.total || 0} templates.
|
||||
</p>
|
||||
<div className="mt-4 flex items-center justify-end gap-3">
|
||||
<button
|
||||
onClick={() => setBulkConfirm(null)}
|
||||
className="rounded-lg border border-gray-700 bg-gray-800 px-4 py-2 text-sm font-medium text-gray-300 hover:border-gray-600 hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => bulkActivateMutation.mutate(bulkConfirm === "activate")}
|
||||
disabled={bulkActivateMutation.isPending}
|
||||
className={`flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors disabled:opacity-50 ${
|
||||
bulkConfirm === "activate"
|
||||
? "bg-green-600 hover:bg-green-500"
|
||||
: "bg-red-600 hover:bg-red-500"
|
||||
}`}
|
||||
>
|
||||
{bulkActivateMutation.isPending ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : bulkConfirm === "activate" ? (
|
||||
<ToggleRight className="h-4 w-4" />
|
||||
) : (
|
||||
<ToggleLeft className="h-4 w-4" />
|
||||
)}
|
||||
{bulkActivateMutation.isPending
|
||||
? "Processing..."
|
||||
: bulkConfirm === "activate"
|
||||
? "Activate All"
|
||||
: "Deactivate All"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Templates Management Table */}
|
||||
<div className="rounded-xl border border-gray-800 bg-gray-900 p-6">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
|
||||
<h2 className="text-lg font-semibold text-white flex items-center gap-2">
|
||||
<FlaskConical className="h-5 w-5 text-cyan-400" />
|
||||
Manage Templates
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setShowCreateForm(!showCreateForm)}
|
||||
className="flex items-center gap-1.5 rounded-lg bg-cyan-600 px-3 py-2 text-sm font-medium text-white hover:bg-cyan-500 transition-colors"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Create Custom Template
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setBulkConfirm("activate")}
|
||||
className="flex items-center gap-1.5 rounded-lg border border-green-500/30 bg-green-900/20 px-3 py-2 text-sm font-medium text-green-400 hover:bg-green-900/40 transition-colors"
|
||||
>
|
||||
<ToggleRight className="h-4 w-4" />
|
||||
Activate All
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setBulkConfirm("deactivate")}
|
||||
className="flex items-center gap-1.5 rounded-lg border border-red-500/30 bg-red-900/20 px-3 py-2 text-sm font-medium text-red-400 hover:bg-red-900/40 transition-colors"
|
||||
>
|
||||
<ToggleLeft className="h-4 w-4" />
|
||||
Deactivate All
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowCreateForm(!showCreateForm)}
|
||||
className="flex items-center gap-1.5 rounded-lg bg-cyan-600 px-3 py-2 text-sm font-medium text-white hover:bg-cyan-500 transition-colors"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Create Custom
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{templatesLoading ? (
|
||||
|
||||
Reference in New Issue
Block a user