diff --git a/backend/app/services/attck_evaluations_service.py b/backend/app/services/attck_evaluations_service.py index 3cc6e74..90ec6b2 100644 --- a/backend/app/services/attck_evaluations_service.py +++ b/backend/app/services/attck_evaluations_service.py @@ -64,10 +64,20 @@ _HEADERS = { # --------------------------------------------------------------------------- # Fallback: hardcoded public CrowdStrike ENTERPRISE rounds -# Used when evals.mitre.org API is unreachable (Cloudflare, outage, etc.) -# These are well-known, publicly documented rounds — safe to hardcode. +# Used when evals.mitre.org API is unreachable (Cloudflare 502, outage, etc.) +# +# Names use the EXACT slugs the live API returns (hyphens, not underscores). +# Verified from live API response on 2025-06-05. +# CrowdStrike did NOT participate in Round 6 (OilRig) — not included. # --------------------------------------------------------------------------- _FALLBACK_ROUNDS: list[dict[str, Any]] = [ + { + "name": "apt3", + "display_name": "APT3", + "eval_round": 1, + "domain": "ENTERPRISE", + "status": "PUBLIC", + }, { "name": "apt29", "display_name": "APT29", @@ -76,14 +86,14 @@ _FALLBACK_ROUNDS: list[dict[str, Any]] = [ "status": "PUBLIC", }, { - "name": "carbanak_fin7", + "name": "carbanak-fin7", "display_name": "Carbanak+FIN7", "eval_round": 3, "domain": "ENTERPRISE", "status": "PUBLIC", }, { - "name": "wizard_spider_sandworm", + "name": "wizard-spider-sandworm", "display_name": "Wizard Spider + Sandworm", "eval_round": 4, "domain": "ENTERPRISE", @@ -97,9 +107,9 @@ _FALLBACK_ROUNDS: list[dict[str, Any]] = [ "status": "PUBLIC", }, { - "name": "oilrig", - "display_name": "OilRig", - "eval_round": 6, + "name": "er7", + "display_name": "Enterprise 2025", + "eval_round": 7, "domain": "ENTERPRISE", "status": "PUBLIC", }, diff --git a/frontend/src/pages/SystemPage.tsx b/frontend/src/pages/SystemPage.tsx index 49147fc..1182dd1 100644 --- a/frontend/src/pages/SystemPage.tsx +++ b/frontend/src/pages/SystemPage.tsx @@ -438,18 +438,24 @@ export default function SystemPage() { - {/* API fallback warning */} - {!evalApiReachable && evalApiError && ( + {/* API fallback warning — only when no rounds loaded at all */} + {!evalApiReachable && evalApiError && (!evalRounds || evalRounds.length === 0) && (
- Live API unavailable - {" — "}showing known public rounds (hardcoded). Importing will attempt to fetch - live results and may also fail if the API remains unreachable. + MITRE Evaluations API temporarily unavailable + {" — "}could not load round list. The server may be overloaded. Try again later. {evalApiError}
)} + {/* Soft note when using fallback data but rounds are still visible */} + {!evalApiReachable && evalRounds && evalRounds.length > 0 && ( +
+ + Showing cached round list — live API had a transient error. Import may still work; retry if it fails. +
+ )} {/* New round check result */} {evalCheckResult && ( @@ -461,9 +467,15 @@ export default function SystemPage() { : "border-gray-700 bg-gray-800/50" }`}> {evalCheckResult.error ? ( -
- - Check failed: {evalCheckResult.error} +
+ +
+ + {evalCheckResult.error.includes("502") || evalCheckResult.error.includes("Cloudflare") || evalCheckResult.error.includes("origin") + ? "MITRE Evaluations API temporarily unavailable (server overload). Try again in a few minutes." + : `Check failed: ${evalCheckResult.error}`} + +
) : evalCheckResult.new_round_available ? (
@@ -514,12 +526,25 @@ export default function SystemPage() { {/* Import error */} {(importLatestMutation.isError || importRoundMutation.isError) && (
-
- - - {((importLatestMutation.error || importRoundMutation.error) as Error)?.message ?? - "Import failed. This round may already be imported."} - +
+ +
+ {(() => { + const msg = ((importLatestMutation.error || importRoundMutation.error) as Error)?.message ?? ""; + if (msg.includes("502") || msg.includes("Cloudflare") || msg.includes("origin") || msg.includes("Connection")) { + return ( + + MITRE Evaluations API is temporarily unavailable (server overload or maintenance). + The import fetches live results from evals.mitre.org — please try again in a few minutes. + + ); + } + if (msg.includes("already imported") || msg.includes("409")) { + return This round has already been imported.; + } + return {msg || "Import failed. This round may already be imported."}; + })()} +
)}