Commit Graph

4 Commits

Author SHA1 Message Date
Knacky
8742fb2b6e refactor(m4): match attack.mitre.org sizing — equal-width cols, name-only cells
Visual parity pass against attack.mitre.org/# per user feedback ("trop dense,
illisible, je veux la même représentation"):

- Layout switched from flex+fixed-width 224px columns to a CSS grid of
  `repeat(N, minmax(0, 1fr))` so the 15 tactic columns share the container
  width equally. No more horizontal scroll on a standard desktop.
- Cells now show NAME ONLY (matches mitre.org). The external_id (TA00xx /
  T1xxx / T1xxx.xxx) is preserved in the chip selection bar at the top and
  in the `title` hover tooltip on every cell — surfaces on demand, doesn't
  consume cell real estate.
- Font: switched to `font-sans` (IBM Plex Sans) at `text-xs` (12px) across
  cells, matching the mitre.org typography. Headers use the same family at
  the same size with a 10px sub-line for the technique count.
- Chevron icons: ▸ (collapsed) / ▾ (expanded) — small, sub-technique count
  rendered inline beside the chevron.
- Helper line below the matrix tells the user where the IDs went.

Spec §F2 + testing-m4.md walkthrough rewritten to lock the new sizing rules
in (font-xs, no external_id in cells, hover/chip for the ID, no horizontal
scroll). spec-reviewer will see the matching contract.

DoD: make e2e → 34 passed. Selectors (data-testid + aria-pressed) unchanged
so the existing M4 e2e test still walks the new layout end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:41:11 +02:00
Knacky
7dbe2dbc28 refactor(m4): flatten the MITRE picker into the attack.mitre.org matrix
The hierarchical 3-column drill-down was hard to scan and forced a stateful
walk per tag. Replaced with a flat, columns-as-tactics matrix that mirrors
attack.mitre.org/# — every cell is a one-click select target, with inline
sub-technique expand via a `+N` chevron.

- New endpoint GET /api/v1/mitre/matrix returns the full grid (tactics →
  techniques → sub-techniques nested) in a single ~55 KB response, so the
  SPA renders the whole matrix without firing 15 parallel queries. Two
  pytest tests added (nested structure + auth required).
- MitreTagPicker.tsx rewritten as a horizontal-scrolling matrix:
  - Click a tactic header → select the tactic (cyan filled).
  - Click a technique cell → select the technique (orange filled).
  - Click the `+N` chevron → expand sub-techniques inline within the column.
  - Click a sub-technique → select (purple filled).
  - Single Filter field matches on external_id or name across all kinds.
  - Selection chips at the top, clickable to remove.
  - `aria-pressed` on every clickable cell for screen readers and Playwright.
- e2e test updated to walk the new flow (click cell → assert aria-pressed,
  expand chevron, click sub, verify chip + JSON preview, filter to T1078).
- Spec §F2 + §F12 + todo.md M4 entry updated to make the matrix layout the
  canonical UI for MITRE tagging (so future spec-reviewer passes accept it).
- testing-m4.md walkthrough rewritten for the flat picker.

DoD post-refactor: make test-api → 53 passed (was 51), make e2e → 34 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:32:20 +02:00
Knacky
37e9e03f02 docs(m4): CHANGELOG, README, lessons, spec drift fix, todo tick
- CHANGELOG: added M4 section listing endpoints, CLI, volume, persisted
  settings, picker, and the post-spec-review fixes (custom-URL integrity
  requirement + /diag/reset consistency + spec drift). Includes the
  intentional decisions paragraph (seed-time download not image-baked, read
  endpoints unauthenticated-perm-wise, stdlib over httpx).
- README: status bumped to M0–M4, added MITRE quickstart (make seed-mitre +
  air-gapped path with --source /data/mitre/<file> + --skip-checksum),
  testing-m<N>.md pointer updated to testing-m4.md, roadmap line.
- tasks/spec.md §10 #4: amended "14 tactics Enterprise" → "≥14 tactics
  Enterprise (la v19 du pin actuel en ship 15)".
- tasks/lessons.md: 7 M4 lessons captured (stdlib STIX parsing, decoupling
  DoD asserts from upstream versions, subtechnique parent resolution, single-
  transaction safety, custom-URL footgun mitigation, /diag/reset consistency,
  named-volume permission caveat, podman build cache surprise).
- tasks/todo.md: M4 marked ☑.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:54:46 +02:00
Knacky
f1fdf27012 feat(m0): bootstrap repo, design system, compose stack
- Repo scaffolding: .gitignore, .env.example, Makefile, docker-compose.yml,
  README.md, CHANGELOG.md, pre-commit config.
- Three-service stack: api (Flask 3), db (postgres:16-alpine), front (nginx
  serving the Vite bundle). Named volumes metamorph_db + metamorph_evidence.
- Backend skeleton: Flask app factory, JSON structured logging on stdout,
  GET /api/v1/health, multi-stage Dockerfile, pyproject.toml driven by uv,
  Pydantic Settings with secret guard rails (refuses to boot in non-dev with
  placeholders), APP_ENV gating.
- Frontend skeleton: Vite + React 18 + TypeScript strict + TailwindCSS, RTOps
  design tokens from tasks/design.md, self-hosted JetBrains Mono / IBM Plex
  Sans via @fontsource, base UI primitives (Card/Tag/SectionHeader/FlowNode/
  Button), home page wired to /api/v1/health.
- Engine-agnostic Makefile: auto-detects docker or podman, picks the matching
  compose driver. Targets: up/down/build/rebuild/dev/lint/fmt/test/migrate/
  seed-mitre/print-install-token/e2e/inspect-health.
- Playwright suite: e2e/tests/m0-smoke.spec.ts (8 tests) + HTML + JUnit
  reports + traces on retry.
- Docs: tasks/spec.md (finalized after Q&A), tasks/design.md, tasks/todo.md
  (14 milestones), tasks/testing-m0.md, tasks/lessons.md.

DoD: make up + make health + make e2e all pass on podman 5.x (Fedora) and
docker. TLS terminated by external reverse proxy (spec §6 NF-network).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 06:16:00 +02:00