Post-review user decision (2026-06-08) switched the export payload to a fixed 7-column FR handoff schema (Scénario / Test / Source de log / Commentaires SOC / Exécution / Logs remontés au SIEM / Cyber incident). Logged in CHANGELOG [Unreleased] Changed section with commit refs (SPECfdab324, backend7335b9f, e2eaeb4bdb) and updated PR #9 body counters: 255 pytest (was 253), 136 vitest unchanged, 223 e2e unchanged.
37 lines
4.0 KiB
Markdown
37 lines
4.0 KiB
Markdown
## Summary
|
|
- **Engagement export** : `GET /api/engagements/<id>/export?format=md|csv|pdf` — clôt la boucle « remplace l'Excel partagé RT ↔ SOC » du SPEC.
|
|
- **3 formats livrés** : Markdown (table GFM 7 colonnes), CSV (7 colonnes machine-readable, défense formula-injection), PDF (table HTML→PDF via WeasyPrint).
|
|
- **Schéma fixe 7 colonnes FR** uniforme MD/CSV/PDF (décision post-review, pre-merge) : `Scénario`, `Test`, `Source de log`, `Commentaires SOC`, `Exécution` (multi-ligne sans labels — `executed_at` → `commands` → `execution_result`), `Logs remontés au SIEM`, `Cyber incident`. Champs retirés intentionnellement : status, MITRE techniques/tactics, prerequisites, id, created_at, updated_at.
|
|
- **UI** : split-button dropdown `[Export ▼]` sur `EngagementDetailPage`, 3 items. Les **deux moitiés ouvrent le menu** (différence sémantique vs sprint 5 où la gauche naviguait blank — il n'y a pas de format "défaut" évident).
|
|
- **RBAC SOC zero access** : admin + redteam exportent ; SOC ne voit pas le bouton (DOM-absent) et tous endpoints `/api/engagements/<id>/export*` → 403.
|
|
- **Security MEDIUM fix mid-sprint** : CSV formula injection défusée par `_csv_safe()` (apostrophe-prefix sur `=`/`+`/`-`/`@`/`\t`/`\r`). Le red team aurait pu injecter une formule qui s'exécute chez le SOC à l'ouverture de l'Excel.
|
|
|
|
## Test plan
|
|
- **Backend** : **255/255** pytest (`ruff` + `mypy` clean).
|
|
- **Frontend** : **136/136** vitest (`typecheck` + `lint` clean).
|
|
- **E2e Playwright** : **223/223** verts — baseline sprint 5 = 201, +22 sprint 6.
|
|
|
|
## Comment tester en local
|
|
```bash
|
|
make build && make start # auto-podman, +50 MB d'image (deps WeasyPrint)
|
|
make create-admin USER=alice PASS=changeme8 # si premier setup
|
|
# Ouvrir http://127.0.0.1:5000 (IPv4 explicite si IPv6 par défaut)
|
|
```
|
|
|
|
Scénarios :
|
|
1. **Export Markdown** — login admin → engagement avec ≥ 2 simulations → header → `[Export ▼]` → Markdown. Le `.md` téléchargé contient le nom de l'engagement, ses dates, et le détail de chaque simulation RT + SOC.
|
|
2. **Export CSV** — même flow → CSV. Ouvre dans LibreOffice : 1 ligne header + N lignes simulations, commands multilines correctement échappés, colonnes RT et SOC visibles.
|
|
3. **Export PDF** — même flow → PDF. Le fichier doit s'ouvrir dans un viewer PDF avec un rendu propre (titres, sections, tables).
|
|
4. **CSV formula injection (sécurité)** — crée une simulation avec `name = "=cmd|'/c calc'!A1"`, exporte le CSV, ouvre dans Excel/LibreOffice. La cellule doit afficher le texte littéral `=cmd|'/c calc'!A1` (apostrophe forcé), pas exécuter la formule.
|
|
5. **SOC zero access** — login en SOC → engagement → bouton `Export` ABSENT du header. Test API direct : `curl -H "Authorization: Bearer <SOC_TOKEN>" http://127.0.0.1:5000/api/engagements/1/export?format=md` → `403`.
|
|
6. **Engagement vide** — engagement avec 0 simulations → export OK (header seul ; CSV = 1 ligne header).
|
|
7. **Filename normalisé** — engagement nommé `"Opération Spéciale"` → filename Content-Disposition = `engagement-<id>-operation-speciale-YYYYMMDD.<ext>` (NFKD strip des accents).
|
|
|
|
## Notes
|
|
- **Endpoint unique** avec query param `format`, pas 3 routes séparées — 1 RBAC à protéger, 1 test d'intégration RBAC.
|
|
- **PDF pipeline** : WeasyPrint (Python HTML→PDF). Le PDF est généré depuis les MÊMES DONNÉES que le Markdown (pas depuis le string Markdown) via `_render_engagement_html()`. CSS inline ≤ 30 lignes.
|
|
- **Dockerfile** : +6 libs minimales pour WeasyPrint (`libcairo2 libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libfontconfig1 shared-mime-info`). `libgdk-pixbuf-2.0-0` exclu (text-only PDF, vérifié `weasyprint --info`).
|
|
- **Process wins sprint 6** : SPEC.md committed en commit #1 du sprint (recurrence 4 sprints enfin tuée) ; spec-reviewer 2-pass APPROVED avant dispatch backend (0 addendum mid-implementation, comme sprint 5) ; team `mimic` persistante avec les 7 agents idle (cohérence cross-sprint à partir du sprint 7+).
|
|
|
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|