fix(jira,tests): push system gaps to Jira, show detect suggested procedure in catalog
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
flag_blue_review_gap saved system_gaps on the test but the Jira in_review comment never mentioned it, so a flagged capability gap was silently invisible in Jira. Now included in the comment and tagged with a system-gap label for filtering. Also surfaces TestTemplate.detect_suggested_procedure as a read-only reference in the Test Catalog's create-from-template modal — it was only ever shown in the admin template editor, not here, so an approved suggestion appeared to vanish when viewed from the catalog.
This commit is contained in:
@@ -508,6 +508,8 @@ def _build_state_comment(
|
|||||||
"",
|
"",
|
||||||
f"*Detection Result:* {test.detection_result or 'N/A'}",
|
f"*Detection Result:* {test.detection_result or 'N/A'}",
|
||||||
]
|
]
|
||||||
|
if test.system_gaps:
|
||||||
|
lines += ["", "h4. ⚠️ System Gap Flagged", test.system_gaps]
|
||||||
if test.blue_summary:
|
if test.blue_summary:
|
||||||
lines += ["", "h4. Blue Team Summary", test.blue_summary]
|
lines += ["", "h4. Blue Team Summary", test.blue_summary]
|
||||||
if test.remediation_steps:
|
if test.remediation_steps:
|
||||||
@@ -933,6 +935,8 @@ def push_test_event(
|
|||||||
"Could not unassign %s for state %s: %s",
|
"Could not unassign %s for state %s: %s",
|
||||||
link.jira_issue_key, new_state, exc_a,
|
link.jira_issue_key, new_state, exc_a,
|
||||||
)
|
)
|
||||||
|
if test.system_gaps:
|
||||||
|
_add_jira_label(jira, link.jira_issue_key, "system-gap")
|
||||||
|
|
||||||
link.last_synced_at = datetime.utcnow()
|
link.last_synced_at = datetime.utcnow()
|
||||||
db.flush()
|
db.flush()
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ def _make_test(**overrides):
|
|||||||
t.blue_validation_notes = None
|
t.blue_validation_notes = None
|
||||||
t.red_tech_assignee = None
|
t.red_tech_assignee = None
|
||||||
t.blue_tech_assignee = None
|
t.blue_tech_assignee = None
|
||||||
|
t.system_gaps = None
|
||||||
for k, v in overrides.items():
|
for k, v in overrides.items():
|
||||||
setattr(t, k, v)
|
setattr(t, k, v)
|
||||||
return t
|
return t
|
||||||
@@ -302,6 +303,45 @@ def test_push_test_event_clears_assignee_on_in_review(mock_get_client, mock_conf
|
|||||||
mock_jira.assign_issue.assert_called_once_with(link.jira_issue_key, account_id=None)
|
mock_jira.assign_issue.assert_called_once_with(link.jira_issue_key, account_id=None)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("app.services.jira_service.has_admin_jira_configured", return_value=True)
|
||||||
|
@patch("app.services.jira_service.get_admin_jira_client")
|
||||||
|
def test_push_test_event_includes_system_gap_and_labels_it(mock_get_client, mock_configured, db):
|
||||||
|
"""A Blue Lead flagging a capability gap must surface it in Jira — both
|
||||||
|
in the comment (so the reason is visible) and as a label (so gap-flagged
|
||||||
|
tickets are filterable without opening each one)."""
|
||||||
|
mock_jira = MagicMock()
|
||||||
|
mock_jira.issue.return_value = {"fields": {"labels": []}}
|
||||||
|
mock_get_client.return_value = mock_jira
|
||||||
|
test = _make_test(system_gaps="Missing EDR agent on host X")
|
||||||
|
link = _make_link(test.id)
|
||||||
|
db.add(link)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
jira_service.push_test_event(db, test, MagicMock(username="bluelead", jira_account_id=None), "in_review")
|
||||||
|
|
||||||
|
comment = mock_jira.issue_add_comment.call_args[0][1]
|
||||||
|
assert "Missing EDR agent on host X" in comment
|
||||||
|
mock_jira.update_issue_field.assert_called_once_with(
|
||||||
|
link.jira_issue_key, fields={"labels": ["system-gap"]},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("app.services.jira_service.has_admin_jira_configured", return_value=True)
|
||||||
|
@patch("app.services.jira_service.get_admin_jira_client")
|
||||||
|
def test_push_test_event_in_review_without_gap_does_not_add_label(mock_get_client, mock_configured, db):
|
||||||
|
mock_jira = MagicMock()
|
||||||
|
mock_get_client.return_value = mock_jira
|
||||||
|
test = _make_test()
|
||||||
|
link = _make_link(test.id)
|
||||||
|
db.add(link)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
jira_service.push_test_event(db, test, MagicMock(username="alice", jira_account_id=None), "in_review")
|
||||||
|
|
||||||
|
mock_jira.issue.assert_not_called()
|
||||||
|
mock_jira.update_issue_field.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
@patch("app.services.jira_service.has_admin_jira_configured", return_value=True)
|
@patch("app.services.jira_service.has_admin_jira_configured", return_value=True)
|
||||||
@patch("app.services.jira_service.get_admin_jira_client")
|
@patch("app.services.jira_service.get_admin_jira_client")
|
||||||
def test_push_test_event_clears_assignee_on_fresh_blue_queue_entry(mock_get_client, mock_configured, db):
|
def test_push_test_event_clears_assignee_on_fresh_blue_queue_entry(mock_get_client, mock_configured, db):
|
||||||
|
|||||||
@@ -221,6 +221,19 @@ export default function TestFromTemplateForm({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Detect Suggested Procedure (read-only reference for Blue Team) */}
|
||||||
|
<div>
|
||||||
|
<label className="mb-1.5 block text-sm font-medium text-gray-300">
|
||||||
|
Detect Suggested Procedure
|
||||||
|
<span className="ml-2 text-xs text-gray-500">(read-only reference for Blue Team)</span>
|
||||||
|
</label>
|
||||||
|
<div className="rounded-lg border border-gray-700 bg-gray-800/50 p-3">
|
||||||
|
<p className="whitespace-pre-wrap font-mono text-sm text-gray-400">
|
||||||
|
{template?.detect_suggested_procedure || "No detection procedure suggested yet."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Error */}
|
{/* Error */}
|
||||||
{createMutation.isError && (
|
{createMutation.isError && (
|
||||||
<div className="rounded-lg border border-red-500/30 bg-red-900/20 p-3 text-sm text-red-400">
|
<div className="rounded-lg border border-red-500/30 bg-red-900/20 p-3 text-sm text-red-400">
|
||||||
|
|||||||
Reference in New Issue
Block a user