From 93b4a700e6ca6f16131235c6cc134e7411b39b8e Mon Sep 17 00:00:00 2001 From: kitos Date: Fri, 5 Jun 2026 16:42:27 +0200 Subject: [PATCH] fix(evaluations): results API returns list of vendors, not dict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /api/results/ endpoint returns a LIST: [{name: crowdstrike, adversaries: [...]}] Previous code called data.get() on the list → AttributeError crash on every import. Fix: detect list vs dict response, extract the crowdstrike vendor entry first, then get its adversaries list. Keeps legacy dict fallback just in case. Co-Authored-By: Claude Sonnet 4.6 --- .../app/services/attck_evaluations_service.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/app/services/attck_evaluations_service.py b/backend/app/services/attck_evaluations_service.py index 90ec6b2..c262563 100644 --- a/backend/app/services/attck_evaluations_service.py +++ b/backend/app/services/attck_evaluations_service.py @@ -240,8 +240,24 @@ def fetch_results_for_adversary(adversary_name: str) -> list[dict[str, Any]]: logger.error("Failed to fetch ATT&CK Evaluations results: %s", exc) raise - # Find the adversary in the response - adversaries = data.get("adversaries", []) + # The results endpoint returns a LIST of vendor objects: + # [{"name": "crowdstrike", "adversaries": [{"Adversary_Name": "apt3", ...}, ...]}, ...] + # (not a dict — hence the explicit vendor lookup below) + if isinstance(data, list): + vendor_entry = next( + (v for v in data if isinstance(v, dict) and v.get("name", "").lower() == _VENDOR), + None, + ) + if not vendor_entry: + raise ValueError( + f"Vendor '{_VENDOR}' not found in results response. " + f"Available vendors: {[v.get('name') for v in data if isinstance(v, dict)]}" + ) + adversaries = vendor_entry.get("adversaries", []) + else: + # Fallback for legacy dict-shaped response (just in case API changes again) + adversaries = data.get("adversaries", []) + target = next( (a for a in adversaries if a.get("Adversary_Name", "").lower() == adversary_name.lower()), None,