test(m4): pytest parser + endpoints + e2e tag picker
- backend/tests/test_mitre.py: 12 integration tests using a hand-crafted minimal STIX bundle (no network in tests). Covers parser (revoked/deprecated skip, sub-technique parent linkage), seed idempotence, persisted settings, checksum mismatch path, all four read endpoints, perm enforcement on /mitre/sync, ILIKE search. - e2e/tests/m4-mitre.spec.ts: 6 Playwright tests against the live stack. beforeAll calls POST /mitre/sync once (real bundle, ~50 MB, ~1.1 s) then the suite validates tactics ≥14, T1003 has ≥5 sub-techniques, the picker walks tactic→technique→subtechnique with chip multi-select, and non-admin sees /mitre but no Sync card. - tasks/testing-m4.md: manual + automated checklist, air-gapped operator notes, volume-permission caveat for pre-existing root-owned volumes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
111
tasks/testing-m4.md
Normal file
111
tasks/testing-m4.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
type: testing
|
||||
milestone: M4
|
||||
date: "2026-05-12"
|
||||
project: Metamorph
|
||||
---
|
||||
|
||||
# Testing M4 — MITRE ATT&CK Enterprise
|
||||
|
||||
## 1. Lancement de la stack
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
make test-api # 51 tests pytest dont 12 nouveaux MITRE (parser + endpoints)
|
||||
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 :
|
||||
- Cliquer **TA0006 — Credential Access** dans la colonne gauche.
|
||||
- La colonne du milieu liste **T1003 OS Credential Dumping** et 16 autres techniques.
|
||||
- Cliquer **T1003** → la colonne droite liste **T1003.001** à **T1003.008**.
|
||||
- Cocher la case en face de **T1003.001 PowerShell**.
|
||||
- Un chip cyan/orange/purple apparaît en haut + la carte « Selected (preview payload) » montre le JSON.
|
||||
- Cliquer le chip → désélection.
|
||||
|
||||
### 3.2 Filtres / recherche
|
||||
1. Dans la colonne **Tactic search**, taper `cred` → seule TA0006 reste.
|
||||
2. Sélectionner TA0006, dans **Technique search** taper `dump` → T1003 apparaît.
|
||||
3. Vider les recherches, sélectionner T1059 → vérifier T1059.001 à T1059.012 dans les sub-techniques.
|
||||
|
||||
### 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
|
||||
```bash
|
||||
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 :
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```bash
|
||||
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 :
|
||||
```bash
|
||||
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
|
||||
|
||||
- [x] `make seed-mitre` initial pinné v19.0 → 15/222/475 sans orphans.
|
||||
- [x] Re-lancer le seed est idempotent (mêmes counts).
|
||||
- [x] `/mitre/tactics` retourne 15 tactics (la spec mentionne 14 — MITRE en a 15 depuis v8).
|
||||
- [x] `/mitre/techniques?tactic=TA0006` retourne ≥ 17 techniques incl. T1003.
|
||||
- [x] `/mitre/subtechniques?technique=T1003` retourne 8 sub-techniques.
|
||||
- [x] `/mitre/status` expose `last_sync`, `version`, `source_url`, `default_url`, `default_version`.
|
||||
- [x] `/mitre/sync` exige la perm `mitre.sync` (admin via bypass `is_admin`).
|
||||
- [x] Sha256 mismatch sur la pinned URL → 502 `checksum_mismatch`, DB intacte.
|
||||
- [x] Bundle local (`--source <path>`) bypasse la vérif checksum.
|
||||
- [x] Picker SPA : tactic → technique → subtechnique, multi-select, déselection via chip cliquable.
|
||||
- [x] Non-admin : voit la page `/mitre` mais pas la carte Sync ; `POST /mitre/sync` → 403.
|
||||
Reference in New Issue
Block a user