diff --git a/backend/app/routers/tests.py b/backend/app/routers/tests.py index 2bd37d0..41c5640 100644 --- a/backend/app/routers/tests.py +++ b/backend/app/routers/tests.py @@ -402,6 +402,7 @@ def create_test_from_template( platform_override=payload.platform, procedure_text_override=payload.procedure_text, tool_used_override=payload.tool_used, + detect_procedure_override=payload.detect_procedure, ) # Call log_action() log_action( diff --git a/backend/app/schemas/test_template.py b/backend/app/schemas/test_template.py index 4ff2830..fac7b36 100644 --- a/backend/app/schemas/test_template.py +++ b/backend/app/schemas/test_template.py @@ -130,3 +130,4 @@ class TestTemplateInstantiate(BaseModel): platform: str | None = None procedure_text: str | None = None tool_used: str | None = None + detect_procedure: str | None = None diff --git a/backend/app/services/test_crud_service.py b/backend/app/services/test_crud_service.py index ab68044..ba3f231 100644 --- a/backend/app/services/test_crud_service.py +++ b/backend/app/services/test_crud_service.py @@ -304,6 +304,7 @@ def create_test_from_template( platform_override: str | None = None, procedure_text_override: str | None = None, tool_used_override: str | None = None, + detect_procedure_override: str | None = None, ) -> Test: """Instantiate a Test from a TestTemplate. @@ -364,7 +365,7 @@ def create_test_from_template( platform=platform_override if platform_override is not None else template.platform, procedure_text=procedure_text_override if procedure_text_override is not None else template.attack_procedure, tool_used=tool_used_override if tool_used_override is not None else template.tool_suggested, - detect_procedure=template.expected_detection, + detect_procedure=detect_procedure_override if detect_procedure_override is not None else template.expected_detection, remediation_steps=template.suggested_remediation, # Keyword argument: created_by created_by=creator_id, diff --git a/backend/tests/test_procedure_suggestions.py b/backend/tests/test_procedure_suggestions.py index 45c05fa..65f3d37 100644 --- a/backend/tests/test_procedure_suggestions.py +++ b/backend/tests/test_procedure_suggestions.py @@ -272,3 +272,37 @@ def test_duplicate_submission_does_not_create_a_second_pending_suggestion( listed = api("get", "/api/v1/procedure-suggestions", red_lead_headers).json() assert len(listed) == 1 + + +def test_lead_can_override_detect_procedure_when_creating_from_template( + client, db, api, red_lead_headers, technique, template, +): + """A lead editing Expected Detection in the create-from-template form + seeds the new test's detect_procedure with their edit — same mechanism + as procedure_text_override on the red side — without touching the + template itself. The template only updates later, through the normal + procedure-suggestion approval flow once a round is actually submitted.""" + resp = api( + "post", "/api/v1/tests/from-template", red_lead_headers, + json={ + "template_id": template["id"], + "technique_id": technique, + "detect_procedure": "Check Sysmon Event ID 1 for mimikatz.exe process creation.", + }, + ) + assert resp.status_code == 201, resp.text + assert resp.json()["detect_procedure"] == "Check Sysmon Event ID 1 for mimikatz.exe process creation." + + reloaded_template = api("get", f"/api/v1/test-templates/{template['id']}", red_lead_headers) + assert reloaded_template.json()["expected_detection"] == "Check process creation logs." + + +def test_detect_procedure_defaults_to_template_expected_detection( + client, db, api, red_lead_headers, technique, template, +): + resp = api( + "post", "/api/v1/tests/from-template", red_lead_headers, + json={"template_id": template["id"], "technique_id": technique}, + ) + assert resp.status_code == 201, resp.text + assert resp.json()["detect_procedure"] == "Check process creation logs." diff --git a/frontend/src/api/tests.ts b/frontend/src/api/tests.ts index e85dcd3..521b2f6 100644 --- a/frontend/src/api/tests.ts +++ b/frontend/src/api/tests.ts @@ -144,6 +144,7 @@ export async function createTestFromTemplate( platform?: string; procedure_text?: string; tool_used?: string; + detect_procedure?: string; }, ): Promise { const { data } = await client.post("/tests/from-template", { diff --git a/frontend/src/components/TestFromTemplateForm.tsx b/frontend/src/components/TestFromTemplateForm.tsx index 932b8d7..8c85dab 100644 --- a/frontend/src/components/TestFromTemplateForm.tsx +++ b/frontend/src/components/TestFromTemplateForm.tsx @@ -75,6 +75,7 @@ export default function TestFromTemplateForm({ platform: platform.trim() || undefined, procedure_text: procedureText.trim() || undefined, tool_used: toolUsed.trim() || undefined, + detect_procedure: expectedDetection.trim() || undefined, }), onSuccess: (test) => { navigate(`/tests/${test.id}`); @@ -208,17 +209,18 @@ export default function TestFromTemplateForm({ /> - {/* Expected Detection (read-only reference for Blue Team) */} + {/* Expected Detection */}
-
-

- {expectedDetection || "No detection guidance provided in template."} -

-
+