Files
mimic/CHANGELOG.md
Knacky b001f57774 docs: sprint 3 wrap-up — README + CHANGELOG + lessons + plan final
- README: status bump to sprint 3, test counts refreshed (164/86/105), IPv6 note for the e2e runner
- CHANGELOG: sprint 3 entry under [Unreleased] (multi-tech model + matrix endpoint + auto-save UI); sprint 2 moved to its own [Sprint 2] section (merged 2026-05-27)
- tasks/lessons.md: 6 lessons captured (2-pass spec-review, inline summary scoping, "test in brief means test in commit" discipline, SQLite batch_alter_table, real migration round-trip, modal Apply 0 disambiguation)
- tasks/todo.md: status flipped to 🟢 SPRINT COMPLET, execution sequence ticks updated with commit hashes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 04:55:12 +02:00

14 KiB
Raw Blame History

Changelog

All notable changes to Mimic are tracked here.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

[Unreleased]

Added — Sprint 3 (Multi-technique simulations + MITRE matrix modal)

Backend (164 pytest passing)

  • Simulation.techniques JSON column replaces the scalar mitre_technique_id / mitre_technique_name pair. Stored as [{"id", "name"}]; tactics are derived at serialize time from the MITRE service (snapshot pattern survives bundle updates).
  • Alembic migration 0003_simulation_techniques_array.py — reversible upgrade (backfill from scalars → drop scalars → enforce NOT NULL via batch_alter_table) and symmetric downgrade.
  • PATCH /api/simulations/<sid> now accepts {technique_ids: ["T1059", "T1059.001", ...]} (flat list of T-IDs, parents and subs at the same level). Server validates each ID against the bundle (400 on unknown), deduplicates while preserving order, resolves names, and rejects SOC payloads (403). Returns 503 if the bundle isn't loaded.
  • GET /api/mitre/matrix — new endpoint returning the full Enterprise tree [{tactic_id, tactic_name, techniques: [{id, name, subtechniques: [{id, name}]}]}]. Tactics in canonical order (Initial Access → Impact). Techniques sorted alphabetically per tactic; sub-techniques nested under their parent via dot-ID detection.
  • mitre_svc extended with get_tactics(id), lookup_name(id), get_matrix(), and a TACTIC_NAMES constant fixing the cosmetic "Command And Control""Command and Control" (MITRE canonical capitalisation).
  • REDTEAM_FIELDS | {"technique_ids"} SOC gate in simulation_workflow.apply_patch preserves the sprint 2 field-level RBAC pattern.
  • Auto-transition pending → in_progress extended: triggers when technique_ids is non-empty (consistent with the "non-empty value" rule from sprint 2). Empty list does not trigger.

