Files
Aegis/backend/tests/test_mitre_technique_id_validation.py
kitos 82ac9c7014
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
feat(templates,campaigns): validate MITRE technique IDs, surface template suggestions first, manager can re-approve rejected campaigns
- TestTemplate/TemplateSuggestion creation and updates now reject any
  mitre_technique_id that doesn't match a real, already-synced MITRE
  ATT&CK technique. Frontend swaps the free-text ID input for a
  technique picker.
- Test Catalog now shows pending template suggestions before the
  catalog grid, with full field detail (platform, severity, tool,
  atomic ID, source URL, remediation) instead of just name/procedure.
- A manager can edit and directly re-approve a campaign they previously
  rejected, instead of needing the original lead to resubmit it.
2026-07-16 14:29:01 +02:00

79 lines
3.0 KiB
Python

"""A TestTemplate/TemplateSuggestion's mitre_technique_id must correspond
to a real, already-synced MITRE ATT&CK technique — free text or a typo'd
ID would otherwise create an orphaned template invisible to technique
lookups and coverage reporting.
"""
import pytest
@pytest.fixture
def technique(api, auth_headers):
resp = api(
"post", "/api/v1/techniques", auth_headers,
json={"mitre_id": "T1059.500", "name": "Command Line MITRE Validation"},
)
assert resp.status_code == 201, resp.text
return resp.json()["id"]
class TestTemplateCreateValidation:
def test_create_template_rejects_unknown_mitre_id(self, api, red_lead_headers, technique):
resp = api(
"post", "/api/v1/test-templates", red_lead_headers,
json={"mitre_technique_id": "T9999.999", "name": "Bogus template"},
)
assert resp.status_code == 400
assert "T9999.999" in resp.text
def test_create_template_accepts_known_mitre_id(self, api, red_lead_headers, technique):
resp = api(
"post", "/api/v1/test-templates", red_lead_headers,
json={"mitre_technique_id": "T1059.500", "name": "Valid template"},
)
assert resp.status_code == 201, resp.text
def test_update_template_rejects_unknown_mitre_id(self, api, red_lead_headers, technique):
created = api(
"post", "/api/v1/test-templates", red_lead_headers,
json={"mitre_technique_id": "T1059.500", "name": "Will be edited"},
)
template_id = created.json()["id"]
resp = api(
"patch", f"/api/v1/test-templates/{template_id}", red_lead_headers,
json={"mitre_technique_id": "T0000.000", "name": "Will be edited"},
)
assert resp.status_code == 400
class TestTemplateSuggestionValidation:
def test_propose_template_rejects_unknown_mitre_id(self, api, red_tech_headers, technique):
resp = api(
"post", "/api/v1/template-suggestions", red_tech_headers,
json={"mitre_technique_id": "T8888.888", "name": "Bogus proposal"},
)
assert resp.status_code == 400
def test_propose_template_accepts_known_mitre_id(self, api, red_tech_headers, technique):
resp = api(
"post", "/api/v1/template-suggestions", red_tech_headers,
json={"mitre_technique_id": "T1059.500", "name": "Valid proposal"},
)
assert resp.status_code == 201, resp.text
def test_approve_suggestion_with_edited_unknown_mitre_id_rejected(
self, api, red_tech_headers, red_lead_headers, technique,
):
created = api(
"post", "/api/v1/template-suggestions", red_tech_headers,
json={"mitre_technique_id": "T1059.500", "name": "Valid proposal"},
)
suggestion_id = created.json()["id"]
resp = api(
"post", f"/api/v1/template-suggestions/{suggestion_id}/approve", red_lead_headers,
json={"mitre_technique_id": "T7777.777"},
)
assert resp.status_code == 400