|
126 | 126 | # product 3: Security Podcast |
127 | 127 |
|
128 | 128 |
|
| 129 | +# Module-level candidate-filter hooks for FINDING_FALSE_POSITIVE_HISTORY_CANDIDATE_FILTER_METHOD. |
| 130 | +# get_custom_method resolves the setting to a dotted path and imports it, so these must be |
| 131 | +# importable at module scope. |
| 132 | +_fp_candidate_filter_calls = [] |
| 133 | + |
| 134 | + |
| 135 | +def _drop_all_fp_candidates(finding, candidates): |
| 136 | + """Hook that discards every candidate — simulates a plugin rejecting all matches.""" |
| 137 | + _fp_candidate_filter_calls.append((finding, list(candidates))) |
| 138 | + return [] |
| 139 | + |
| 140 | + |
| 141 | +def _passthrough_fp_candidates(finding, candidates): |
| 142 | + """Hook that keeps every candidate — FP history must behave exactly as with no hook.""" |
| 143 | + _fp_candidate_filter_calls.append((finding, list(candidates))) |
| 144 | + return candidates |
| 145 | + |
| 146 | + |
129 | 147 | @versioned_fixtures |
130 | 148 | @override_settings(SETTINGS_CACHE_L1_TTL=30, SETTINGS_CACHE_L2_TTL=-1) |
131 | 149 | class TestFalsePositiveHistoryLogic(DojoTestCase): |
@@ -177,6 +195,46 @@ def test_fp_history_equal_hash_code_same_test(self): |
177 | 195 | self.assert_finding(find_created_before_mark, false_p=True, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
178 | 196 | self.assert_finding(find_created_after_mark, false_p=True, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
179 | 197 |
|
| 198 | + # Candidate-filter hook (FINDING_FALSE_POSITIVE_HISTORY_CANDIDATE_FILTER_METHOD) # |
| 199 | + |
| 200 | + @override_settings( |
| 201 | + FINDING_FALSE_POSITIVE_HISTORY_CANDIDATE_FILTER_METHOD="unittests.test_false_positive_history_logic._drop_all_fp_candidates", |
| 202 | + ) |
| 203 | + def test_fp_history_hook_can_suppress_matches(self): |
| 204 | + # A plugin hook that drops all candidates must prevent FP replication even when |
| 205 | + # findings share a hash_code with an existing false-positive finding. |
| 206 | + _fp_candidate_filter_calls.clear() |
| 207 | + find_created_before_mark, find_2 = self.copy_and_reset_finding(find_id=2) |
| 208 | + find_created_before_mark.save() |
| 209 | + find_2 = Finding.objects.get(id=2) |
| 210 | + find_2.false_p = True |
| 211 | + find_2.save() |
| 212 | + find_created_after_mark, find_2 = self.copy_and_reset_finding(find_id=2) |
| 213 | + find_created_after_mark.save() |
| 214 | + # Hook discarded every candidate, so neither copy is marked despite the shared hash_code. |
| 215 | + self.assert_finding(find_created_before_mark, false_p=False, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
| 216 | + self.assert_finding(find_created_after_mark, false_p=False, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
| 217 | + # And the hook was actually invoked. |
| 218 | + self.assertTrue(_fp_candidate_filter_calls) |
| 219 | + |
| 220 | + @override_settings( |
| 221 | + FINDING_FALSE_POSITIVE_HISTORY_CANDIDATE_FILTER_METHOD="unittests.test_false_positive_history_logic._passthrough_fp_candidates", |
| 222 | + ) |
| 223 | + def test_fp_history_hook_passthrough_matches_default(self): |
| 224 | + # A passthrough hook must leave default FP-history behavior unchanged. |
| 225 | + _fp_candidate_filter_calls.clear() |
| 226 | + find_created_before_mark, find_2 = self.copy_and_reset_finding(find_id=2) |
| 227 | + find_created_before_mark.save() |
| 228 | + find_2 = Finding.objects.get(id=2) |
| 229 | + find_2.false_p = True |
| 230 | + find_2.save() |
| 231 | + find_created_after_mark, find_2 = self.copy_and_reset_finding(find_id=2) |
| 232 | + find_created_after_mark.save() |
| 233 | + # Identical outcome to test_fp_history_equal_hash_code_same_test — both copies marked. |
| 234 | + self.assert_finding(find_created_before_mark, false_p=True, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
| 235 | + self.assert_finding(find_created_after_mark, false_p=True, not_pk=2, test_id=3, hash_code=find_2.hash_code) |
| 236 | + self.assertTrue(_fp_candidate_filter_calls) |
| 237 | + |
180 | 238 | # Finding 2 in Product 2, Engagement 1, Test 3 |
181 | 239 | def test_fp_history_equal_hash_code_same_test_non_retroactive(self): |
182 | 240 | # Disable retroactive FP history |
|
0 commit comments