Frontend (86 vitest passing)

  • MitreTechniquesField orchestrates multi-technique selection with auto-save — every add (Quick Search / matrix Apply) and every remove (× on tag chip) triggers a PATCH via useUpdateSimulation. Toast feedback on success/error; UI disabled during the in-flight PATCH; silent dedup if the user re-adds an already-present technique.
  • MitreTechniqueTag — chip component (bg-primary-soft text-primary-deep rounded-full) with an × remove button.
  • MitreMatrixModal — full-width modal, one column per tactic (220px fixed), horizontal scroll. Each technique top-level is clickable (toggle); a chevron expands/collapses sub-techniques rendered in cascade. Search filter (case-insensitive on id + name) auto-expands the parent of a matched sub-technique. Tactic header shows a "N selected" counter (parents + subs). Footer: Cancel + "Apply N technique(s)" (or "Clear all" when N=0 and there's an existing selection). Focus trap V1: search input auto-focus on open, Tab cycles within the modal, Escape and backdrop click both = Cancel.
  • MitreTechniquePicker (sprint 2) clean-rewritten to a one-shot onSelect({id, name}) signature; no incoming value props. The picker resets after each selection — the parent (MitreTechniquesField) handles append + dedup.
  • SimulationList MITRE column displays T1059 +2 when 3 techniques are selected (first id + remainder counter) or when empty.
  • SimulationFormPageMitreTechniquesField replaces the old standalone MitreTechniquePicker. The technique state moves out of the RT form (independent auto-save cycle); the Save Red Team button still batches the other RT fields.

Acceptance tests (Playwright)

  • 4 new spec files: us13-multi-techniques.spec.ts, us14-techniques-tags.spec.ts, us15-mitre-matrix-modal.spec.ts, us16-regression-sprint2.spec.ts — all ACs (AC-13.1 → AC-16.3) pass.
  • Sprint 2 specs us8-simulation-redteam-fill.spec.ts and us10-mitre-autocomplete.spec.ts adapted to the new techniques: [] array (no more scalar field assertions).

Changed

  • 2026-05-27 — SPEC.md § Simulation: "Type d'attaque MITRE correspondant" (singular) → "Types d'attaque MITRE correspondants (multi-techniques) — sélectionnables par autocomplete OU via la matrice ATT&CK affichée en modale. Sub-techniques supportées."
  • 2026-05-27 — Breaking API change: mitre_technique_id and mitre_technique_name removed from the Simulation payload (both directions). Replaced by techniques: [{id, name, tactics}] in responses and technique_ids: string[] in PATCH requests. No backwards-compatibility shim (no external consumer at this stage).

[Sprint 2] — Simulations + MITRE ATT&CK (merged 2026-05-27)

Added — Sprint 2 (Simulations + MITRE ATT&CK)

Backend (Flask + SQLAlchemy, 131 pytest passing)

  • Simulation model with redteam-side (name, mitre_technique_id, mitre_technique_name, description, commands, prerequisites, executed_at, execution_result) and SOC-side (log_source, logs, soc_comment, incident_number) fields, plus status enum (pending / in_progress / review_required / done), FK to Engagement (cascade delete) and User (creator).
  • Alembic migration 0002_add_simulations.py.
  • 7 new endpoints: GET/POST /api/engagements/<eid>/simulations, GET/PATCH/DELETE /api/simulations/<sid>, POST /api/simulations/<sid>/transition, GET /api/mitre/techniques?q=.
  • simulation_workflow service: field-level RBAC (SOC blocked when status ∈ {pending, in_progress}; SOC rejected if payload contains a redteam field), state machine (only forward transitions, validated by role), and auto-transition pending → in_progress when admin/redteam saves any non-empty redteam field.
  • mitre service: STIX 2.1 Enterprise bundle loaded at boot, indexed by T-id + name + tactic. Ranked search (exact-id > prefix-id > substring-name), max 20 results. Includes sub-techniques (T1059.001). Boot-safe: missing/corrupt bundle logs a warning and the endpoint returns 503 instead of crashing the app.
  • make update-mitre is now a real target — fetches the upstream STIX bundle and restarts the container if running. Bundle is committed at backend/data/mitre/enterprise-attack.json (~46 MB) so make build stays self-contained.
  • Upfront validation of executed_at (no partial mutation on parse failure).

Frontend (React + TanStack Query, 63 vitest passing)

  • SimulationList component rendered inside EngagementDetailPage (replaces the Sprint 1 placeholder). Columns: name, MITRE id, status badge, executed_at. Row click → SPA navigation via useNavigate (no full reload).
  • SimulationFormPage (/engagements/:eid/simulations/new and /engagements/:eid/simulations/:sid/edit): single role-aware page with two cards ("Red Team" / "SOC"). Redteam/admin can edit all fields; SOC sees the redteam card as read-only and the SOC card disabled (with an explanatory banner) until status reaches review_required. Footer surfaces context-appropriate transition buttons ("Marquer en revue" / "Clôturer") and a confirmation modal for delete.
  • MitreTechniquePicker: debounced (200 ms) autocomplete input with keyboard navigation (↑↓ / Enter / Escape), listbox accessibility, and an inline 503 error path. Selection populates both mitre_technique_id and mitre_technique_name. A hasHydratedFromProps ref prevents the input from being wiped mid-stroke when the parent emits onChange(null, null).
  • SimulationStatusBadge: 4 variants mapped to DESIGN.md tokens (bg-fog, bg-primary-soft, bg-bloom-coral, bg-storm-deep). Sibling of the existing StatusBadge rather than a forked generic — the two badges share visual scaffolding but their enums diverge.
  • ConfirmDialog: generic modal used by the delete flow.
  • TanStack Query hooks: useEngagementSimulations, useSimulation, useCreateSimulation, useUpdateSimulation, useDeleteSimulation, useTransitionSimulation, useMitreSearch. Mutations invalidate both the simulation detail key and the engagement-scoped list key.

Acceptance tests (Playwright, 68/68 passing)

  • 6 new spec files (one per user story US-7 → US-12), 32 tests, all green.
  • us4-engagements.spec.ts AC-4.9 assertion refreshed: the Sprint 1 placeholder text was correctly replaced by the new SimulationList (the test now asserts the new heading + "New simulation" link).
  • Sprint 1 docker-hardcoded tests (us1, us6) now resolve thanks to the podman auto-detect added to those specs in the same sprint — full suite is green on both docker and podman hosts.
  • E2e assertions translated to match the i18n cleanup (French → English) shipped in the post-QA fix.

Post-QA fixes (2026-05-26)

  • All French labels in the frontend translated to English (convention: anglais partout). Affected: SimulationList, SimulationFormPage, ConfirmDialog strings.
  • UsersAdminPage "Create account" form: grid alignment fixed — the password field's hint="≥ 8 characters" was pushing labels out of alignment with items-end. Now uses items-start + self-end button wrapper so labels sit at the same baseline and the Create button stays bottom-aligned.
  • SimulationFormPage "Execution result" field: switched from single-line TextInput to multiline TextArea (5 rows).
  • SimulationFormPage actions reorganised: single sticky action bar at the bottom of the page replaces the previous split between RT-card footer, SOC-card footer, and workflow div. Layout: Save Red Team · Save SOC · | · Mark for review · Close · (right-aligned) Delete.

Changed

  • 2026-05-26 — make update-mitre upgraded from no-op placeholder to a real curl + optional container restart (Sprint 1 marker resolved).
  • 2026-05-26 — EngagementDetailPage no longer renders the "Simulations à venir au Sprint 2" placeholder; it embeds <SimulationList> instead.
  • 2026-05-26 — Makefile now auto-detects the container engine (CONTAINER_CMD ?= docker || podman) instead of hard-coding docker. Override with make <target> CONTAINER_CMD=podman or export CONTAINER_CMD=…. The matching e2e tests (us1, us6) were updated to mirror the same detection so they pass on podman-only machines without an explicit MIMIC_CONTAINER_CMD export.

[Sprint 1] — Auth + CRUD Engagement (merged 2026-05-26)

Added

Backend (Flask + SQLAlchemy + SQLite, 63 pytest passing)

  • User model with admin / redteam / soc enum, argon2 password hashing.
  • Engagement model with planned / active / closed status, FK to creator user.
  • JWT Bearer auth (PyJWT, HS256, 60-min TTL), @login_required and @role_required(*roles) decorators.
  • 13 API endpoints: /api/auth/{login,logout,me}, /api/users CRUD (admin-only with last-admin protection), /api/engagements CRUD (RBAC per role), /api/health.
  • Alembic migration applied at container boot by docker/entrypoint.sh.
  • flask create-admin CLI with duplicate-username and short-password validation.
  • Engagement serializer returns created_by={id, username} (not bare User object).
  • SPA fallback returns JSON 404 for unknown /api/* paths (no HTML leakage).

Frontend (React + Vite + TailwindCSS + TanStack Query, 20 vitest passing)

  • Inter font bundled locally via @fontsource-variable/inter (no CDN at runtime).
  • Tailwind config maps the DESIGN.md token system (palette, typography, spacing, radii).
  • Pages: LoginPage, EngagementsListPage, EngagementFormPage (new+edit), EngagementDetailPage (Sprint 2 placeholder), UsersAdminPage.
  • Components: Layout, ProtectedRoute (auth + role gate), StatusBadge, FormField, LoadingState/ErrorState/EmptyState, Toast + provider.
  • Axios client with Bearer interceptor; 401 → token purge + redirect /login + "Session expirée" toast (AC-2.6); 403 → "Accès refusé" toast (AC-3.7).
  • TanStack Query hooks: useAuth, useEngagements, useUsers, useToast.

Deployment

  • Single-container docker/Dockerfile (multistage: node:20-alpinepython:3.12-slim).
  • docker/entrypoint.sh running flask db upgrade && flask run.
  • Makefile with build, start, stop, restart, update, logs, create-admin, update-mitre (no-op placeholder for Sprint 2), test-backend, test-frontend, test-e2e, clean.
  • .env.example documenting MIMIC_JWT_SECRET, MIMIC_DB_PATH, MIMIC_PORT.
  • SQLite persisted at /data/mimic.sqlite, volume mimic-data survives make restart.

Acceptance tests (Playwright, 36 specs, all 27 ACs covered)

  • e2e/ scaffold: playwright.config.ts, fixtures/{auth,api}.ts, 6 spec files (one per user story).
  • Suite is portable via MIMIC_CONTAINER_CMD / MIMIC_BASE_URL env vars (works with docker or podman).

Docs

  • README.md with quick-start, architecture overview, project layout, make target reference, and dev workflow.
  • pyrightconfig.json at repo root pointing the Python LSP to backend/.venv and adding the worktree root to extraPaths for absolute imports.

Changed

  • 2026-05-26 — admin role widened in SPEC.md § Décisions techniques. The initial draft restricted admin to user-management only; after the Sprint 1 plan review surfaced the operational pain (admin would need a second redteam account just to manage engagements), the user decided to make admin a super-user that cumulates redteam rights on engagements/simulations.

Removed

  • none

[Sprint 0] — Bootstrap (merged 2026-05-26)

Added

  • Initial SPEC.md covering project scope, simulation model, workflow, stack, and agent team.
  • Technical decisions section in SPEC.md: 3-role auth (admin/redteam/soc), JWT Bearer, single-container Flask+React, local MITRE STIX bundle, minimal Engagement model, admin bootstrap via Makefile target.
  • Sub-agent definitions under .claude/agents/ for backend-builder, frontend-builder, spec-reviewer (project override of the built-in, covers plan-vs-spec and code-vs-spec), code-reviewer, test-verifier, devil-advocate.
  • Project tracking scaffold: tasks/todo.md, tasks/lessons.md, CHANGELOG.md, .gitignore.