1111import json
1212
1313from aboutcode .pipeline import LoopProgress
14+ from django .utils import timezone
1415
1516from vulnerabilities .models import Advisory
1617from vulnerabilities .models import AdvisoryToDo
1718from vulnerabilities .models import Alias
19+ from vulnerabilities .models import ToDoRelatedAdvisory
1820from vulnerabilities .pipelines import VulnerableCodePipeline
1921from vulnerabilities .pipes .advisory import advisories_checksum
2022
@@ -32,8 +34,14 @@ def steps(cls):
3234 )
3335
3436 def compute_individual_advisory_todo (self ):
35- advisories = Advisory .objects .all ().iterator (chunk_size = 5000 )
36- advisories_count = Advisory .objects .all ().count ()
37+ """Create ToDos for missing summary, affected and fixed packages."""
38+
39+ advisories = Advisory .objects .all ()
40+ advisories_count = advisories .count ()
41+ advisory_relation_to_create = {}
42+ todo_to_create = []
43+ new_todos_count = 0
44+ batch_size = 5000
3745
3846 self .log (
3947 f"Checking missing summary, affected and fixed packages in { advisories_count } Advisories"
@@ -43,23 +51,48 @@ def compute_individual_advisory_todo(self):
4351 logger = self .log ,
4452 progress_step = 1 ,
4553 )
46- for advisory in progress .iter (advisories ):
54+ for advisory in progress .iter (advisories . iterator ( chunk_size = 5000 ) ):
4755 advisory_todo_id = advisories_checksum (advisories = advisory )
4856 check_missing_summary (
4957 advisory = advisory ,
5058 todo_id = advisory_todo_id ,
51- logger = self .log ,
59+ todo_to_create = todo_to_create ,
60+ advisory_relation_to_create = advisory_relation_to_create ,
5261 )
5362
5463 check_missing_affected_and_fixed_by_packages (
5564 advisory = advisory ,
5665 todo_id = advisory_todo_id ,
57- logger = self .log ,
66+ todo_to_create = todo_to_create ,
67+ advisory_relation_to_create = advisory_relation_to_create ,
5868 )
5969
70+ if len (todo_to_create ) > batch_size :
71+ new_todos_count += bulk_create_with_m2m (
72+ todos = todo_to_create ,
73+ advisories = advisory_relation_to_create ,
74+ logger = self .log ,
75+ )
76+ advisory_relation_to_create .clear ()
77+ todo_to_create .clear ()
78+
79+ new_todos_count += bulk_create_with_m2m (
80+ todos = todo_to_create ,
81+ advisories = advisory_relation_to_create ,
82+ logger = self .log ,
83+ )
84+
6085 def detect_conflicting_advisories (self ):
86+ """
87+ Create ToDos for advisories with conflicting opinions on fixed and affected
88+ package versions for a vulnerability.
89+ """
6190 aliases = Alias .objects .filter (alias__istartswith = "cve" )
6291 aliases_count = aliases .count ()
92+ advisory_relation_to_create = {}
93+ todo_to_create = []
94+ new_todos_count = 0
95+ batch_size = 5000
6396
6497 self .log (f"Cross validating advisory affected and fixed package for { aliases_count } CVEs" )
6598
@@ -73,24 +106,50 @@ def detect_conflicting_advisories(self):
73106 advisory_todos__issue_type = "MISSING_AFFECTED_AND_FIXED_BY_PACKAGES"
74107 ).distinct ()
75108
76- check_conflicting_affected_and_fixed_by_packages (
109+ check_conflicting_affected_and_fixed_by_packages_for_alias (
77110 advisories = advisories ,
78111 cve = alias ,
79- logger = self .log ,
112+ todo_to_create = todo_to_create ,
113+ advisory_relation_to_create = advisory_relation_to_create ,
80114 )
81115
116+ if len (todo_to_create ) > batch_size :
117+ new_todos_count += bulk_create_with_m2m (
118+ todos = todo_to_create ,
119+ advisories = advisory_relation_to_create ,
120+ logger = self .log ,
121+ )
122+ advisory_relation_to_create .clear ()
123+ todo_to_create .clear ()
124+
125+ new_todos_count += bulk_create_with_m2m (
126+ todos = todo_to_create ,
127+ advisories = advisory_relation_to_create ,
128+ logger = self .log ,
129+ )
82130
83- def check_missing_summary (advisory , todo_id , logger = None ):
131+
132+ def check_missing_summary (
133+ advisory ,
134+ todo_id ,
135+ todo_to_create ,
136+ advisory_relation_to_create ,
137+ ):
84138 if not advisory .summary :
85- todo , created = AdvisoryToDo . objects . get_or_create (
139+ todo = AdvisoryToDo (
86140 related_advisories_id = todo_id ,
87141 issue_type = "MISSING_SUMMARY" ,
88142 )
89- if created :
90- todo . advisories . add ( advisory )
143+ advisory_relation_to_create [ todo_id ] = [ advisory ]
144+ todo_to_create . append ( todo )
91145
92146
93- def check_missing_affected_and_fixed_by_packages (advisory , todo_id , logger = None ):
147+ def check_missing_affected_and_fixed_by_packages (
148+ advisory ,
149+ todo_id ,
150+ todo_to_create ,
151+ advisory_relation_to_create ,
152+ ):
94153 """
95154 Check for missing affected or fixed-by packages in the advisory
96155 and create appropriate AdvisoryToDo.
@@ -121,15 +180,21 @@ def check_missing_affected_and_fixed_by_packages(advisory, todo_id, logger=None)
121180 issue_type = "MISSING_AFFECTED_PACKAGE"
122181 elif not has_fixed_package :
123182 issue_type = "MISSING_FIXED_BY_PACKAGE"
124- todo , created = AdvisoryToDo .objects .get_or_create (
183+
184+ todo = AdvisoryToDo (
125185 related_advisories_id = todo_id ,
126186 issue_type = issue_type ,
127187 )
128- if created :
129- todo . advisories . add ( advisory )
188+ todo_to_create . append ( todo )
189+ advisory_relation_to_create [ todo_id ] = [ advisory ]
130190
131191
132- def check_conflicting_affected_and_fixed_by_packages (advisories , cve , logger = None ):
192+ def check_conflicting_affected_and_fixed_by_packages_for_alias (
193+ advisories ,
194+ cve ,
195+ todo_to_create ,
196+ advisory_relation_to_create ,
197+ ):
133198 """
134199 Add appropriate AdvisoryToDo for conflicting affected/fixed packages.
135200
@@ -222,15 +287,13 @@ def check_conflicting_affected_and_fixed_by_packages(advisories, cve, logger=Non
222287 messages .append ("Comparison matrix:" )
223288 messages .append (json .dumps (matrix , indent = 2 , default = list ))
224289 todo_id = advisories_checksum (advisories )
225- todo , created = AdvisoryToDo . objects . get_or_create (
290+ todo = AdvisoryToDo (
226291 related_advisories_id = todo_id ,
227292 issue_type = issue_type ,
228- defaults = {
229- "issue_detail" : "\n " .join (messages ),
230- },
293+ issue_detail = "\n " .join (messages ),
231294 )
232- if created :
233- todo . advisories . add ( * advisories )
295+ todo_to_create . append ( todo )
296+ advisory_relation_to_create [ todo_id ] = list ( advisories )
234297
235298
236299def initialize_sub_matrix (matrix , affected_purl , advisory ):
@@ -245,3 +308,30 @@ def initialize_sub_matrix(matrix, affected_purl, advisory):
245308 matrix [affected_purl ]["affected" ][advisory_id ] = set ()
246309 if advisory not in matrix [affected_purl ]["fixed" ]:
247310 matrix [affected_purl ]["fixed" ][advisory_id ] = set ()
311+
312+
313+ def bulk_create_with_m2m (todos , advisories , logger ):
314+ """Bulk create ToDos and also bulk create M2M ToDo Advisory relationships."""
315+ if not todos :
316+ return 0
317+
318+ start_time = timezone .now ()
319+ try :
320+ AdvisoryToDo .objects .bulk_create (objs = todos , ignore_conflicts = True )
321+ except Exception as e :
322+ logger (f"Error creating AdvisoryToDo: { e } " )
323+
324+ new_todos = AdvisoryToDo .objects .filter (created_at__gte = start_time )
325+
326+ relations = [
327+ ToDoRelatedAdvisory (todo = todo , advisory = advisory )
328+ for todo in new_todos
329+ for advisory in advisories [todo .related_advisories_id ]
330+ ]
331+
332+ try :
333+ ToDoRelatedAdvisory .objects .bulk_create (relations )
334+ except Exception as e :
335+ logger (f"Error creating Advisory ToDo relations: { e } " )
336+
337+ return new_todos .count ()
0 commit comments