- 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>
130 lines
14 KiB
Markdown
130 lines
14 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to Mimic are tracked here.
|
||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/).
|
||
|
||
## [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.
|
||
- `SimulationFormPage` — `MitreTechniquesField` 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-alpine` → `python: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`.
|