2026-05-28 07:18:21 +02:00
# Sprint 5 — Simulation templates
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
**Branche** : `sprint/5-templates`
**Statut** : 🟢 SPRINT COMPLET — backend 226/226 + frontend 121/121 + e2e 201/201, PR prête
**Base** : `main` @ `9873c53` (PR #7 sprint 4 mergé)
**Objectif** : permettre à un admin/redteam de créer des **templates de simulations ** pré-remplies (RT-side : name, description, commands, prerequisites, techniques, tactics). Instancier un template dans un engagement crée une nouvelle simulation décorrélée (copie indépendante — éditer l'instance ne touche pas le template et vice-versa). User QA item 8 sprint 3.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
---
2026-05-28 07:18:21 +02:00
## 0. SPEC.md à enrichir en début de sprint
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
Ajouter une section `## Templates de simulations` (entre § Fonctionnement et § Authentification & rôles) :
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
> Un **template de simulation** est une simulation pré-remplie côté redteam (name + description + commandes + pré-requis + techniques MITRE + tactiques MITRE) qui sert de point de départ pour instancier rapidement des simulations dans un engagement. Le template ne contient PAS de partie SOC, ni de date d'exécution, ni de résultat d'exécution — ces champs restent par-instance. L'instanciation d'un template dans un engagement crée une **nouvelle simulation indépendante** : le template et l'instance sont décorrélés, l'édition de l'un n'affecte pas l'autre. **Templates = ressource red team** : admin et redteam les gèrent (CRUD). SOC n'y a aucun accès (ni lecture, ni écriture, pas de nav link).
L'évolution est tracée dans CHANGELOG.md § Changed sprint 5.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-27 04:55:12 +02:00
---
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-27 04:55:12 +02:00
## 1. User stories
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
### US-26 — En tant qu'admin/redteam, je crée et gère des templates de simulations
**Pourquoi** : standardiser des simulations récurrentes (ex: "Mimikatz LSASS dump", "PowerShell empire stager") et éviter de retaper les mêmes commandes/MITRE à chaque engagement.
2026-05-27 19:41:16 +02:00
**Critères d'acceptation**
2026-05-28 07:18:21 +02:00
- [ ] AC-26.1 : modèle `SimulationTemplate` (table `simulation_templates` ) :
- `id` int PK
- `name` str NOT NULL UNIQUE (limite UX : un template unique par nom pour éviter les doublons dans le dropdown d'instanciation)
- `description` text nullable
- `commands` text nullable (chaîne multiligne, une commande par ligne, pattern sprint 2)
- `prerequisites` text nullable
- `techniques` JSON NOT NULL default `[]` (liste `[{id, name}]` , snapshot des techniques MITRE)
- `tactic_ids` JSON NOT NULL default `[]` (liste de TA-id strings)
- `created_at` datetime NOT NULL
- `updated_at` datetime nullable
- `created_by_id` int FK User
- [ ] AC-26.2 : migration Alembic `0005_simulation_templates.py` — CREATE TABLE simulation_templates + index sur `name` . Downgrade : DROP TABLE.
- [ ] AC-26.3 : `GET /api/templates` (admin|redteam) → liste `[{id, name, description, commands, prerequisites, techniques: [{id, name, tactics}], tactics: [{id, name}], created_at, created_by}]` , ordre `name ASC` . SOC → 403.
- [ ] AC-26.4 : `POST /api/templates` (admin|redteam) → 201 + template créé. Body : `{name, description?, commands?, prerequisites?, technique_ids?: [...], tactic_ids?: [...]}` . Valide : `name` non vide, name unique (409 si doublon), technique_ids / tactic_ids validés contre bundle MITRE / `_TACTIC_IDS` (réutilise les helpers `_resolve_technique_ids` / `_resolve_tactic_ids` sprint 3/4). SOC → 403.
- [ ] AC-26.5 : `GET /api/templates/<tid>` (admin|redteam) → 200 ou 404. SOC → 403.
- [ ] AC-26.6 : `PATCH /api/templates/<tid>` (admin|redteam) → 200, accepte les mêmes champs que POST en partial. Si `name` est modifié et entre en conflit avec un autre template → 409. SOC → 403.
- [ ] AC-26.7 : `DELETE /api/templates/<tid>` (admin|redteam) → 204. **Pas de cascade vers les simulations déjà instanciées ** — celles-ci sont décorrélées et survivent. SOC → 403.
- [ ] AC-26.8 : page `/admin/templates` (admin|redteam uniquement) liste les templates en table (Name, MITRE count, Created by, Updated at, Actions). Boutons "New template" + "Edit" + "Delete". Tous les endpoints templates sont gated `@role_required("admin", "redteam")` côté backend, et ProtectedRoute frontend impose le même filtre.
### US-27 — En tant que redteam, j'instancie un template dans un engagement
**Pourquoi** : c'est le use-case principal des templates.
2026-05-27 19:41:16 +02:00
**Critères d'acceptation**
2026-05-28 07:18:21 +02:00
- [ ] AC-27.1 : `POST /api/engagements/<eid>/simulations` (admin|redteam) accepte un nouveau paramètre optionnel `template_id` . Si présent, le serveur valide que le template existe (404 sinon), puis crée une nouvelle simulation en copiant :
- `name` ← template.name (peut être override par `name` du body si fourni)
- `description` ← template.description
- `commands` ← template.commands
- `prerequisites` ← template.prerequisites
- `techniques` ← template.techniques (deep copy)
- `tactic_ids` ← template.tactic_ids (deep copy)
- Autres champs : status=pending, executed_at=null, execution_result=null, SOC fields=null
- [ ] AC-27.2 : `POST` sans `template_id` garde le comportement sprint 2 (création vierge avec juste `name` ).
- [ ] AC-27.3 : la simulation créée depuis un template est **complètement décorrélée ** : modifier l'instance ne touche pas le template, modifier le template ne touche pas les instances existantes. Pas de FK `template_id` stockée sur la simulation (clean decoupling).
- [ ] AC-27.4 : auto-transition pending → in_progress NE se déclenche PAS lors de la création depuis un template (même si le template a un name + description + techniques non vides). La création reste status=pending — la transition se fera au prochain PATCH explicite de la redteam. Cohérent avec règle sprint 2 "trigger sur PATCH" pas "trigger sur création".
- [ ] AC-27.5 : engagement auto-status n'est PAS déclenché par l'instanciation (status reste planned). Coherent avec AC-27.4.
- [ ] AC-27.6 : sur `EngagementDetailPage` (sprint 2/3/4), le bouton "+ New" (ou équivalent UI) ouvre désormais un menu / dropdown / modal avec 2 options : "Blank" et "From template…". L'option "From template…" affiche la liste des templates disponibles avec leur nom + un aperçu (count techniques/tactics). Click sur un template → POST avec `template_id` + redirection sur la simu créée. **Si `useTemplates()` retourne une liste vide → la modale affiche un `<EmptyState title="No templates available" description="Create one from the Templates page" />`. NE PAS désactiver l'option "From template…" dans le dropdown ** (l'utilisateur doit pouvoir l'ouvrir pour comprendre qu'il n'y a rien — un disabled item silencieux serait confus).
- [ ] AC-27.7 : SOC n'a PAS accès au bouton d'instanciation (cohérent avec RBAC simulation creation sprint 2).
### US-28 — En tant qu'admin/redteam, j'accède aux templates depuis la nav
2026-05-27 19:41:16 +02:00
**Critères d'acceptation**
2026-05-28 07:18:21 +02:00
- [ ] AC-28.1 : `Layout.tsx` topbar nav contient un nouveau lien "Templates" (visible **uniquement à admin + redteam ** ). Pour SOC : le lien n'apparaît pas (cohérent avec "Users" qui est admin-only et masqué côté SOC).
- [ ] AC-28.2 : `ProtectedRoute` pour `/admin/templates` impose `roles=["admin", "redteam"]` . SOC qui tente d'y accéder en tapant l'URL → redirigé vers `/engagements` + toast "Accès refusé" (pattern existant ProtectedRoute sprint 1).
- [ ] AC-28.3 : la page `/admin/templates` n'inclut PAS de mode "read-only SOC" — elle est strictement admin+redteam. Les composants peuvent assumer `canEditTemplates = isAdmin || isRedteam = true` (toujours vrai à ce niveau).
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
---
## 2. Brief technique — Backend Builder
2026-05-28 07:18:21 +02:00
**Scope strict** : `backend/` . Pas de touche au frontend, e2e, docs, agents, scripts.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
### Livrables
2026-05-26 11:41:23 +02:00
2026-05-28 07:18:21 +02:00
**Modèle `SimulationTemplate` ** (`backend/app/models/simulation_template.py` — nouveau fichier)
2026-05-27 19:41:16 +02:00
```python
2026-05-28 07:18:21 +02:00
from datetime import UTC, datetime
from sqlalchemy.orm import Mapped, mapped_column
class SimulationTemplate(db.Model):
__tablename __ = "simulation_templates"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
description = db.Column(db.Text, nullable=True)
commands = db.Column(db.Text, nullable=True)
prerequisites = db.Column(db.Text, nullable=True)
techniques = db.Column(db.JSON, nullable=False, default=list)
tactic_ids = db.Column(db.JSON, nullable=False, default=list)
created_at = db.Column(db.DateTime, nullable=False, default=lambda: datetime.now(UTC))
updated_at = db.Column(db.DateTime, nullable=True)
created_by_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
created_by = db.relationship("User")
2026-05-27 19:41:16 +02:00
```
2026-05-28 07:18:21 +02:00
Ajouter à `backend/app/models/__init__.py` .
2026-05-27 19:41:16 +02:00
2026-05-28 07:18:21 +02:00
**Migration Alembic `0005_simulation_templates.py` **
- Upgrade : `op.create_table("simulation_templates", ...)` avec tous les champs et la contrainte UNIQUE sur `name` . Pas besoin de batch (CREATE TABLE est natif SQLite).
- Downgrade : `op.drop_table("simulation_templates")` . SQLite OK natif.
- Pas de backfill (table vide à la création).
2026-05-27 19:41:16 +02:00
2026-05-28 07:18:21 +02:00
**Serializer** (`backend/app/serializers.py` )
- Nouvelle fonction `serialize_template(t)` :
feat(frontend): sprint 4 — dark mode + matrix overhaul + tactic selection + done read-only + UI polish
US-17: fix duplicate "Create engagement" button, icon conventions (Save/RotateCcw/Grid2x2), UsersAdminPage form baseline alignment
US-18: done status fully read-only + Reopen button (done → review_required) for all roles
US-19: invalidate engagement queries on simulation PATCH/transition for auto-status propagation
US-20: MitreMatrixModal rewritten — CSS grid 12-column layout, no horizontal scroll, attack.mitre.org compact look
US-21: tactic header clickable in matrix, tactic chips (MitreTacticTag) in field, single atomic PATCH with technique_ids + tactic_ids
US-22: MitreTechniquesField chips-only area + inline search input + matrix icon button; chips show ID-only (name in title=)
US-23: useTheme hook — 3-state light/dark/system, CSS variables, Tailwind darkMode class, localStorage persistence
92/92 tests passing, typecheck and lint clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 20:06:01 +02:00
```python
2026-05-28 07:18:21 +02:00
return {
"id": t.id,
"name": t.name,
"description": t.description,
"commands": t.commands,
"prerequisites": t.prerequisites,
"techniques": _enrich_techniques(t.techniques), # réutilise sprint 3
"tactics": _enrich_tactics(t.tactic_ids), # réutilise sprint 4
"created_at": t.created_at.isoformat() if t.created_at else None,
"updated_at": t.updated_at.isoformat() if t.updated_at else None,
"created_by": serialize_user_brief(t.created_by) if t.created_by else None,
feat(frontend): sprint 4 — dark mode + matrix overhaul + tactic selection + done read-only + UI polish
US-17: fix duplicate "Create engagement" button, icon conventions (Save/RotateCcw/Grid2x2), UsersAdminPage form baseline alignment
US-18: done status fully read-only + Reopen button (done → review_required) for all roles
US-19: invalidate engagement queries on simulation PATCH/transition for auto-status propagation
US-20: MitreMatrixModal rewritten — CSS grid 12-column layout, no horizontal scroll, attack.mitre.org compact look
US-21: tactic header clickable in matrix, tactic chips (MitreTacticTag) in field, single atomic PATCH with technique_ids + tactic_ids
US-22: MitreTechniquesField chips-only area + inline search input + matrix icon button; chips show ID-only (name in title=)
US-23: useTheme hook — 3-state light/dark/system, CSS variables, Tailwind darkMode class, localStorage persistence
92/92 tests passing, typecheck and lint clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 20:06:01 +02:00
}
```
2026-05-28 07:18:21 +02:00
- Pattern parallèle à `serialize_simulation` mais SANS les champs SOC / status / executed_at.
**API** (`backend/app/api/templates.py` — nouveau blueprint)
**Tous les endpoints templates sont gated `@role_required("admin", "redteam")` — SOC reçoit 403 partout.**
- `GET /api/templates` (admin|redteam) → liste serializée, tri `name ASC` .
- `POST /api/templates` (admin|redteam) → création. Validation : `name` non vide (400), `technique_ids` / `tactic_ids` valides (réutilise `_resolve_technique_ids` / `_resolve_tactic_ids` de `simulation_workflow.py` — import direct, KISS). Pour le `name` UNIQUE conflict : **catch `sqlalchemy.exc.IntegrityError` sur INSERT → 409 `{"error": "template name already exists"}` ** . Pas de pre-check SELECT (race condition + code mort, la contrainte UNIQUE en DB est l'autorité).
- `GET /api/templates/<tid>` (admin|redteam) → 200 ou 404.
- `PATCH /api/templates/<tid>` (admin|redteam) → update partial. Pour le `name` conflict : même pattern, **catch IntegrityError sur UPDATE → 409 `{"error": "template name already exists"}` ** . Cas edge : PATCH avec `name == current_name` (no-op rename) → 200 (l'UPDATE sur sa propre row ne viole pas UNIQUE). Mettre à jour `updated_at` .
- `DELETE /api/templates/<tid>` (admin|redteam) → 204. Pas de cascade FK simulations (les simulations n'ont pas de `template_id` FK).
Enregistrer le blueprint dans `backend/app/__init__.py` .
**Modification `POST /api/engagements/<eid>/simulations` ** (`backend/app/api/simulations.py` )
- Le payload accepte maintenant un `template_id` optionnel.
- Si présent :
- Charger le template (404 si non trouvé).
- Créer la simulation en **setant DIRECTEMENT les champs RT ** sur l'objet ORM Simulation à partir des champs du template (`sim.techniques = template.techniques` , `sim.tactic_ids = template.tactic_ids` , `sim.description = template.description` , `sim.commands = template.commands` , `sim.prerequisites = template.prerequisites` ).
- `name` du body override si fourni, sinon `template.name` .
- **NE PAS appeler `apply_patch()` , `_resolve_technique_ids()` , ni `_resolve_tactic_ids()` ** — les données viennent du template déjà persisté+validé, re-résoudre frapperait inutilement le bundle MITRE ET déclencherait l'auto-transition pending→in_progress via la logique auto-trigger de `apply_patch` , ce qui violerait AC-27.4. Le set direct ORM est intentionnellement court-circuité.
- Si absent : comportement actuel inchangé (création vierge avec `name` ).
- Auto-transition NE PAS déclencher (status reste pending — règle sprint 2 "trigger sur PATCH", la création ne compte pas). Le fait de ne pas appeler `apply_patch` est ce qui garantit ça structurellement.
- Engagement auto-status NE PAS déclencher (status engagement reste planned). Idem — `_maybe_activate_engagement` n'est appelé que depuis `apply_patch` .
2026-05-27 04:55:12 +02:00
**Tests pytest**
2026-05-28 07:18:21 +02:00
- `test_simulation_templates_crud.py` (nouveau) :
- GET liste vide, GET liste après création, GET liste tri name ASC.
- POST valide → 201, fields persisted.
- POST name vide → 400.
- POST name dupliqué → 409.
- POST technique_id inconnu → 400.
- POST tactic_id inconnu → 400.
- POST par SOC → 403.
- GET inexistant → 404.
- PATCH valide → 200, updated_at set.
- PATCH name → conflit → 409.
- PATCH par SOC → 403.
- DELETE valide → 204, GET ensuite → 404.
- DELETE par SOC → 403.
- `test_simulations_from_template.py` (nouveau) :
- POST simulation avec template_id valide → copie tous les RT fields, status=pending, executed_at=null, SOC fields=null.
- POST avec template_id valide + name override → name override gagne.
- POST avec template_id inexistant → 404.
- POST avec template_id par SOC → 403 (cohérent avec création).
- Vérifier décorrélation : créer template → instancier → modifier l'instance → assert template inchangé. Symétrique : modifier le template → instance inchangée.
- Auto-transition NE PAS déclenchée (sim reste pending même si template avait des techniques).
- Engagement reste planned (auto-status NOT triggered).
- Migration test : `0005` create/drop round-trip propre (réutilise pattern Alembic round-trip sprint 3/4 avec `Path(__file__)` ).
**Quality bar** : ruff + mypy clean, tous tests existants + nouveaux verts.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-27 19:41:16 +02:00
### Règles
2026-05-28 07:18:21 +02:00
- Pas de touche au frontend, e2e, agents, scripts, Makefile.
- Renvoyer le summary attendu (cf. `.claude/agents/backend-builder.md` ).
2026-05-27 19:41:16 +02:00
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
---
## 3. Brief technique — Frontend Builder
2026-05-28 07:18:21 +02:00
**Scope strict** : `frontend/` uniquement.
2026-05-27 04:55:12 +02:00
2026-05-28 07:18:21 +02:00
**Screenshots MANDATORY (lesson sprint 4)** : à la fin de ton travail, dev server + auth flow (page.goto('/login') + fill creds + submit + wait) pour fournir MIN 6 screenshots :
1. `/admin/templates` liste (admin OU redteam vue, ≥ 2 templates) — light + dark
2. Template create/edit form (mode edit avec techniques + tactic chips) — light + dark
3. EngagementDetail avec dropdown "Blank | From template…" ouvert — light
4. TemplatePickerModal ouverte (au moins 2 templates listés) — light
5. TemplatePickerModal ouverte avec aucun template — empty state visible — light
6. Simulation créée depuis un template (champs pré-remplis avec le nom, MITRE chips) — light
2026-05-27 19:41:16 +02:00
2026-05-28 07:18:21 +02:00
Paths absolus dans le summary. Si auth flow ne marche pas → escalade.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
### Livrables
2026-05-28 07:18:21 +02:00
**Types** (`frontend/src/api/types.ts` )
- `SimulationTemplate` : `{id, name, description, commands, prerequisites, techniques: MitreTechnique[], tactics: MitreTactic[], created_at, updated_at, created_by}` .
- `SimulationTemplateCreateInput` : payload POST.
- `SimulationTemplatePatchInput` : payload PATCH.
- Étendre `SimulationCreateInput` avec `template_id?: number` .
**API client** (`frontend/src/api/templates.ts` — nouveau)
- `listTemplates()` , `getTemplate(id)` , `createTemplate(input)` , `updateTemplate(id, patch)` , `deleteTemplate(id)` .
**Hooks TanStack Query** (`frontend/src/hooks/useTemplates.ts` — nouveau)
- `useTemplates()` , `useTemplate(id)` , mutations `useCreateTemplate` , `useUpdateTemplate` , `useDeleteTemplate` .
- Invalidation : create/update/delete invalide `["templates"]` et `["templates", id]` .
**Pages**
- **`TemplatesListPage.tsx` ** (nouveau, `/admin/templates` ) — admin+redteam only :
- Table : Name, MITRE count (techniques + tactics), Created by, Updated at, Actions.
- Bouton "+ New template" en header.
- Actions par ligne : "Edit" + "Delete".
- Click sur une ligne → `/admin/templates/:id/edit` .
- States : loading / error / empty.
- **`TemplateFormPage.tsx` ** (nouveau, `/admin/templates/new` et `/admin/templates/:id/edit` ) — admin+redteam only :
- Form pour name + description + commands (textarea) + prerequisites + MitreTechniquesField.
- Mode `new` : seul `name` requis ; après création, redirige sur `/admin/templates/:id/edit` .
- Mode `edit` : load existing template, allow update.
- Bouton Delete (confirmation modal).
- Pas de mode read-only (SOC n'a pas accès aux routes).
- **`EngagementDetailPage.tsx` ** (modification) :
- Remplacer le bouton simple "+ New simulation" par un dropdown OU un menu :
- "Blank" (action default)
- "From template…" → ouvre une modale avec la liste des templates.
- Modale "From template…" : `useTemplates()` , table simple Name + MITRE count, click sur un template → `useCreateSimulation` avec `template_id: t.id` → redirect sur la simu créée.
**Composants** (`frontend/src/components/` )
- **`TemplatePickerModal.tsx` ** (nouveau) : modale qui liste les templates, permet de cliquer pour instancier. Props : `engagementId` , `onClose` , `onInstantiated(simId)` .
**Routing** (`App.tsx` ) — toutes routes templates gated `roles=["admin", "redteam"]` :
- `/admin/templates` (admin|redteam)
- `/admin/templates/new` (admin|redteam)
- `/admin/templates/:id/edit` (admin|redteam)
**Layout** (`Layout.tsx` )
- Nouveau lien "Templates" dans la topbar, à droite de "Users" — **visible UNIQUEMENT à admin + redteam ** (masqué pour SOC, pattern identique à "Users" qui est admin-only).
**Tests Vitest**
- `TemplatesListPage.test.tsx` — loading/error/empty + boutons New/Edit/Delete présents (admin|redteam — pas de variante soc puisque route inaccessible).
- `TemplateFormPage.test.tsx` — mode new + mode edit (pas de mode read-only).
- `TemplatePickerModal.test.tsx` — liste templates, click instantiate, gestion erreur, close.
- `EngagementDetailPage.test.tsx` — adapter pour le nouveau dropdown "+ New simulation".
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-26 11:41:23 +02:00
### Règles
2026-05-27 04:55:12 +02:00
- Lit le summary backend EN PREMIER.
2026-05-27 19:41:16 +02:00
- Pas d'invention d'endpoints.
2026-05-28 07:18:21 +02:00
- Réutilise `LoadingState` , `ErrorState` , `EmptyState` , `Toast` , `ConfirmDialog` , `MitreTechniquesField` , `StatusBadge` etc.
- Respect DESIGN.md tokens. Dark mode déjà en place — applique les patterns sprint 4 (`bg-canvas dark:bg-canvas` , etc.).
- Pas de dépendance npm sans escalade.
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
---
2026-05-28 07:18:21 +02:00
## 4. Brief — Test verifier
2026-05-27 04:55:12 +02:00
2026-05-28 07:18:21 +02:00
E2e Playwright. Un fichier par US :
- `us26-templates-crud.spec.ts` — AC-26.1 → AC-26.8 (focus API + UI gérance templates)
- `us27-instantiate-from-template.spec.ts` — AC-27.1 → AC-27.7 (création simu depuis template, décorrélation)
- `us28-templates-nav.spec.ts` — AC-28.1 → AC-28.3 (nav link, accès SOC read-only)
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
Adapter les sprint 2/3/4 e2e si l'ajout du dropdown "+ New simulation" casse des sélecteurs (les tests sprint 2/3 cliquent directement sur "+ New" — désormais ça ouvre un menu avant d'aller au form blanc).
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
---
2026-05-28 07:18:21 +02:00
## 5. Definition of Done — Sprint 5
2026-05-26 11:41:23 +02:00
2026-05-28 07:18:21 +02:00
- [ ] Tous les AC US-26 → US-28 passent.
- [ ] Backend pytest verts (~193 existants + ~25 nouveaux). Ruff + mypy clean.
- [ ] Frontend vitest verts (92 existants + nouveaux). Typecheck + lint clean.
- [ ] E2e Playwright suite verte (sprint 1-4 + sprint 5).
- [ ] Migration 0005 testée via Alembic round-trip.
- [ ] SPEC.md § Templates de simulations ajoutée.
- [ ] README.md mis à jour si nouveau bullet "Templates" pertinent.
- [ ] CHANGELOG.md sprint 5 entry sous [Unreleased].
- [ ] **Design-reviewer pass ** sur les nouveaux écrans (lesson sprint 4 — design-reviewer = part of workflow depuis sprint 4).
- [ ] Code-reviewer sans BLOCKER.
- [ ] PR via `make open-pr` (sprint 4 dogfood validé).
feat(frontend): sprint 4 — dark mode + matrix overhaul + tactic selection + done read-only + UI polish
US-17: fix duplicate "Create engagement" button, icon conventions (Save/RotateCcw/Grid2x2), UsersAdminPage form baseline alignment
US-18: done status fully read-only + Reopen button (done → review_required) for all roles
US-19: invalidate engagement queries on simulation PATCH/transition for auto-status propagation
US-20: MitreMatrixModal rewritten — CSS grid 12-column layout, no horizontal scroll, attack.mitre.org compact look
US-21: tactic header clickable in matrix, tactic chips (MitreTacticTag) in field, single atomic PATCH with technique_ids + tactic_ids
US-22: MitreTechniquesField chips-only area + inline search input + matrix icon button; chips show ID-only (name in title=)
US-23: useTheme hook — 3-state light/dark/system, CSS variables, Tailwind darkMode class, localStorage persistence
92/92 tests passing, typecheck and lint clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 20:06:01 +02:00
2026-05-26 11:41:23 +02:00
---
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
## 6. Décisions arrêtées (utilisateur 2026-05-28)
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-28 07:18:21 +02:00
1. **Table ** : `simulation_templates` séparée (clean schema, pas de colonnes nullable confuses).
2. **Instantiation API ** : extension de `POST /api/engagements/<eid>/simulations` avec `template_id` optionnel.
3. **Name uniqueness ** : UNIQUE (1 template par nom, UX dropdown clean).
4. **Template RBAC ** : admin + redteam writable. **SOC pas d'accès du tout ** — pas de nav link, pas de page, tous endpoints templates → 403. Templates sont une ressource Red Team uniquement.
5. **UI instanciation ** : dropdown sur le bouton "+ New simulation" (Blank | From template…).
2026-05-27 04:55:12 +02:00
---
chore: bootstrap project (sprint 0)
Lay down the project foundation before Sprint 1 implementation:
- SPEC.md enriched with a "Décisions techniques" section that pins
down 3-role auth (admin super-user / redteam / soc), JWT bearer,
single-container Flask+React topology, minimal Engagement model,
local MITRE STIX bundle, and the Makefile target list.
- .claude/agents/ defines the 6 sub-agents per SPEC.md § Team:
backend-builder, frontend-builder, spec-reviewer (project override
covering plan-vs-spec + code-vs-spec), code-reviewer, test-verifier,
devil-advocate.
- tasks/todo.md holds the full Sprint 1 plan (Auth + CRUD Engagement)
validated by spec-reviewer on 2026-05-26 after one round of fixes.
- CHANGELOG.md and tasks/lessons.md scaffolded.
- .gitignore covers Python, Node, Playwright, secrets, build artifacts
and Claude Code worktrees.
No application code is shipped in this commit — Sprint 1 will be a
separate branch and PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:01:13 +02:00
2026-05-27 04:55:12 +02:00
## 7. Plan d'exécution
2026-05-28 07:18:21 +02:00
1. ✅ User a validé les 5 décisions §6 (2026-05-28). SOC zero access acté.
2. 🟡 Team-lead met à jour SPEC.md (§0).
3. 🟡 Spec-reviewer valide le plan (2-pass — lesson sprint 3/4 — RBAC SOC blocked, name unique conflict 409 handling, template_id passing through auto-transition, design-reviewer scope new pages).
4. 🔵 Backend-builder : modèle + migration 0005 + endpoints + tests.
5. 🔵 Frontend-builder : pages Templates list/form + TemplatePickerModal + nav link + dropdown engagement + tests Vitest. Screenshots mandatory.
6. 🔵 Design-reviewer : revoit diff frontend + screenshots.
7. 🔵 Code-reviewer : LSP-first review du diff complet.
8. 🔵 Test-verifier : e2e US-26 → US-28.
9. 🟢 Team-lead : `make open-pr` + récap.
Branche : `sprint/5-templates` .