- 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>
6.1 KiB
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_mitreest créé chownémetamorph:metamorphpar le Dockerfile à la 1ʳᵉ initialisation. Si tu as un volume préexistant (d'une expé antérieure) appartenant à root, le seed échouera avecPermissionError. Solution :podman volume rm metamorph_metamorph_mitreavantmake 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-mitreexé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)
- Se connecter en admin.
- Cliquer MITRE dans la nav → page chargée.
- Carte Source : vérifier
version 19.0+ URL pinnée +Last syncnon vide. - Carte Sync (admin uniquement) : cliquer Trigger MITRE sync → bannière verte avec counts (15 tactics / 222 techniques / 475 subtechniques).
- 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 techniquesen 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 Dumpingen 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
- Taper
dumpdans 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). - Taper
TA0006→ idem mais filtre parexternal_id. - Vider le filtre → toutes les cellules réapparaissent.
3.3 Non-admin
- Inviter un user sans perms via Admin > Invitations.
- Se connecter en tant que ce user.
- Naviguer sur
/mitre→ page accessible, picker fonctionnel (read-only). - La carte Sync n'apparaît PAS (UI gate
is_admin). - Tenter
POST /api/v1/mitre/syncvia curl avec son token → 403insufficient 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é,
versionestnull(on ne connaît pas la version d'un fichier custom).
3.6 Mode air-gap
- Préparer un STIX 2.1 valide localement :
enterprise-attack-19.0.json. - Le copier dans le volume :
podman cp enterprise-attack-19.0.json metamorph-api:/data/mitre/ - 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-mitreinitial pinné v19.0 → 15/222/475 sans orphans.- Re-lancer le seed est idempotent (mêmes counts).
/mitre/tacticsretourne 15 tactics (la spec mentionne 14 — MITRE en a 15 depuis v8)./mitre/techniques?tactic=TA0006retourne ≥ 17 techniques incl. T1003./mitre/subtechniques?technique=T1003retourne 8 sub-techniques./mitre/statusexposelast_sync,version,source_url,default_url,default_version./mitre/syncexige la permmitre.sync(admin via bypassis_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/matrixretourne tous les tactics + leurs techniques + sub-techniques nestées en un seul appel (~55 KB pour v19).- Non-admin : voit la page
/mitremais pas la carte Sync ;POST /mitre/sync→ 403.