Files
Metamorph/tasks/testing-m4.md
Knacky 2c85f9b57e docs(m4): reconcile CHANGELOG + testing-m4 with the flat matrix + CR fixes
- CHANGELOG M4 Added: rewrote the frontend bullet to describe the actual
  flat ATT&CK matrix that ships (full-bleed, 15-col grid with minmax(7rem,
  1fr), name-only cells, ▸/▾ chevron). The original entry still described
  the abandoned 3-column drill-down picker.
- New "Fixed (post-M4 code-review pass)" subsection enumerating the six
  CR-driven fixes that landed in this branch (SSRF allowlist, advisory
  lock, typed contract, N+1 elimination, version clearing, error scrub +
  the test additions and e2e count pinning).
- DoD counts: 53 → 58 pytest, 34 e2e unchanged. testing-m4.md follows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:19:44 +02:00

6.1 KiB

type, milestone, date, project
type milestone date project
testing M4 2026-05-12 Metamorph

Testing M4 — MITRE ATT&CK Enterprise

1. Lancement de la stack

make clean       # reset si une stack tournait
make up          # build + start db/api/front
make migrate
make seed-mitre  # télécharge le bundle pinné v19.0 (~50 MB, ~1 s parse)

Permissions volume : metamorph_mitre est créé chowné metamorph:metamorph par le Dockerfile à la 1ʳᵉ initialisation. Si tu as un volume préexistant (d'une expé antérieure) appartenant à root, le seed échouera avec PermissionError. Solution : podman volume rm metamorph_metamorph_mitre avant make up.

2. Tests automatisés

make test-api    # 58 tests pytest dont 19 MITRE (parser, idempotence, security guards, all endpoints, dotted fallback, version clearing)
make e2e         # 34 tests Playwright dont 6 M4

Le rapport HTML est dans e2e/playwright-report/, le JUnit dans e2e/playwright-report/junit.xml.

3. Procédure manuelle (smoke navigateur)

Pré-requis

  • Stack up, migrations appliquées, make seed-mitre exécuté.
  • Le bundle est cache dans le volume metamorph_mitre (/data/mitre/enterprise-attack-19.0.json). Pour ré-utiliser un fichier local : flask --app app.cli metamorph seed-mitre --source /chemin/vers/enterprise-attack.json.

3.1 Page MITRE (/mitre)

  1. Se connecter en admin.
  2. Cliquer MITRE dans la nav → page chargée.
  3. Carte Source : vérifier version 19.0 + URL pinnée + Last sync non vide.
  4. Carte Sync (admin uniquement) : cliquer Trigger MITRE sync → bannière verte avec counts (15 tactics / 222 techniques / 475 subtechniques).
  5. Picker — matrice flat type attack.mitre.org :
    • La matrice tient sur la largeur de la page sans scroll horizontal (15 colonnes de largeur égale, partagent l'espace dispo).
    • Chaque header de colonne montre seulement le nom de la tactic (ex. Credential Access) + 17 techniques en petit dessous. L'external_id (TA0006) apparaît au hover (title).
    • Click sur le header Credential Access → toute la colonne est sélectionnée (chip cyan en haut, header en cyan filled).
    • Re-click pour désélectionner.
    • Les cellules affichent uniquement le nom de la technique (ex. OS Credential Dumping). L'external_id (T1003) apparaît au hover (title) et dans le chip de sélection.
    • Cliquer la cellule OS Credential Dumping → cellule en orange filled, chip T1003 · OS Credential Dumping en haut.
    • Cliquer le chevron ▸ 8 à droite de la cellule → la liste des sub-techniques se déploie inline dans la même colonne, chevron passe à ▾ 8.
    • Cliquer LSASS Memory (sub-technique) → cell purple filled, chip T1003.001 · LSASS Memory.
    • Click le chip pour le retirer.
    • La carte « Selected (preview payload) » sous la matrice montre le JSON cumulatif avec les external_id.

3.2 Filtre

  1. Taper dump dans le champ Filter → seules T1003 + sub-techniques restent visibles, les autres techniques sont cachées (mais leurs colonnes restent visibles pour préserver la grille).
  2. Taper TA0006 → idem mais filtre par external_id.
  3. Vider le filtre → toutes les cellules réapparaissent.

3.3 Non-admin

  1. Inviter un user sans perms via Admin > Invitations.
  2. Se connecter en tant que ce user.
  3. Naviguer sur /mitre → page accessible, picker fonctionnel (read-only).
  4. La carte Sync n'apparaît PAS (UI gate is_admin).
  5. Tenter POST /api/v1/mitre/sync via curl avec son token → 403 insufficient permissions.

3.4 Re-sync admin

ACCESS=$(curl -sX POST http://localhost:8080/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@metamorph.local","password":"AdminPass1234!"}' | jq -r .access_token)
curl -sX POST http://localhost:8080/api/v1/mitre/sync \
  -H "Authorization: Bearer $ACCESS" | jq

Sortie attendue :

{
  "tactics_upserted": 15,
  "techniques_upserted": 222,
  "subtechniques_upserted": 475,
  "subtechniques_skipped_orphan": 0,
  "technique_tactic_links": 254,
  "version": "19.0",
  "duration_ms": ~1000
}

3.5 Sync via URL custom

curl -sX POST http://localhost:8080/api/v1/mitre/sync \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d '{"source":"https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/enterprise-attack/enterprise-attack-18.1.json"}' | jq
  • Avec une URL ≠ pinned : sha256 désactivé, version est null (on ne connaît pas la version d'un fichier custom).

3.6 Mode air-gap

  1. Préparer un STIX 2.1 valide localement : enterprise-attack-19.0.json.
  2. Le copier dans le volume :
    podman cp enterprise-attack-19.0.json metamorph-api:/data/mitre/
    
  3. Lancer le seed pointé sur le path : podman compose exec api flask --app app.cli metamorph seed-mitre --source /data/mitre/enterprise-attack-19.0.json --skip-checksum

4. Points de contrôle critiques

  • make seed-mitre initial pinné v19.0 → 15/222/475 sans orphans.
  • Re-lancer le seed est idempotent (mêmes counts).
  • /mitre/tactics retourne 15 tactics (la spec mentionne 14 — MITRE en a 15 depuis v8).
  • /mitre/techniques?tactic=TA0006 retourne ≥ 17 techniques incl. T1003.
  • /mitre/subtechniques?technique=T1003 retourne 8 sub-techniques.
  • /mitre/status expose last_sync, version, source_url, default_url, default_version.
  • /mitre/sync exige la perm mitre.sync (admin via bypass is_admin).
  • Sha256 mismatch sur la pinned URL → 502 checksum_mismatch, DB intacte.
  • Bundle local (--source <path>) bypasse la vérif checksum.
  • Picker SPA : matrice flat attack.mitre.org-style — 15 colonnes equal-width sans scroll horizontal, cellules avec name only (external_id au hover + dans chips), chevron ▸ N / ▾ N → sub-techniques inline, chips multi-niveaux en haut.
  • GET /mitre/matrix retourne tous les tactics + leurs techniques + sub-techniques nestées en un seul appel (~55 KB pour v19).
  • Non-admin : voit la page /mitre mais pas la carte Sync ; POST /mitre/sync → 403.