Skip to content

Commit 2593926

Browse files
🐛 fix finding closed with a provided mitigated date #13699 (#13700)
* 🐛 fix finding closed with a provided mitigated date * advance unittests
1 parent 44ebefb commit 2593926

File tree

2 files changed

+111
-9
lines changed

2 files changed

+111
-9
lines changed

dojo/finding/helper.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from contextlib import suppress
3+
from datetime import datetime
34
from time import strftime
45

56
from django.conf import settings
@@ -9,6 +10,7 @@
910
from django.dispatch.dispatcher import receiver
1011
from django.urls import reverse
1112
from django.utils import timezone
13+
from django.utils.timezone import is_naive, make_aware, now
1214
from fieldsignals import pre_save_changed
1315

1416
import dojo.jira_link.helper as jira_helper
@@ -740,6 +742,17 @@ def save_vulnerability_ids_template(finding_template, vulnerability_ids):
740742
finding_template.cve = None
741743

742744

745+
def normalize_datetime(value):
746+
"""Ensure value is timezone-aware datetime."""
747+
if value:
748+
if not isinstance(value, datetime):
749+
value = datetime.combine(value, datetime.min.time())
750+
# Make timezone-aware if naive
751+
if is_naive(value):
752+
value = make_aware(value)
753+
return value
754+
755+
743756
def close_finding(
744757
*,
745758
finding,
@@ -761,15 +774,16 @@ def close_finding(
761774
"""
762775
# Core status updates
763776
finding.is_mitigated = is_mitigated
764-
now = timezone.now()
765-
finding.mitigated = mitigated or now
777+
current_time = now()
778+
mitigated_date = normalize_datetime(mitigated) or current_time
779+
finding.mitigated = mitigated_date
766780
finding.mitigated_by = mitigated_by or user
767781
finding.active = False
768782
finding.false_p = bool(false_p)
769783
finding.out_of_scope = bool(out_of_scope)
770784
finding.duplicate = bool(duplicate)
771785
finding.under_review = False
772-
finding.last_reviewed = finding.mitigated
786+
finding.last_reviewed = mitigated_date
773787
finding.last_reviewed_by = user
774788

775789
# Create note if provided
@@ -779,16 +793,16 @@ def close_finding(
779793
entry=note_entry,
780794
author=user,
781795
note_type=note_type,
782-
date=finding.mitigated,
796+
date=mitigated_date,
783797
)
784798
finding.notes.add(new_note)
785799

786800
# Endpoint statuses
787801
for status in finding.status_finding.all():
788802
status.mitigated_by = finding.mitigated_by
789-
status.mitigated_time = finding.mitigated
803+
status.mitigated_time = mitigated_date
790804
status.mitigated = True
791-
status.last_modified = timezone.now()
805+
status.last_modified = current_time
792806
status.save()
793807

794808
# Risk acceptance

unittests/test_finding_model.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,103 @@
1-
from datetime import datetime, timedelta
1+
from datetime import date, datetime, timedelta
22

33
from crum import impersonate
4-
5-
from dojo.models import DojoMeta, Engagement, Finding, Test, User
4+
from django.utils.timezone import is_naive, now
5+
6+
from dojo.finding.helper import close_finding
7+
from dojo.models import (
8+
DojoMeta,
9+
Endpoint,
10+
Endpoint_Status,
11+
Engagement,
12+
Finding,
13+
Note_Type,
14+
Notes,
15+
Product,
16+
Product_Type,
17+
Test,
18+
Test_Type,
19+
User,
20+
)
621

722
from .dojo_test_case import DojoTestCase
823

924

1025
class TestFindingModel(DojoTestCase):
1126
fixtures = ["dojo_testdata.json"]
1227

28+
def setUp(self):
29+
self.user = User.objects.first() # Use a user from fixtures
30+
self.product_type = Product_Type.objects.create(name="Test Product Type")
31+
self.product = Product.objects.create(name="Test Product", prod_type=self.product_type)
32+
self.engagement = Engagement.objects.create(
33+
name="Test Engagement",
34+
product=self.product,
35+
target_start=now(),
36+
target_end=now(),
37+
)
38+
self.test_type = Test_Type.objects.create(name="Unit Test Type")
39+
self.test = Test.objects.create(
40+
engagement=self.engagement,
41+
test_type=self.test_type,
42+
title="Test for Finding",
43+
target_start=now(),
44+
target_end=now(),
45+
)
46+
self.finding = Finding.objects.create(title="Close Finding Test", active=True, test=self.test)
47+
self.endpoint = Endpoint.objects.create(host="test.local")
48+
self.endpoint_status = Endpoint_Status.objects.create(finding=self.finding, endpoint=self.endpoint)
49+
self.finding.status_finding.add(self.endpoint_status)
50+
51+
def test_close_finding_with_naive_date(self):
52+
note_type_obj = Note_Type.objects.create(name="General")
53+
naive_date = date.today() # No timezone
54+
close_finding(
55+
finding=self.finding,
56+
user=self.user,
57+
is_mitigated=True,
58+
mitigated=naive_date,
59+
mitigated_by=None,
60+
false_p=False,
61+
out_of_scope=False,
62+
duplicate=False,
63+
note_entry="Mitigation note",
64+
note_type=note_type_obj,
65+
)
66+
self.assertFalse(is_naive(self.finding.mitigated))
67+
note = Notes.objects.filter(finding=self.finding).first()
68+
self.assertIsNotNone(note)
69+
self.assertFalse(is_naive(note.date))
70+
status = Endpoint_Status.objects.filter(finding=self.finding).first()
71+
self.assertTrue(status.mitigated)
72+
self.assertFalse(is_naive(status.mitigated_time))
73+
74+
def test_close_finding_with_naive_datetime(self):
75+
naive_datetime = datetime(2025, 11, 12, 0, 0, 0)
76+
close_finding(
77+
finding=self.finding,
78+
user=self.user,
79+
is_mitigated=True,
80+
mitigated=naive_datetime,
81+
mitigated_by=None,
82+
false_p=False,
83+
out_of_scope=False,
84+
duplicate=False,
85+
)
86+
self.assertFalse(is_naive(self.finding.mitigated))
87+
88+
def test_close_finding_with_none_mitigated(self):
89+
close_finding(
90+
finding=self.finding,
91+
user=self.user,
92+
is_mitigated=True,
93+
mitigated=None,
94+
mitigated_by=None,
95+
false_p=False,
96+
out_of_scope=False,
97+
duplicate=False,
98+
)
99+
self.assertFalse(is_naive(self.finding.mitigated))
100+
13101
def test_get_sast_source_file_path_with_link_no_file_path(self):
14102
finding = Finding()
15103
self.assertEqual(None, finding.get_sast_source_file_path_with_link())

0 commit comments

Comments
 (0)