60ab2e558f
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
- Campaign creation and generate-from-threat-actor now use the strict
role check — admin no longer gets a free pass into campaign content,
same principle already applied to test-template creation.
- New manager-only DELETE /tests/{id}: removes a standalone test still
in draft (not started, not linked to any campaign).
- Replaced the orange/indigo stand-ins used across team badges, tabs,
action buttons, and timers with true red/blue so Red Team and Blue
Team read unambiguously at a glance.
74 lines
2.5 KiB
Python
74 lines
2.5 KiB
Python
"""A manager (and only a manager) can delete a standalone test from the
|
|
queue, but only while it hasn't started yet (draft) and isn't linked to
|
|
any campaign — a test already in progress, or one that's part of a
|
|
campaign's plan, must go through the normal workflow instead.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def technique(api, auth_headers):
|
|
resp = api(
|
|
"post", "/api/v1/techniques", auth_headers,
|
|
json={"mitre_id": "T1059.600", "name": "Command Line Delete Test"},
|
|
)
|
|
assert resp.status_code == 201, resp.text
|
|
return resp.json()["id"]
|
|
|
|
|
|
@pytest.fixture
|
|
def draft_test(api, red_lead_headers, technique):
|
|
resp = api(
|
|
"post", "/api/v1/tests", red_lead_headers,
|
|
json={"technique_id": technique, "name": "Standalone draft test"},
|
|
)
|
|
assert resp.status_code == 201, resp.text
|
|
return resp.json()["id"]
|
|
|
|
|
|
def test_manager_can_delete_draft_standalone_test(api, manager_headers, draft_test):
|
|
resp = api("delete", f"/api/v1/tests/{draft_test}", manager_headers)
|
|
assert resp.status_code == 204, resp.text
|
|
|
|
reloaded = api("get", f"/api/v1/tests/{draft_test}", manager_headers)
|
|
assert reloaded.status_code == 404
|
|
|
|
|
|
def test_red_lead_cannot_delete_test(api, red_lead_headers, draft_test):
|
|
resp = api("delete", f"/api/v1/tests/{draft_test}", red_lead_headers)
|
|
assert resp.status_code == 403
|
|
|
|
|
|
def test_admin_cannot_delete_test(api, auth_headers, draft_test):
|
|
resp = api("delete", f"/api/v1/tests/{draft_test}", auth_headers)
|
|
assert resp.status_code == 403
|
|
|
|
|
|
def test_manager_cannot_delete_started_test(api, manager_headers, red_tech_headers, draft_test):
|
|
started = api("post", f"/api/v1/tests/{draft_test}/start-execution", red_tech_headers)
|
|
assert started.status_code == 200, started.text
|
|
|
|
resp = api("delete", f"/api/v1/tests/{draft_test}", manager_headers)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
def test_manager_cannot_delete_test_linked_to_a_campaign(
|
|
api, db, manager_headers, red_lead_headers, draft_test,
|
|
):
|
|
campaign = api(
|
|
"post", "/api/v1/campaigns", red_lead_headers,
|
|
json={"name": "Holds the test"},
|
|
)
|
|
assert campaign.status_code == 201, campaign.text
|
|
campaign_id = campaign.json()["id"]
|
|
|
|
add = api(
|
|
"post", f"/api/v1/campaigns/{campaign_id}/tests", red_lead_headers,
|
|
json={"test_id": draft_test},
|
|
)
|
|
assert add.status_code in (200, 201), add.text
|
|
|
|
resp = api("delete", f"/api/v1/tests/{draft_test}", manager_headers)
|
|
assert resp.status_code == 400
|