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:
2026-02-10 13:22:23 +01:00
parent 8032b67fab
commit c2e9c687f4
19 changed files with 778 additions and 197 deletions

View File

@@ -114,3 +114,9 @@ export async function importNistMappings(): Promise<Record<string, unknown>> {
const { data } = await client.post("/compliance/import/nist-800-53");
return data;
}
/** Import CIS Controls v8 mappings (admin). */
export async function importCisMappings(): Promise<Record<string, unknown>> {
const { data } = await client.post("/compliance/import/cis-controls-v8");
return data;
}

View File

@@ -9,6 +9,7 @@ export interface TemplateFilters {
severity?: string;
mitre_technique_id?: string;
search?: string;
is_active?: boolean;
offset?: number;
limit?: number;
}
@@ -51,6 +52,8 @@ export async function getTemplates(
if (filters?.mitre_technique_id)
params.append("mitre_technique_id", filters.mitre_technique_id);
if (filters?.search) params.append("search", filters.search);
// Default to active-only for catalog; admin uses getAllTemplates without this filter
params.append("is_active", filters?.is_active !== undefined ? String(filters.is_active) : "true");
if (filters?.offset !== undefined)
params.append("offset", String(filters.offset));
if (filters?.limit !== undefined)
@@ -125,7 +128,8 @@ export async function toggleTemplateActive(
// ── All templates (include inactive, for admin) ────────────────────
/** Fetch all templates including inactive ones (for admin management). */
/** Fetch all templates including inactive ones (for admin management).
* Does NOT filter by is_active so the backend returns all templates. */
export async function getAllTemplates(
filters?: TemplateFilters,
): Promise<TestTemplate[]> {
@@ -135,6 +139,7 @@ export async function getAllTemplates(
if (filters?.search) params.append("search", filters.search);
if (filters?.offset !== undefined) params.append("offset", String(filters.offset));
if (filters?.limit !== undefined) params.append("limit", String(filters.limit));
// Explicitly don't pass is_active so backend returns ALL templates
const { data } = await client.get<TestTemplate[]>(
`/test-templates${params.toString() ? `?${params}` : ""}`,
@@ -142,6 +147,24 @@ export async function getAllTemplates(
return data;
}
// ── Bulk activate/deactivate (admin) ──────────────────────────────
export interface BulkActivateResponse {
detail: string;
affected: number;
is_active: boolean;
}
/** Activate or deactivate all templates. Admin only. */
export async function bulkActivateTemplates(
activate: boolean,
): Promise<BulkActivateResponse> {
const { data } = await client.patch<BulkActivateResponse>(
`/test-templates/bulk-activate?activate=${activate}`,
);
return data;
}
// ── Import Atomic Red Team ─────────────────────────────────────────
/** Trigger Atomic Red Team import. Admin only. */