diff --git a/frontend/src/pages/ExecutiveDashboardPage.tsx b/frontend/src/pages/ExecutiveDashboardPage.tsx index 3973e99..8fc07e1 100644 --- a/frontend/src/pages/ExecutiveDashboardPage.tsx +++ b/frontend/src/pages/ExecutiveDashboardPage.tsx @@ -232,14 +232,39 @@ export default function ExecutiveDashboardPage() { }) .slice(0, 10); - // Coverage by tactic for bar chart - const tacticData = (tacticCoverage || []).map((tc) => ({ - name: tc.tactic - .split("-") - .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1)) - .join(" "), - coverage: tc.total > 0 ? Math.round(((tc.validated + tc.partial) / tc.total) * 100) : 0, - })); + // Official MITRE ATT&CK tactic order (slug → display label) + const MITRE_TACTIC_ORDER: Record = { + "reconnaissance": "Reconnaissance", + "resource-development": "Resource Development", + "initial-access": "Initial Access", + "execution": "Execution", + "persistence": "Persistence", + "privilege-escalation": "Privilege Escalation", + "defense-evasion": "Defense Evasion", + "credential-access": "Credential Access", + "discovery": "Discovery", + "lateral-movement": "Lateral Movement", + "collection": "Collection", + "command-and-control": "C2", + "exfiltration": "Exfiltration", + "impact": "Impact", + }; + + const tacticDataRaw = (tacticCoverage || []).map((tc) => { + const slug = tc.tactic.toLowerCase(); + const label = MITRE_TACTIC_ORDER[slug] || + tc.tactic.split("-").map((w: string) => w.charAt(0).toUpperCase() + w.slice(1)).join(" "); + const order = Object.keys(MITRE_TACTIC_ORDER).indexOf(slug); + return { + name: label, + slug, + order: order === -1 ? 99 : order, + coverage: tc.total > 0 ? Math.round(((tc.validated + tc.partial) / tc.total) * 100) : 0, + }; + }); + + // Sort by official MITRE order; include any unknown tactics at end + const tacticData = tacticDataRaw.sort((a, b) => a.order - b.order); const getBarColor = (coverage: number) => { if (coverage < 30) return "#ef4444"; @@ -512,24 +537,25 @@ export default function ExecutiveDashboardPage() { Coverage by Tactic - + - + + `${v}%`} - /> - [`${value}%`, "Coverage"]} /> - + {tacticData.map((entry, index) => (