fix(evaluations): bypass Cloudflare 403 with browser headers + hardcoded fallback rounds
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

- Add browser User-Agent and Referer headers to all evals.mitre.org requests
- fetch_rounds_with_status() returns api_reachable flag + rounds list
- Fallback to 5 known public CrowdStrike rounds (APT29/R2 through OilRig/R6)
  when live API is blocked, so UI always shows something actionable
- Router returns {rounds, api_reachable, api_error} instead of plain array
- Frontend shows orange warning banner when using fallback data
- Remove 502 HTTPException - rounds are always returned (live or fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-06-05 16:10:27 +02:00
parent e3e79be35a
commit a4cdc06534
4 changed files with 145 additions and 23 deletions

View File

@@ -53,6 +53,12 @@ export interface EvaluationRound {
techniques_covered: number | null;
}
export interface EvaluationRoundsResponse {
rounds: EvaluationRound[];
api_reachable: boolean;
api_error: string | null;
}
export interface EvaluationImportResult {
message: string;
created: number;
@@ -70,8 +76,8 @@ export interface NewRoundCheckResult {
}
/** List all public CrowdStrike evaluation rounds with import status. */
export async function listEvaluationRounds(): Promise<EvaluationRound[]> {
const { data } = await client.get<EvaluationRound[]>("/system/attck-evaluations/rounds");
export async function listEvaluationRounds(): Promise<EvaluationRoundsResponse> {
const { data } = await client.get<EvaluationRoundsResponse>("/system/attck-evaluations/rounds");
return data;
}

View File

@@ -39,6 +39,7 @@ import {
type SyncMitreResponse,
type IntelScanResponse,
type EvaluationRound,
type EvaluationRoundsResponse,
type EvaluationImportResult,
type NewRoundCheckResult,
} from "../api/system";
@@ -164,14 +165,18 @@ export default function SystemPage() {
// ── ATT&CK Evaluations queries & mutations ───────────────────────
const {
data: evalRounds,
data: evalRoundsData,
isLoading: evalRoundsLoading,
refetch: refetchEvalRounds,
} = useQuery<EvaluationRound[]>({
} = useQuery<EvaluationRoundsResponse>({
queryKey: ["eval-rounds"],
queryFn: listEvaluationRounds,
});
const evalRounds = evalRoundsData?.rounds;
const evalApiReachable = evalRoundsData?.api_reachable ?? true;
const evalApiError = evalRoundsData?.api_error ?? null;
const checkNewRoundMutation = useMutation({
mutationFn: checkNewEvaluationRound,
onSuccess: (data) => {
@@ -433,6 +438,19 @@ export default function SystemPage() {
</div>
</div>
{/* API fallback warning */}
{!evalApiReachable && evalApiError && (
<div className="mb-4 flex items-start gap-3 rounded-lg border border-orange-500/30 bg-orange-900/10 p-3">
<AlertCircle className="h-4 w-4 text-orange-400 flex-shrink-0 mt-0.5" />
<div className="text-xs text-orange-300">
<span className="font-medium">Live API unavailable</span>
{" — "}showing known public rounds (hardcoded). Importing will attempt to fetch
live results and may also fail if the API remains unreachable.
<span className="block mt-1 text-orange-400/70 font-mono truncate">{evalApiError}</span>
</div>
</div>
)}
{/* New round check result */}
{evalCheckResult && (
<div className={`mb-4 rounded-lg border p-3 ${
@@ -608,9 +626,11 @@ export default function SystemPage() {
</table>
</div>
) : (
<div className="flex items-center justify-center gap-2 py-8 text-gray-500 text-sm">
<AlertCircle className="h-4 w-4" />
<span>Unable to load evaluation rounds. Check network connectivity to evals.mitre.org.</span>
<div className="py-8 text-center">
<p className="text-sm text-gray-500">No evaluation rounds available.</p>
{evalApiError && (
<p className="mt-1 text-xs text-orange-400/70 font-mono">{evalApiError}</p>
)}
</div>
)}
</div>