Files
mimic-big/tasks/todo.md
knacky df6294ed7b docs: align doc references with compose.yml rename (code-reviewer M1)
Three docs still referenced the old docker-compose.yml path. Replace
with compose.yml so a future reader cloning at this hash finds the
file at the documented path.

- CHANGELOG.md:31 — backend skeleton recap line.
- docs/architecture.md:28 — deployment artifacts note (D-010 scope).
- tasks/todo.md:9 — B0.1 task description.

Also adds a "CI follow-ups (sprint 1+)" section to tasks/todo.md
capturing the 3 MINOR + 6 NIT deferred from code-reviewer's review
of chore/podman-and-ci, plus a FERNET-KEY tracker for the secret
provisioning before c2_credential.config_fernet (D-004) is wired.
2026-05-22 19:49:16 +02:00

8.8 KiB

Sprint 0 — Mimic

Repo skeleton + foundational modules. Nothing that depends on PR1/PR2/PR3.

Backend (backend) — done in feature/backend-skeleton

  • B0.1 — backend/ Python 3.12+ project: pyproject.toml (ruff, mypy strict, pytest, coverage 70 %), Makefile (Docker/Podman auto), multi-stage Dockerfile, compose.yml for Postgres dev DB, .env.example.
  • B0.2 — Alembic baseline migration 202605210001_initial_schema creates every table, enum, index, and the idempotent grants for the audit write-only Postgres role. No ttp_version table (D-009). Groups rt_operator, rt_lead, soc_analyst seeded with the exact F11 permission matrix (D-008).
  • B0.3 — SQLAlchemy 2 typed mapped classes for every spec §8 aggregate (engagement, host, user/group RBAC, ttp, scenario/scenario_step, run/run_step/cleanup, detection, evidence, report, soc_session, c2_credential, audit_log).
  • B0.4 — C2Connector ABC + dataclasses + payload_type enum + factory keyed on c2_type. Mythic payload map populated; Home stays empty until PR2.
  • B0.5 — Jinja2 SandboxedEnvironment, regex_extract filter (google-re2 hard dependency per D-011, raises RuntimeError at boot if absent — no re fallback), fail-loud no-match, {{ outputs.text }} / {{ outputs.blob() }} accessors reading gzip-compressed blobs with 10 MB cap.
  • B0.6 — bcrypt password helpers + SOC opaque token (256-bit url-safe, bcrypt-hashed) + group-based RBAC matrix matching F11 + @require_perm decorator.
  • B0.7 — Flat CRUD blueprints for engagements / hosts / TTPs / scenarios (incl. step composition with F3 invariant host.c2_type == scenario.c2_type).
  • B0.8 — pytest baseline: unit tests passing, integration scaffold ready (testcontainers Postgres + /healthz smoke).

Backend follow-ups (sprint 1+)

Tracked from code-review verdict on feature/backend-skeleton @ 12d131c:

MINOR (8) — to schedule

  • M1 — Replace parse_uuid integer-ish lookup with werkzeug UUID converter on the routes (<uuid:eid>) to avoid the 404 on malformed strings being hidden by the 400 path.
  • M2 — Add OpenAPI generation (Pydantic 2 + flask-pydantic-openapi or hand-rolled).
  • M3 — Wire flask-limiter for /auth/local/login (NF-network).
  • M4 — Replace string-based Engagement.status setter with a typed transition method.
  • M5 — Introduce per-engagement read view that pre-joins engagement_member for RT operator dashboards (current per-request join is fine for v1 traffic, but re-evaluate at scale).
  • M6mimic-cli user create does not handle the SOC user-type (intended, but document and gate explicitly with a clean error message).
  • M7 — Add a mimic-cli engagement add-member <uid> --role rt_operator shortcut so the F11 scoping in MA6 is reachable from the CLI without manual SQL.
  • M8(fixed in MA1 follow-up commit) Initial migration docstring no longer references ttp_version.

NIT (6) — opportunistic

  • N1 — Sort imports inside mimic.db.models.__init__ alphabetically for diff stability.
  • N2 — Extract the _engagement_or_404 duplicated body into a shared helper.
  • N3 — Replace the inline Permission.TTP_PROMOTE not in perms check in ttps.py with a second @require_perm-style decorator.
  • N4(fixed) gunicorn added to pyproject.toml dependencies.
  • N5 — Replace bare getattr(current_user, "groups", frozenset()) accesses by a thin current_groups() helper.
  • N6tests/integration/conftest.py uses db.create_all() instead of running Alembic. Marked with a TODO; switch over once the F11 seed must be exercised in integration. Plan: convert to alembic upgrade head once the audit role bootstrap lives in the playbook (D-010).

