feat(templates,campaigns): validate MITRE technique IDs, surface template suggestions first, manager can re-approve rejected campaigns
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
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
- 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.
This commit is contained in:
@@ -417,3 +417,56 @@ def test_lead_created_campaign_is_still_draft_not_auto_approved(api, db, red_lea
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
assert resp.json()["status"] == "draft"
|
||||
|
||||
|
||||
def test_manager_can_edit_a_campaign_they_rejected(api, db, red_lead_user, red_lead_headers, manager_headers):
|
||||
campaign = _make_draft_campaign(db, red_lead_user.id)
|
||||
api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers)
|
||||
reject = api(
|
||||
"post", f"/api/v1/campaigns/{campaign.id}/reject", manager_headers,
|
||||
json={"reason": "Needs a clearer scope"},
|
||||
)
|
||||
assert reject.status_code == 200, reject.text
|
||||
assert reject.json()["status"] == "draft"
|
||||
|
||||
resp = api(
|
||||
"patch", f"/api/v1/campaigns/{campaign.id}", manager_headers,
|
||||
json={"name": "Edited by manager after rejection"},
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["name"] == "Edited by manager after rejection"
|
||||
|
||||
|
||||
def test_manager_cannot_edit_a_draft_campaign_that_was_never_rejected(
|
||||
api, db, red_lead_user, red_lead_headers, manager_headers,
|
||||
):
|
||||
campaign = _make_draft_campaign(db, red_lead_user.id)
|
||||
resp = api(
|
||||
"patch", f"/api/v1/campaigns/{campaign.id}", manager_headers,
|
||||
json={"name": "Manager should not be able to do this"},
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
def test_manager_can_approve_a_campaign_they_previously_rejected(
|
||||
api, db, red_lead_user, red_lead_headers, manager_headers,
|
||||
):
|
||||
campaign = _make_draft_campaign(db, red_lead_user.id)
|
||||
api("post", f"/api/v1/campaigns/{campaign.id}/submit", red_lead_headers)
|
||||
api(
|
||||
"post", f"/api/v1/campaigns/{campaign.id}/reject", manager_headers,
|
||||
json={"reason": "Needs a clearer scope"},
|
||||
)
|
||||
api(
|
||||
"patch", f"/api/v1/campaigns/{campaign.id}", manager_headers,
|
||||
json={"description": "Scope clarified after rejection"},
|
||||
)
|
||||
|
||||
resp = api(
|
||||
"post", f"/api/v1/campaigns/{campaign.id}/approve", manager_headers,
|
||||
json={"start_date": "2020-01-01T00:00:00"},
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
body = resp.json()
|
||||
assert body["status"] == "active"
|
||||
assert body["rejection_reason"] is None
|
||||
|
||||
Reference in New Issue
Block a user