Files
mimic/tasks/pr-body-sprint-6.md
Knacky 2d1c113f0c docs: log 2 MEDIUM security fixes in CHANGELOG (post-refactor)
CSV multiline injection + Markdown stored-XSS regressions caught by
security-guidance@claude-code-plugins on the 7-column refactor.
Backend fix in 3a9d9d3 (257 pytest, ruff/mypy clean). PR #9 body
counter bumped 255 → 257.
2026-06-08 19:29:59 +02:00

4.0 KiB

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_atcommandsexecution_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 : 257/257 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

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=md403.
  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