Frontend (ux-frontend)

  • F0.1 — frontend/ Vite + React + TypeScript strict + Tailwind 4 + TanStack Query 5, eslint strict + prettier, Playwright skeleton.
  • F0.2 — Design system provisional: semantic tokens in theme.css (status colors, RT accent, data mono / UI sans), dark-first palette, placeholder logo.
  • F0.3 — Wireframes (via frontend-design skill) on mock data: Login + engagement selection, Live cockpit, Scenario composer, Report + MITRE matrix, TTP library + import.
  • F0.4 — Routing skeleton + role-aware layout shell (no real auth wired yet).
  • F0.5 — Push feature/frontend-skeleton, open PR for code-reviewer.
  • F0.6 — Polish M1-M3 from code-review (single SessionProvider, typo, fallback removal).

Frontend follow-ups (sprint 1+, non-blocking, from review NITs)

  • N1 — Tighten readMockSession payload validation when real auth wires up (currently checks only role; should validate the full SessionUser shape).
  • N2 — Replace the UI-side recomposition of the MIMIC-RUN: marker in the cockpit's "Resolved command" panel with resolvedCommandText returned by the backend (run_step_cleanup.resolved_command_text for cleanup, equivalent field for steps when exposed).
  • N3 — Wire StatusRail.linkState to the real WebSocket connection state once Flask-SocketIO is reachable (currently hardcoded 'up').
  • N4 — Unify router.tsx and any future router helpers under src/routing/ (single naming, no split between root file and folder). Addressed in F0.6: src/routing/Root.tsx introduced; router.tsx left at top level as the app-level entry that other code imports — split kept minimal.
  • N5 — Actually import Recharts somewhere (likely the MITRE matrix or a latency chart) since it's declared in README + package.json but not yet used.
  • N6 — When vendoring IBM Plex woff2 into public/fonts/, add public/fonts/LICENSE.txt (OFL-1.1) for license compliance.
  • N7 — Add frontend/.env.example exposing VITE_API_BASE_URL and VITE_WS_URL once the backend publishes endpoints.

Spec / Docs (spec-analyst)

  • S0.1 — Cross-check the data model in B0.2 against §8 of the spec; report deltas before merge.
  • S0.2 — Cross-check the RBAC matrix in B0.6 against F11; report deltas before merge.
  • S0.3 — Maintain tasks/spec-decisions.md as new arbitrations land.
  • S0.4 — Open docs/architecture.md once backend layout is committed.

Review (code-reviewer)

  • R0.1 — Review each PR per the published charter; block on security/OPSEC violations.
  • R0.2 — Verify mypy strict and ruff clean before approving any backend PR.
  • R0.3 — Verify TS strict, no useEffect(fetch), exhaustive deps before approving any frontend PR.

CI follow-ups (sprint 1+) (devops)

Raised by code-reviewer during review of chore/podman-and-ci (M2-M3 + N1-N6). None blocking, all deferred to sprint 1+.

  • M2 — backend/Makefile $(COMPOSE) detection: invert legacy docker-compose v1 probe, prefer the Compose v2 plugin ($(CONTAINER) compose) first.
  • M3 — .gitea/workflows/ci.yml backend job: chain apt-get update && apt-get install in one RUN-style step and drop rm -rf /var/lib/apt/lists/* (no-op in an ephemeral CI container).
  • N1 — Smoke workflow cat /etc/os-release | head -3 → use head -3 /etc/os-release (moot once smoke.yml is removed; track here in case smoke is reintroduced).
  • N2 — .gitea/workflows/ci.yml pull_request: trigger: restrict to branches: [main] to avoid double-running on PR retargets.
  • N3 — Anticipate single-runner serialization: jobs will queue. Plan a second runner (different host or capacity: >1) before scaling sprint 2+ workload.
  • N4 — Add top-level concurrency: { group: ${{ github.ref }}, cancel-in-progress: true } to cancel superseded PR runs.
  • N5 — CI uses MIMIC_DATABASE_AUDIT_URL == MIMIC_DATABASE_URL (same role). Acceptable for unit tests; integration tests covering the audit write-only role must provision a separate mimic_audit_writer role in the Postgres service before they can run.
  • N6 — Cache pip + npm via actions/cache@v4 (verify Gitea Actions fork support before adoption; fallback to manual cache volume on the runner if unsupported).
  • FERNET-KEY — Provision FERNET_KEY_TEST Gitea repo secret before sprint 1 wires c2_credential.config_fernet (D-004). config.py:32 accepts an empty default at boot but Fernet(b"") raises ValueError at first use.

Conventions

  • Branches: feature/<scope>, fix/<scope>, docs/<scope>, chore/<scope>. Long-lived: main.
  • Commits: Conventional Commits (feat:, fix:, chore:, docs:, test:, refactor:).
  • PRs: each branch → review (code-reviewer) → team-lead merges.
  • No direct push to main.