Files
mimic-big/tasks/todo.md
knacky c44f8b90ad
Some checks failed
ci / backend (lint + typecheck + unit tests) (push) Failing after 1s
ci / frontend (lint + typecheck + build + unit tests) (push) Failing after 0s
docs: archive Podman runner setup runbook + track F-D1..F-D5
Two changes scoped together since both stem from the post-PR2 wrap-up.

docs/podman-runner-setup.md (new, ~190 LOC):

Operational runbook for the gitea-runner host that drives CI. The first
attempt at install hit four traps that this archived version documents
so we don't lose the lesson:

 1. `act_runner register` performs a sanity ping against the container
    daemon before writing the credential. Without the Podman socket
    mounted on the *register one-shot*, register fails silently and no
    .runner file is produced. The runbook mounts the socket on both
    register and daemon containers.
 2. SELinux blocks rootless socket access by default. Quadlet
    SecurityLabelDisable=true (or --security-opt label=disable for the
    legacy CLI form) is the documented bypass. No-op on Debian, required
    on RHEL/Fedora hosts.
 3. The runner user UID is not 1000 on every host (gitea = 1005 here).
    Quadlet `%U` substitution makes the unit portable; hardcoded UIDs
    are explicitly called out as a sprint 0 mistake.
 4. `podman generate systemd` is officially deprecated. Quadlet is the
    only supported pattern going forward and is what this runbook ships;
    legacy alternative is omitted on purpose.

Also captures: token placeholder convention (<TOKEN_FROM_GITEA_UI>,
never the real value in archived docs), single-use semantics, the
"secrets via file, not chat" convention, the `:X.Y.Z` pin policy versus
`:latest` in prod (ties into follow-up F-D1), and a decommissioning
section that cleans up state without nuking the user-level Podman socket.

tasks/todo.md:

New section "Frontend follow-ups (sprint 1+)" with F-D1..F-D5 from
code-reviewer on `chore/frontend-dockerfile` (649194b). All deferred,
none blocking. F-D1 (digest pinning) is project-wide and explicitly
references the backend image and the runner image alongside the
frontend ones for a single chore commit.
2026-05-23 03:08:03 +02:00

10 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.

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

Raised by code-reviewer during review of chore/frontend-dockerfile (649194b). None blocking, all deferred to sprint 1+. Some have a project- wide reach (F-D1 also covers the backend image and the runner image).

  • F-D1 — Pin container images by minor + digest, not by tag alone. Scope : frontend/Dockerfile (node:22-alpine, nginxinc/nginx-unprivileged:alpine), backend/Dockerfile (python:3.12-slim-bookworm), and the runner image referenced in docs/podman-runner-setup.md (gitea/act_runner:X.Y.Z). Harmonise the policy in a single chore commit.
  • F-D2 — Decide image-level HEALTHCHECK directives vs delegating health probing to Caddy upstream. Document the choice in docs/deploy.md.
  • F-D3 — Security response headers (X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Content-Security-Policy). Arbitrate ownership between frontend/nginx.conf and the Caddy outer layer to avoid duplication / conflict.
  • F-D4 — Enable response compression. Either gzip on + gzip_types in frontend/nginx.conf (runtime), or vite-plugin-compression (precompute .br / .gz at build time, served via gzip_static). Pick one.
  • F-D5 — OCI image labels (org.opencontainers.image.source, image.title, image.licenses, image.revision) on every Dockerfile. Useful for registry metadata and supply-chain attestation tooling.

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.