Adds the mission layer that materialises template snapshots, plus the SPA list / 3-step wizard / detail page. Backend: - app/services/missions.py — create_mission snapshots scenarios, tests, MITRE tags in a 4-query write; list/get apply a non-admin membership filter that collapses to 404 (no existence leak); status state machine enforces draft → in_progress → completed → archived with archived as a sink; the non-admin creator is auto-added as role_hint='red' to retain visibility. - app/api/missions.py — 8 endpoints (list, get, create, update, add scenarios, set members, transition, soft-delete) with strict pydantic schemas. The transition endpoint splits the perm gate manually so archive requires mission.archive while other targets use mission.update. - app/api/users.py — new GET /users/roster returning (id, email, display_name) only, gated by user.read OR mission.create OR mission.update — lets non-admin wizard users see assignable peers without exposing the admin /users payload. - app/api/diag.py — /diag/reset truncates the mission_* tables before the template tables because the source_*_template_id FKs are ON DELETE SET NULL, which is cheaper to short-circuit by removing the children first. Frontend: - lib/missions.ts — typed client, queryKey factory, status accent map. - pages/MissionsListPage.tsx — list cards with status accent + filters (q, client, status). - pages/MissionsCreatePage.tsx — 3-step wizard (meta → scenarios → members) with member roster fed by /users/roster. - pages/MissionDetailPage.tsx — header + transition buttons (legal next states only) + Tests/Members/Synthesis/Export tabs. - Routes + nav entry (visible to anyone with mission.read or admin). Tests: - backend/tests/test_missions.py — 22 pytest covering snapshot fidelity, MITRE propagation, membership visibility, transition state machine, perm gating, member set replace, append scenarios, soft-delete, partial update, inverted-date rejection. - e2e/tests/m6-missions.spec.ts — 5 Playwright (snapshot freezing, non-admin visibility, status transitions + 409, SPA wizard end-to-end, list filter). Docs: - CHANGELOG, tasks/testing-m6.md, tasks/lessons.md (snapshot tradeoffs, membership=404 pattern, /diag/reset order, auto-creator add). - README + tasks/todo.md updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.3 KiB
5.3 KiB
type, milestone, date, project
| type | milestone | date | project |
|---|---|---|---|
| testing | M6 | 2026-05-13 | Metamorph |
Testing M6 — Missions & snapshot
1. Lancement de la stack
make clean
make up
make migrate
make seed-mitre # MITRE tags are snapshotted onto mission_tests; without them
# the snapshot will simply have an empty mitre_tags array
L'admin stable
admin@metamorph.local / AdminPass1234!est restauré automatiquement par le hookafterAlldu spec e2e M6, mais la 1ʳᵉ fois, bootstrappe-le via/setup(ou laisse les tests faire le travail).
2. Tests automatisés
make test-api # 103 tests pytest dont 22 M6 (snapshot, membership, transitions, members CRUD, perm gating)
make e2e # 43 tests Playwright dont 5 M6 (snapshot freezing, non-admin visibility, transitions, wizard, list filter)
Rapport HTML : e2e/playwright-report/.
3. Smoke navigateur
Pré-requis
- Stack
make up+ admin loggé. - MITRE seedé (
/mitremontre 15 tactics). - Au moins 1 test_template et 1 scenario_template dans le catalogue M5 (pour avoir quelque chose à snapshotter).
3.1 Liste & création (/missions)
- Cliquer Missions dans la nav (visible si tu as la perm
mission.readou tu es admin) → la liste s'affiche avec un message vide la 1ʳᵉ fois. - Cliquer + New mission → page wizard
/missions/new. - Étape 1 — Metadata :
Name(requis) →purple-2026-Q2Client / target→Acme CorpStart date/End date→ si tu inverses, un message en rouge apparaît et Next est désactivé.ROE / Description(markdown) → optionnel.
- Next → Étape 2 — Scenarios :
- Le catalogue M5 s'affiche en grille de boutons. Cliquer un scénario le sélectionne (bordure cyan).
- Le sous-titre du Card affiche le total de tests qui seront snapshotés.
- Next → Étape 3 — Members :
- Le roster (issu de
/users/roster) liste les utilisateurs actifs. - Pour chaque user, deux boutons Red / Blue togglent l'inclusion + le rôle. ✕ retire.
- Si tu es un redteamer non-admin, tu es pré-sélectionné en
red(auto-add côté backend si tu oublies).
- Le roster (issu de
- Create mission → redirection vers
/missions/<id>. La nouvelle mission apparaît en haut de la liste après retour.
3.2 Filtres (/missions)
- Search : full-text sur
name/description_md. - Client : LIKE sur
client_target. - Status : select draft / in_progress / completed / archived.
- Les filtres sont combinés en AND (ex :
status=in_progress & client=acme).
3.3 Page détail (/missions/<id>)
- En-tête : nom + status pill + boutons de transition.
- draft → boutons
→ In Progresset→ Archived. - in_progress →
→ Completedet→ Archived. - completed →
→ Archiveduniquement. - archived → aucun bouton.
- draft → boutons
- Cliquer un bouton → status update immédiat (cache invalidé, badge re-rendu).
- Delete (en rose) → confirm prompt → soft-delete → redirige vers
/missions. Réapparait via?include_deleted=true(admin only). - Tabs :
- tests : tableau par scénario avec
# | Test | MITRE | OPSEC | State. Les MITRE chips affichent l'external_id frozen. - members : pills Red/Blue avec email + display_name.
- synthesis : placeholder « lands in M10 ».
- export : placeholder « lands in M11 ».
- tests : tableau par scénario avec
4. Vérification du snapshot (DoD)
- Crée une mission qui référence un scenario_template
sc1contenanttest_template_t1. - Aller dans
/admin/tests, éditertest_template_t1: changer le nom et les tags MITRE. - Retour sur
/missions/<id>(rafraîchir si la cache TanStack tient encore) → la table montre toujours l'ancien nom et l'ancien tag MITRE. Le snapshot est gelé. ✅
5. Vérification visibilité par membership
- Login en admin, créer 2 missions :
m-only-adminsans aucun membre.m-sharedavec Alice (red) en membre.
- Login en Alice.
/missions→ seulem-sharedapparaît dans la liste.GET /api/v1/missions/<m-only-admin>retourne 404 (pas 403 — pas de fuite d'existence).- Alice tente de PUT/transition/delete sur
m-only-admin→ 404 idem.
6. Vérification transitions
| from | to | result |
|---|---|---|
| draft | in_progress | 200 |
| draft | archived | 200 |
| draft | completed | 409 invalid_transition |
| in_progress | completed | 200 |
| in_progress | archived | 200 |
| completed | archived | 200 |
| completed | in_progress | 409 |
| archived | (anything) | 409 |
| any | (same status) | 200 (no-op) |
curl -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
-d '{"status":"completed"}' \
http://localhost:8080/api/v1/missions/<id>/transition
7. Quick teardown
make down
# ou pour un reset complet :
curl -X POST http://localhost:8080/api/v1/diag/reset # test-only, wipes everything
Reminder:
make test-apiandmake e2eshare the dev DB container — running them mid-session WILL wipe user data. The M6 spec'safterAllrestores the stable admin and re-seeds MITRE, but custom templates / missions you've created by hand are lost. Cf.tasks/lessons.md(M5 lessons section).