"""Deterministic in-memory C2 adapter — used when MIMIC_C2_ADAPTER=fake. Intended for integration tests and local development without a live Mythic instance. Task state is per-instance so parallel tests don't interfere with each other. """ from __future__ import annotations from backend.app.services.c2.adapter import ( C2Adapter, C2Callback, C2Health, C2TaskPage, C2TaskStatus, ) # Three fixed callbacks the test suite can pin against. _FAKE_CALLBACKS = [ C2Callback( display_id=1, active=True, host="WORKSTATION-01", user="jdoe", domain="LAB", last_checkin="2026-06-10T00:00:00Z", ), C2Callback( display_id=2, active=True, host="SERVER-DC01", user="svc_backup", domain="LAB", last_checkin="2026-06-10T00:01:00Z", ), C2Callback( display_id=3, active=True, host="LAPTOP-RT", user="admin", domain="LAB", last_checkin="2026-06-10T00:02:00Z", ), ] class FakeAdapter(C2Adapter): """In-memory adapter with deterministic behaviour. Each instance starts with an empty task store and display_ids from 1000. """ def __init__(self) -> None: self._tasks: dict[int, dict] = {} self._next_task_id = 1000 def test_connection(self) -> C2Health: return C2Health(ok=True) def list_callbacks(self) -> list[C2Callback]: return list(_FAKE_CALLBACKS) def create_task( self, callback_display_id: int, command: str, params: str | None = None, ) -> int: tid = self._next_task_id self._next_task_id += 1 self._tasks[tid] = { "display_id": tid, "callback_display_id": callback_display_id, "command": command, "params": params, "status": "submitted", "completed": False, "output": None, } return tid def get_task(self, task_display_id: int) -> C2TaskStatus: task = self._tasks.get(task_display_id) if task is None: return C2TaskStatus(display_id=task_display_id, status="unknown", completed=False) return C2TaskStatus( display_id=task_display_id, status=task["status"], completed=task["completed"], ) def get_task_output(self, task_display_id: int) -> str: task = self._tasks.get(task_display_id) if task is None: return "" return task.get("output") or "" def list_callback_tasks( self, callback_display_id: int, page: int = 1, page_size: int = 25, ) -> C2TaskPage: items = [ t for t in self._tasks.values() if t["callback_display_id"] == callback_display_id ] start = (page - 1) * page_size return C2TaskPage( items=items[start : start + page_size], total=len(items), page=page, page_size=page_size, )