Skip to content

Commit 50450e1

Browse files
JuuJulien Reitzel
andauthored
Add notification when finding is created via the API (#13732)
* Add notification when finding is created via the API * fix: add tests * fix: Ruff linter COM812 errors * fix: move tests to test_notifications.TestNotificationTriggersApi * fix: W293 Blank line contains whitespace --------- Co-authored-by: Julien Reitzel <[email protected]>
1 parent 820f74c commit 50450e1

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

dojo/api_v2/serializers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
Vulnerability_Id_Template,
115115
get_current_date,
116116
)
117+
from dojo.notifications.helper import create_notification
117118
from dojo.product_announcements import (
118119
LargeScanSizeProductAnnouncement,
119120
ScanTypeProductAnnouncement,
@@ -1949,6 +1950,16 @@ def create(self, validated_data):
19491950
if push_to_jira:
19501951
jira_helper.push_to_jira(new_finding)
19511952

1953+
# Create a notification
1954+
create_notification(
1955+
event="finding_added",
1956+
title=_("Addition of %s") % new_finding.title,
1957+
finding=new_finding,
1958+
description=_('Finding "%s" was added by %s') % (new_finding.title, new_finding.reporter),
1959+
url=reverse("view_finding", args=(new_finding.id,)),
1960+
icon="exclamation-triangle",
1961+
)
1962+
19521963
return new_finding
19531964

19541965
def validate(self, data):

dojo/test/views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,6 @@ def process_forms(self, request: HttpRequest, test: Test, context: dict):
607607

608608
# Note: this notification has not be moved to "@receiver(post_save, sender=Finding)" method as many other notifications
609609
# Because it could generate too much noise, we keep it here only for findings created by hand in WebUI
610-
# TODO: but same should be implemented for API endpoint
611610

612611
# Create a notification
613612
create_notification(

unittests/test_notifications.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,122 @@ def setUp(self):
405405
token = Token.objects.get(user__username="admin")
406406
self.client = APIClient()
407407
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
408+
self.admin = User.objects.get(username="admin")
409+
self.base_url = "/api/v2/findings/"
410+
411+
def _minimal_create_payload(self, title: str):
412+
return {
413+
"test": 3,
414+
"found_by": [],
415+
"title": title,
416+
"date": "2020-05-20",
417+
"cwe": 1,
418+
"severity": "High",
419+
"description": "TEST finding for notification",
420+
"mitigation": "MITIGATION",
421+
"impact": "HIGH",
422+
"references": "",
423+
"active": True,
424+
"verified": False,
425+
"false_p": False,
426+
"duplicate": False,
427+
"out_of_scope": False,
428+
"under_review": False,
429+
"under_defect_review": False,
430+
"numerical_severity": "S0",
431+
}
408432

409433
@patch("dojo.notifications.helper.NotificationManager._process_notifications")
410434
def test_auditlog_on(self, mock):
411435
prod_type = Product_Type.objects.create(name="notif prod type API")
412436
self.client.delete(reverse("product_type-detail", args=(prod_type.pk,)), format="json")
413437
self.assertEqual(mock.call_args_list[-1].kwargs["description"], 'The product type "notif prod type API" was deleted by admin')
414438

439+
@patch("dojo.api_v2.serializers.create_notification")
440+
def test_create_calls_notification_with_auto_assigned_reporter(self, mock_create_notification):
441+
"""Test that create_notification is called when creating a finding without explicit reporter."""
442+
payload = self._minimal_create_payload("Finding with auto-assigned reporter notification")
443+
444+
response = self.client.post(self.base_url, payload, format="json")
445+
self.assertEqual(201, response.status_code, response.content[:1000])
446+
447+
# Verify notification was called
448+
mock_create_notification.assert_called_once()
449+
call_args = mock_create_notification.call_args
450+
451+
# Check the notification parameters
452+
self.assertEqual(call_args[1]["event"], "finding_added")
453+
self.assertEqual(call_args[1]["title"], "Addition of Finding With Auto-Assigned Reporter Notification")
454+
self.assertEqual(
455+
call_args[1]["description"],
456+
f'Finding "Finding With Auto-Assigned Reporter Notification" was added by {self.admin}',
457+
)
458+
self.assertEqual(call_args[1]["icon"], "exclamation-triangle")
459+
460+
# Verify the finding was created successfully
461+
created_id = response.data.get("id")
462+
self.assertIsNotNone(created_id)
463+
created_finding = Finding.objects.get(id=created_id)
464+
self.assertEqual(created_finding.reporter, self.admin)
465+
466+
@patch("dojo.api_v2.serializers.create_notification")
467+
def test_create_calls_notification_with_explicit_reporter(self, mock_create_notification):
468+
"""Test that create_notification is called when creating a finding with explicit reporter."""
469+
# Create another user to use as explicit reporter
470+
explicit_reporter = User.objects.create(username="explicit_reporter", email="[email protected]")
471+
472+
payload = self._minimal_create_payload("Finding with explicit reporter notification")
473+
payload["reporter"] = explicit_reporter.id
474+
475+
response = self.client.post(self.base_url, payload, format="json")
476+
self.assertEqual(201, response.status_code, response.content[:1000])
477+
478+
# Verify notification was called
479+
mock_create_notification.assert_called_once()
480+
call_args = mock_create_notification.call_args
481+
482+
# Check the notification parameters
483+
self.assertEqual(call_args[1]["event"], "finding_added")
484+
self.assertEqual(call_args[1]["title"], "Addition of Finding With Explicit Reporter Notification")
485+
self.assertEqual(
486+
call_args[1]["description"],
487+
f'Finding "Finding With Explicit Reporter Notification" was added by {explicit_reporter}',
488+
)
489+
self.assertEqual(call_args[1]["icon"], "exclamation-triangle")
490+
491+
# Verify the finding was created with explicit reporter
492+
created_id = response.data.get("id")
493+
self.assertIsNotNone(created_id)
494+
created_finding = Finding.objects.get(id=created_id)
495+
self.assertEqual(created_finding.reporter, explicit_reporter)
496+
497+
@patch("dojo.api_v2.serializers.create_notification")
498+
def test_notification_parameters_are_correct(self, mock_create_notification):
499+
"""Test that all notification parameters are properly formatted and passed."""
500+
payload = self._minimal_create_payload("Test Finding for Parameter Validation")
501+
502+
response = self.client.post(self.base_url, payload, format="json")
503+
self.assertEqual(201, response.status_code, response.content[:1000])
504+
505+
# Get the created finding to verify URL formation
506+
created_id = response.data.get("id")
507+
created_finding = Finding.objects.get(id=created_id)
508+
509+
# Verify notification was called with correct parameters
510+
mock_create_notification.assert_called_once()
511+
call_args = mock_create_notification.call_args
512+
513+
# Verify all required parameters exist
514+
self.assertEqual(call_args[1]["event"], "finding_added")
515+
self.assertEqual(call_args[1]["title"], "Addition of Test Finding for Parameter Validation")
516+
self.assertEqual(
517+
call_args[1]["description"],
518+
f'Finding "Test Finding for Parameter Validation" was added by {self.admin}',
519+
)
520+
self.assertEqual(call_args[1]["url"], f"/finding/{created_finding.id}")
521+
self.assertEqual(call_args[1]["icon"], "exclamation-triangle")
522+
self.assertEqual(call_args[1]["finding"], created_finding)
523+
415524

416525
class TestNotificationWebhooks(DojoTestCase):
417526
fixtures = ["dojo_testdata.json"]

0 commit comments

Comments
 (0)