fix(backend): AC-21.6 — matrix tactic_id returns TA-format (TA0007 not slug)

- mitre.py: add _SLUG_TO_TA_ID reverse map; _build_matrix() now emits tactic_id
  as TA-id (e.g. "TA0007") so frontend can send it back verbatim in PATCH tactic_ids
- test_mitre.py: update all matrix assertions to use TA-ids; add
  test_get_matrix_tactic_id_is_ta_format regression guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Knacky
2026-05-27 21:30:48 +02:00
parent 5aa839d105
commit a824df06b2
2 changed files with 27 additions and 13 deletions

View File

@@ -42,6 +42,9 @@ _TACTIC_IDS: dict[str, str] = {
"TA0040": "impact",
}
# Reverse: slug → TA-id (derived from _TACTIC_IDS, used by _build_matrix).
_SLUG_TO_TA_ID: dict[str, str] = {v: k for k, v in _TACTIC_IDS.items()}
TACTIC_NAMES: dict[str, str] = {
"initial-access": "Initial Access",
"execution": "Execution",
@@ -114,14 +117,16 @@ def _build_matrix(entries: list[dict[str, Any]]) -> list[dict[str, Any]]:
subs.sort(key=lambda x: x["name"])
matrix: list[dict[str, Any]] = []
for tactic_id in _TACTIC_ORDER:
techs = tactic_techs.get(tactic_id, [])
for slug in _TACTIC_ORDER:
techs = tactic_techs.get(slug, [])
# Sort techniques alphabetically.
techs_sorted = sorted(techs, key=lambda x: x["name"])
tactic_name = TACTIC_NAMES.get(tactic_id, tactic_id.replace("-", " ").title())
tactic_name = TACTIC_NAMES.get(slug, slug.replace("-", " ").title())
# Expose TA-id so the frontend can send tactic_ids back in PATCH unchanged.
ta_id = _SLUG_TO_TA_ID.get(slug, slug)
matrix.append(
{
"tactic_id": tactic_id,
"tactic_id": ta_id,
"tactic_name": tactic_name,
"techniques": [
{