88from django .urls import reverse
99
1010from dojo .metrics import utils
11- from dojo .models import Product_Type , User
11+ from dojo .models import Engagement , Finding , Product , Product_Type , Test , User
1212
1313from .dojo_test_case import DojoTestCase
1414
@@ -31,12 +31,12 @@ def add(*args, **kwargs):
3131FINDING_8 = {"id" : 240 , "date" : date (2018 , 1 , 1 ), "severity" : "High" , "active" : True , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 2 , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
3232FINDING_9 = {"id" : 241 , "date" : date (2018 , 1 , 1 ), "severity" : "High" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 2 , "out_of_scope" : False , "risk_accepted" : True , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
3333FINDING_10 = {"id" : 242 , "date" : date (2018 , 1 , 1 ), "severity" : "High" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 2 , "out_of_scope" : False , "risk_accepted" : True , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
34- FINDING_11 = {"id" : 243 , "date" : date (2017 , 12 , 31 ), "severity" : "High" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : False , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : True , "under_review" : False , "is_mitigated" : True , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 2 , "numerical_severity" : "S0" }
34+ FINDING_11 = {"id" : 243 , "date" : date (2017 , 12 , 31 ), "severity" : "High" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : False , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : True , "under_review" : False , "is_mitigated" : True , "mitigated" : datetime ( 2018 , 1 , 2 , tzinfo = zoneinfo . ZoneInfo ( "UTC" )) , "mitigated_by_id" : None , "reporter_id" : 2 , "numerical_severity" : "S0" }
3535FINDING_12 = {"id" : 244 , "date" : date (2017 , 12 , 29 ), "severity" : "Low" , "active" : True , "verified" : True , "false_p" : False , "duplicate" : False , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
3636FINDING_13 = {"id" : 245 , "date" : date (2017 , 12 , 27 ), "severity" : "Low" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 22 , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
3737FINDING_14 = {"id" : 246 , "date" : date (2018 , 1 , 2 ), "severity" : "Low" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 22 , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
3838FINDING_15 = {"id" : 247 , "date" : date (2018 , 1 , 3 ), "severity" : "Low" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
39- FINDING_16 = {"id" : 248 , "date" : date (2017 , 12 , 27 ), "severity" : "Low" , "active" : True , "verified" : True , "false_p" : False , "duplicate" : False , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : True , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
39+ FINDING_16 = {"id" : 248 , "date" : date (2017 , 12 , 27 ), "severity" : "Low" , "active" : True , "verified" : True , "false_p" : False , "duplicate" : False , "duplicate_finding_id" : None , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : True , "mitigated" : datetime ( 2017 , 12 , 28 , tzinfo = zoneinfo . ZoneInfo ( "UTC" )) , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
4040FINDING_17 = {"id" : 249 , "date" : date (2018 , 1 , 4 ), "severity" : "Low" , "active" : False , "verified" : False , "false_p" : False , "duplicate" : True , "duplicate_finding_id" : 224 , "out_of_scope" : False , "risk_accepted" : False , "under_review" : False , "is_mitigated" : False , "mitigated" : None , "mitigated_by_id" : None , "reporter_id" : 1 , "numerical_severity" : "S0" }
4141
4242
@@ -163,6 +163,96 @@ def test_finding_queries(self, mock_timezone):
163163 self .assertIsInstance (finding_queries ["start_date" ], datetime )
164164 self .assertIsInstance (finding_queries ["end_date" ], datetime )
165165
166+ @patch ("django.utils.timezone.now" )
167+ def test_closed_findings_filtered_by_mitigated_date (self , mock_timezone ):
168+ """
169+ Test that closed findings are filtered by mitigated date, not discovery date.
170+
171+ This test verifies the fix for issue #9735: findings discovered outside the date
172+ range but closed within it should appear in closed metrics.
173+ """
174+ mock_datetime = datetime (2020 , 12 , 9 , tzinfo = zoneinfo .ZoneInfo ("UTC" ))
175+ mock_timezone .return_value = mock_datetime
176+
177+ # Get a test product and engagement
178+ product = Product .objects .first ()
179+ if not product :
180+ self .skipTest ("No product available in test data" )
181+ engagement = Engagement .objects .filter (product = product ).first ()
182+ if not engagement :
183+ self .skipTest ("No engagement available in test data" )
184+ test = Test .objects .filter (engagement = engagement ).first ()
185+ if not test :
186+ self .skipTest ("No test available in test data" )
187+
188+ # Create a finding discovered BEFORE the date range but closed WITHIN it
189+ # Date range: 2017-12-26 to 2018-01-05
190+ finding_discovered_before = Finding .objects .create (
191+ title = "Finding discovered before range, closed within range" ,
192+ description = "Test finding" ,
193+ severity = "High" ,
194+ date = date (2017 , 10 , 1 ), # Discovered before range
195+ test = test ,
196+ reporter = self .request .user ,
197+ active = False ,
198+ is_mitigated = True ,
199+ mitigated = datetime (2018 , 1 , 2 , tzinfo = zoneinfo .ZoneInfo ("UTC" )), # Closed within range
200+ )
201+
202+ # Create a finding discovered WITHIN the date range but closed AFTER it
203+ finding_closed_after = Finding .objects .create (
204+ title = "Finding discovered within range, closed after range" ,
205+ description = "Test finding" ,
206+ severity = "Medium" ,
207+ date = date (2017 , 12 , 30 ), # Discovered within range
208+ test = test ,
209+ reporter = self .request .user ,
210+ active = False ,
211+ is_mitigated = True ,
212+ mitigated = datetime (2018 , 2 , 1 , tzinfo = zoneinfo .ZoneInfo ("UTC" )), # Closed after range
213+ )
214+
215+ # Create a finding discovered and closed WITHIN the date range
216+ finding_both_within = Finding .objects .create (
217+ title = "Finding discovered and closed within range" ,
218+ description = "Test finding" ,
219+ severity = "Low" ,
220+ date = date (2017 , 12 , 30 ), # Discovered within range
221+ test = test ,
222+ reporter = self .request .user ,
223+ active = False ,
224+ is_mitigated = True ,
225+ mitigated = datetime (2018 , 1 , 3 , tzinfo = zoneinfo .ZoneInfo ("UTC" )), # Closed within range
226+ )
227+
228+ try :
229+ product_types = []
230+ finding_queries = utils .finding_queries (
231+ product_types ,
232+ self .request ,
233+ )
234+
235+ closed_findings = finding_queries ["closed" ]
236+ closed_ids = list (closed_findings .values_list ("id" , flat = True ))
237+
238+ # The finding discovered before but closed within should appear
239+ self .assertIn (finding_discovered_before .id , closed_ids ,
240+ "Finding discovered before range but closed within range should appear in closed metrics" )
241+
242+ # The finding discovered within but closed after should NOT appear
243+ self .assertNotIn (finding_closed_after .id , closed_ids ,
244+ "Finding discovered within range but closed after range should NOT appear in closed metrics" )
245+
246+ # The finding discovered and closed within should appear
247+ self .assertIn (finding_both_within .id , closed_ids ,
248+ "Finding discovered and closed within range should appear in closed metrics" )
249+
250+ finally :
251+ # Clean up test findings
252+ finding_discovered_before .delete ()
253+ finding_closed_after .delete ()
254+ finding_both_within .delete ()
255+
166256
167257class EndpointQueriesTest (DojoTestCase ):
168258 fixtures = ["dojo_testdata.json" ]
0 commit comments