Skip to content

Commit 57a7b4d

Browse files
committed
refactor the signal related to preset analysis
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent ee6bdb4 commit 57a7b4d

5 files changed

Lines changed: 62 additions & 21 deletions

File tree

product_portfolio/views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
from vulnerabilities.models import Vulnerability
151151
from vulnerabilities.models import VulnerabilityAnalysis
152152
from vulnerabilities.models import get_risk_level
153+
from vulnerabilities.triage.engine import delete_triage_records_for_assignment
153154
from vulnerabilities.triage.engine import reevaluate_product_rulesets
154155
from vulnerabilities.triage.models import AnalysisPreset
155156
from vulnerabilities.triage.models import ProductTriageRuleset
@@ -2262,6 +2263,9 @@ def manage_triage_rulesets_view(request, dataspace, name, version=""):
22622263
for ruleset_uuid, assignment in current_assignments.items():
22632264
if ruleset_uuid not in submitted_uuids:
22642265
assignment.delete()
2266+
delete_triage_records_for_assignment(
2267+
ruleset=assignment.ruleset, product=product
2268+
)
22652269
reevaluate_product_rulesets(product)
22662270
return JsonResponse({"success": True})
22672271

vulnerabilities/triage/admin.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from dje.admin import DataspacedAdmin
1414
from dje.admin import dejacode_site
1515
from dje.list_display import AsLink
16+
from vulnerabilities.triage.engine import delete_preset_analyses_before_ruleset_delete
1617
from vulnerabilities.triage.engine import evaluate_ruleset
1718
from vulnerabilities.triage.forms import AnalysisPresetForm
1819
from vulnerabilities.triage.forms import TriageRulesetForm
@@ -146,6 +147,15 @@ def save_model(self, request, obj, form, change):
146147
for assignment in obj.product_triage_rulesets.select_related("product"):
147148
evaluate_ruleset(ruleset=obj, product=assignment.product)
148149

150+
def delete_model(self, request, obj):
151+
delete_preset_analyses_before_ruleset_delete(obj)
152+
super().delete_model(request, obj)
153+
154+
def delete_queryset(self, request, queryset):
155+
for obj in queryset:
156+
delete_preset_analyses_before_ruleset_delete(obj)
157+
super().delete_queryset(request, queryset)
158+
149159
def get_form(self, request, obj=None, change=False, **kwargs):
150160
kwargs["fields"] = [
151161
"name",

vulnerabilities/triage/engine.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,24 @@ def delete_preset_analyses_for_product(preset_id, product, vulnerability_ids):
130130
).delete()
131131

132132

133+
def delete_triage_records_for_assignment(ruleset, product):
134+
"""
135+
Delete triage records and associated preset analyses for a ruleset being
136+
unassigned from a product.
137+
"""
138+
matching_records = TriageRecord.objects.filter(ruleset=ruleset, product=product)
139+
stale_vulnerability_ids = list(matching_records.values_list("vulnerability_id", flat=True))
140+
# Records with an open Request are kept so reassigning the ruleset reconnects to it
141+
# instead of opening a duplicate Request.
142+
matching_records.filter(request__isnull=True).delete()
143+
if ruleset.analysis_preset_id and stale_vulnerability_ids:
144+
delete_preset_analyses_for_product(
145+
preset_id=ruleset.analysis_preset_id,
146+
product=product,
147+
vulnerability_ids=stale_vulnerability_ids,
148+
)
149+
150+
133151
def sync_triage_records(ruleset, product, matched_rules_per_vulnerability_id, apply_preset=True):
134152
"""
135153
Create or update one TriageRecord per matching vulnerability, then
@@ -228,6 +246,33 @@ def evaluate_ruleset(ruleset, product, apply_preset=True):
228246
)
229247

230248

249+
def delete_preset_analyses_before_ruleset_delete(ruleset):
250+
"""
251+
Delete VulnerabilityAnalysis records applied by the ruleset's preset, for every
252+
product currently assigned to it.
253+
254+
Must run before the ruleset itself is deleted: its TriageRecords have no signal of
255+
their own, so Django cascade-deletes them via a fast bulk query before a post_delete
256+
signal on the ruleset or its assignments would even get a chance to see them.
257+
"""
258+
if not ruleset.analysis_preset_id:
259+
return
260+
261+
assignments = ProductTriageRuleset.objects.filter(ruleset=ruleset).select_related("product")
262+
for assignment in assignments:
263+
vulnerability_ids = list(
264+
TriageRecord.objects.filter(
265+
ruleset=ruleset, product=assignment.product
266+
).values_list("vulnerability_id", flat=True)
267+
)
268+
if vulnerability_ids:
269+
delete_preset_analyses_for_product(
270+
preset_id=ruleset.analysis_preset_id,
271+
product=assignment.product,
272+
vulnerability_ids=vulnerability_ids,
273+
)
274+
275+
231276
def reevaluate_product_rulesets(product, apply_preset=True):
232277
"""Re-evaluate all enabled triage rulesets currently assigned to the product."""
233278
assignments = ProductTriageRuleset.objects.filter(

vulnerabilities/triage/management/commands/create_triage_rulesets.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from django.core.management.base import CommandError
1111

1212
from dje.models import Dataspace
13+
from vulnerabilities.triage.engine import delete_preset_analyses_before_ruleset_delete
1314
from vulnerabilities.triage.models import AnalysisPreset
1415
from vulnerabilities.triage.models import TriageAction
1516
from vulnerabilities.triage.models import TriageRuleset
@@ -179,6 +180,8 @@ def handle(self, *args, **options):
179180
self.stdout.write("Reset cancelled.")
180181
return
181182

183+
for ruleset in TriageRuleset.objects.filter(dataspace=dataspace):
184+
delete_preset_analyses_before_ruleset_delete(ruleset)
182185
deleted_rulesets, _ = TriageRuleset.objects.filter(dataspace=dataspace).delete()
183186
deleted_presets, _ = AnalysisPreset.objects.filter(dataspace=dataspace).delete()
184187
self.stdout.write(

vulnerabilities/triage/signals.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,7 @@
1010
from django.db.models.signals import post_save
1111
from django.dispatch import receiver
1212

13-
from vulnerabilities.triage.engine import delete_preset_analyses_for_product
1413
from vulnerabilities.triage.engine import reevaluate_product_rulesets
15-
from vulnerabilities.triage.models import TriageRecord
16-
17-
18-
@receiver(post_delete, sender="vulnerabilities_triage.ProductTriageRuleset")
19-
def delete_triage_records_on_unassign(sender, instance, **kwargs):
20-
"""Delete triage records and associated preset analyses when a ruleset is de-assigned."""
21-
matching_records = TriageRecord.objects.filter(
22-
ruleset=instance.ruleset,
23-
product=instance.product,
24-
)
25-
stale_vulnerability_ids = list(matching_records.values_list("vulnerability_id", flat=True))
26-
# Records with an open Request are kept so reassigning the ruleset reconnects to it
27-
# instead of opening a duplicate Request.
28-
matching_records.filter(request__isnull=True).delete()
29-
if instance.ruleset.analysis_preset_id and stale_vulnerability_ids:
30-
delete_preset_analyses_for_product(
31-
preset_id=instance.ruleset.analysis_preset_id,
32-
product=instance.product,
33-
vulnerability_ids=stale_vulnerability_ids,
34-
)
3514

3615

3716
@receiver([post_save, post_delete], sender="vulnerabilities.VulnerabilityAnalysis")

0 commit comments

Comments
 (0)