88#
99from aboutcode .pipeline import LoopProgress
1010from django .db .models import Prefetch
11- from django .db .models import Q
1211
12+ from vulnerabilities .models import AdvisoryExploit
13+ from vulnerabilities .models import AdvisoryReference
1314from vulnerabilities .models import AdvisorySeverity
1415from vulnerabilities .models import AdvisoryV2
1516from vulnerabilities .models import PackageV2
@@ -36,61 +37,92 @@ def steps(cls):
3637 )
3738
3839 def compute_and_store_vulnerability_risk_score (self ):
40+
3941 affected_advisories = (
40- AdvisoryV2 .objects .filter (impacted_packages__affecting_packages__isnull = False )
42+ AdvisoryV2 .objects .latest_per_avid ()
43+ .filter (impacted_packages__affecting_packages__isnull = False )
44+ .only ("id" )
4145 .prefetch_related (
42- "references" ,
43- "severities" ,
44- "exploits" ,
46+ Prefetch (
47+ "references" , queryset = AdvisoryReference .objects .only ("id" , "reference_type" )
48+ ),
49+ Prefetch (
50+ "severities" ,
51+ queryset = AdvisorySeverity .objects .only ("id" , "value" , "url" , "scoring_system" ),
52+ ),
53+ Prefetch ("exploits" , queryset = AdvisoryExploit .objects .only ("id" )),
4554 Prefetch (
4655 "related_advisory_severities" ,
47- queryset = AdvisoryV2 .objects .prefetch_related ("severities" ),
56+ queryset = AdvisoryV2 .objects .only ("id" ).prefetch_related (
57+ Prefetch (
58+ "severities" ,
59+ queryset = AdvisorySeverity .objects .only (
60+ "id" , "value" , "url" , "scoring_system"
61+ ),
62+ )
63+ ),
4864 ),
4965 )
5066 .distinct ()
5167 )
5268
69+ estimated_vulnerability_count = affected_advisories .count ()
70+
5371 self .log (
54- f"Calculating risk for { affected_advisories . count () :,d} advisory with a affected packages records"
72+ f"Calculating risk for { estimated_vulnerability_count :,d} advisory with a affected packages records"
5573 )
5674
57- progress = LoopProgress (total_iterations = affected_advisories .count (), logger = self .log )
75+ progress = LoopProgress (
76+ logger = self .log , total_iterations = estimated_vulnerability_count , progress_step = 5
77+ )
5878
5979 updatables = []
6080 updated_vulnerability_count = 0
6181 batch_size = 5000
6282
6383 for advisory in progress .iter (affected_advisories .iterator (chunk_size = batch_size )):
84+
6485 references = advisory .references .all ()
6586 exploits = advisory .exploits .all ()
6687
67- severities = AdvisorySeverity .objects .filter (
68- Q (advisories = advisory ) | Q (advisories__related_to_advisory_severities = advisory )
69- ).distinct ()
88+ severities = list (advisory .severities .all ())
89+
90+ for rel in advisory .related_advisory_severities .all ():
91+ severities .extend (rel .severities .all ())
7092
71- weighted_severity , exploitability = compute_vulnerability_risk_factors (
72- references = references ,
73- severities = severities ,
74- exploits = exploits ,
75- )
76- advisory .weighted_severity = weighted_severity
77- advisory .exploitability = exploitability
78- updatables .append (advisory )
93+
94+ try :
95+ weighted_severity , exploitability = compute_vulnerability_risk_factors (
96+ references = references ,
97+ severities = severities ,
98+ exploits = exploits ,
99+ )
100+
101+ advisory .weighted_severity = weighted_severity
102+ advisory .exploitability = exploitability
103+ if advisory .exploitability and advisory .weighted_severity :
104+ risk_score = min (float (advisory .exploitability * advisory .weighted_severity ), 10.0 )
105+ advisory .risk_score = round (risk_score , 1 )
106+ updatables .append (advisory )
107+ except Exception as e :
108+ self .log (f"Error computing risk score for advisory { advisory .advisory_id } : { e } " )
79109
80110 if len (updatables ) >= batch_size :
81111 updated_vulnerability_count += bulk_update (
82112 model = AdvisoryV2 ,
83113 items = updatables ,
84- fields = ["weighted_severity" , "exploitability" ],
114+ fields = ["weighted_severity" , "exploitability" , "risk_score" ],
85115 logger = self .log ,
86116 )
87-
88- updated_vulnerability_count += bulk_update (
89- model = AdvisoryV2 ,
90- items = updatables ,
91- fields = ["weighted_severity" , "exploitability" ],
92- logger = self .log ,
93- )
117+ updatables .clear ()
118+
119+ if updatables :
120+ updated_vulnerability_count += bulk_update (
121+ model = AdvisoryV2 ,
122+ items = updatables ,
123+ fields = ["weighted_severity" , "exploitability" , "risk_score" ],
124+ logger = self .log ,
125+ )
94126
95127 self .log (
96128 f"Successfully added risk score for { updated_vulnerability_count :,d} vulnerability"
@@ -109,17 +141,19 @@ def compute_and_store_package_risk_score(self):
109141
110142 updatables = []
111143 updated_package_count = 0
112- batch_size = 10000
144+ batch_size = 1000
113145
114146 for package in progress .iter (affected_packages .iterator (chunk_size = batch_size )):
115- risk_score = compute_package_risk_v2 (package )
116-
117- if not risk_score :
147+ try :
148+ risk_score = compute_package_risk_v2 (package )
149+ if not risk_score :
150+ continue
151+ package .risk_score = risk_score
152+ updatables .append (package )
153+ except Exception as e :
154+ self .log (f"Error computing risk score for package { package .purl } : { e } " )
118155 continue
119156
120- package .risk_score = risk_score
121- updatables .append (package )
122-
123157 if len (updatables ) >= batch_size :
124158 updated_package_count += bulk_update (
125159 model = PackageV2 ,
0 commit comments