From 13ce5adec0d4afc7a0aaa96a76869ed94c04070b Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 31 May 2026 20:10:07 +0200 Subject: [PATCH] Preserve verified flag when promoting duplicate to new original When the original of a duplicate cluster is deleted (e.g. via engagement deletion), reconfigure_duplicate_cluster promotes the first remaining duplicate to the new primary. It already copies active and is_mitigated from the original, but not verified. The promoted finding kept its own verified=False, which blocked Jira's "Push All Issues" (requires active+verified). Add verified to the fields copied to the new original. Fixes #14911 --- dojo/finding/helper.py | 1 + .../test_prepare_duplicates_for_delete.py | 46 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/dojo/finding/helper.py b/dojo/finding/helper.py index 704402409d4..d1235afcadd 100644 --- a/dojo/finding/helper.py +++ b/dojo/finding/helper.py @@ -603,6 +603,7 @@ def reconfigure_duplicate_cluster(original, cluster_outside): duplicate=False, duplicate_finding=None, active=original.active, + verified=original.verified, is_mitigated=original.is_mitigated, ) new_original.found_by.set(original.found_by.all()) diff --git a/unittests/test_prepare_duplicates_for_delete.py b/unittests/test_prepare_duplicates_for_delete.py index b2fd4c64fb1..be2c2a6a0f6 100644 --- a/unittests/test_prepare_duplicates_for_delete.py +++ b/unittests/test_prepare_duplicates_for_delete.py @@ -294,15 +294,56 @@ def test_multiple_originals(self): self.assertFalse(dupe_of_b.duplicate) self.assertIsNone(dupe_of_b.duplicate_finding) - def test_original_status_copied_to_new_original(self): - """New original inherits active/is_mitigated status from deleted original.""" + def test_original_status_copied_to_new_original_active_verified(self): + """ + New original inherits active/verified/is_mitigated from deleted original. + + Positive case: original is an open, verified, not-mitigated finding. + Duplicate starts with the opposite of each field so every copy is observable. + + Regression test for issue #14911: promoted duplicates kept verified=False + even when the original was verified, blocking Jira "Push All Issues". + """ + original = self._create_finding(self.test1, "Original") + original.active = True + original.verified = True + original.is_mitigated = False + super(Finding, original).save(skip_validation=True) + + outside_dupe = self._create_finding(self.test2, "Outside Dupe") + outside_dupe.is_mitigated = True + super(Finding, outside_dupe).save(skip_validation=True) + self._make_duplicate(outside_dupe, original) # forces active=False, verified default False + + with impersonate(self.testuser): + prepare_duplicates_for_delete(self.test1) + + outside_dupe.refresh_from_db() + self.assertFalse(outside_dupe.duplicate) + self.assertTrue(outside_dupe.active) + self.assertTrue(outside_dupe.verified) + self.assertFalse(outside_dupe.is_mitigated) + + def test_original_status_copied_to_new_original_inactive_mitigated(self): + """ + New original inherits active/verified/is_mitigated from deleted original. + + Negative case: original is closed, unverified, mitigated. + Duplicate starts with the opposite of each field so every copy is observable. + """ original = self._create_finding(self.test1, "Original") original.active = False + original.verified = False original.is_mitigated = True super(Finding, original).save(skip_validation=True) outside_dupe = self._create_finding(self.test2, "Outside Dupe") + outside_dupe.verified = True + super(Finding, outside_dupe).save(skip_validation=True) self._make_duplicate(outside_dupe, original) + # _make_duplicate forces active=False; flip to True so the copy is observable + outside_dupe.active = True + super(Finding, outside_dupe).save(skip_validation=True) with impersonate(self.testuser): prepare_duplicates_for_delete(self.test1) @@ -310,6 +351,7 @@ def test_original_status_copied_to_new_original(self): outside_dupe.refresh_from_db() self.assertFalse(outside_dupe.duplicate) self.assertFalse(outside_dupe.active) + self.assertFalse(outside_dupe.verified) self.assertTrue(outside_dupe.is_mitigated) def test_found_by_copied_to_new_original(self):