"""Tests for GET /api/engagements//c2/callbacks.""" from __future__ import annotations import pytest from cryptography.fernet import Fernet from flask.testing import FlaskClient from backend.app.services.c2.adapter import C2Error from backend.tests.conftest import auth_headers as _h _FERNET_KEY = Fernet.generate_key().decode() @pytest.fixture(autouse=True) def set_encryption_key(monkeypatch): monkeypatch.setenv("MIMIC_ENCRYPTION_KEY", _FERNET_KEY) @pytest.fixture(autouse=True) def use_fake_adapter(monkeypatch): monkeypatch.setenv("MIMIC_C2_ADAPTER", "fake") def _make_engagement(client: FlaskClient, token: str) -> dict: resp = client.post( "/api/engagements", headers=_h(token), json={"name": "Op Alpha", "start_date": "2026-06-10"}, ) assert resp.status_code == 201 return resp.get_json() def _put_config(client: FlaskClient, token: str, eid: int) -> None: resp = client.put( f"/api/engagements/{eid}/c2-config", headers=_h(token), json={"url": "https://c2.internal:7443", "api_token": "s3cr3t", "verify_tls": True}, ) assert resp.status_code == 200 class TestGetCallbacksHappyPath: def test_returns_3_callbacks_with_fake_adapter( self, client: FlaskClient, admin_token: str ) -> None: eng = _make_engagement(client, admin_token) _put_config(client, admin_token, eng["id"]) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(admin_token), ) assert resp.status_code == 200 body = resp.get_json() assert "callbacks" in body assert len(body["callbacks"]) == 3 def test_callback_shape(self, client: FlaskClient, admin_token: str) -> None: eng = _make_engagement(client, admin_token) _put_config(client, admin_token, eng["id"]) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(admin_token), ) cb = resp.get_json()["callbacks"][0] assert "display_id" in cb assert "active" in cb assert "host" in cb assert "user" in cb assert "domain" in cb assert "last_checkin" in cb def test_redteam_allowed( self, client: FlaskClient, admin_token: str, redteam_token: str ) -> None: eng = _make_engagement(client, admin_token) _put_config(client, admin_token, eng["id"]) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(redteam_token), ) assert resp.status_code == 200 class TestGetCallbacksErrorCases: def test_404_when_no_config(self, client: FlaskClient, admin_token: str) -> None: eng = _make_engagement(client, admin_token) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(admin_token), ) assert resp.status_code == 404 def test_404_engagement_not_found(self, client: FlaskClient, admin_token: str) -> None: resp = client.get( "/api/engagements/9999/c2/callbacks", headers=_h(admin_token), ) assert resp.status_code == 404 def test_403_soc( self, client: FlaskClient, admin_token: str, soc_token: str ) -> None: eng = _make_engagement(client, admin_token) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(soc_token), ) assert resp.status_code == 403 def test_503_no_key( self, monkeypatch, client: FlaskClient, admin_token: str ) -> None: monkeypatch.delenv("MIMIC_ENCRYPTION_KEY", raising=False) eng = _make_engagement(client, admin_token) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(admin_token), ) assert resp.status_code == 503 def test_502_when_adapter_raises( self, monkeypatch, client: FlaskClient, admin_token: str ) -> None: from backend.app.services.c2 import fake as fake_mod def _boom(self): raise C2Error("mythic unreachable") monkeypatch.setattr(fake_mod.FakeAdapter, "list_callbacks", _boom) eng = _make_engagement(client, admin_token) _put_config(client, admin_token, eng["id"]) resp = client.get( f"/api/engagements/{eng['id']}/c2/callbacks", headers=_h(admin_token), ) assert resp.status_code == 502 assert "mythic unreachable" in resp.get_json().get("error", "")