@@ -181,13 +181,36 @@ def add_affected(self, instances):
181181
182182 @classmethod
183183 def create_from_data (cls , dataspace , data , validate = False , affecting = None ):
184+ """Create a Vulnerability from provided ``data``."""
184185 instance = super ().create_from_data (user = dataspace , data = data , validate = False )
185186
186187 if affecting :
187188 instance .add_affected (affecting )
188189
189190 return instance
190191
192+ @classmethod
193+ def get_or_create_from_data (cls , dataspace , data , validate = False ):
194+ """Get or create a Vulnerability from provided ``data``."""
195+ vulnerability_qs = Vulnerability .objects .scope (dataspace )
196+
197+ # Support for CycloneDX data structure
198+ data = data .copy ()
199+ vulnerability_id = data .get ("vulnerability_id" ) or data .pop ("id" , None )
200+ if not vulnerability_id :
201+ return
202+ data ["vulnerability_id" ] = vulnerability_id
203+
204+ vulnerability = vulnerability_qs .get_or_none (vulnerability_id = vulnerability_id )
205+ if not vulnerability :
206+ vulnerability = cls .create_from_data (
207+ dataspace = dataspace ,
208+ data = data ,
209+ validate = validate ,
210+ )
211+
212+ return vulnerability
213+
191214 def as_cyclonedx (self , affected_instances , analysis = None ):
192215 affects = [
193216 cdx_vulnerability .BomTarget (ref = instance .cyclonedx_bom_ref )
@@ -465,28 +488,28 @@ def fetch_vulnerabilities(self):
465488 self .create_vulnerabilities (vulnerabilities_data = affected_by_vulnerabilities )
466489
467490 def create_vulnerabilities (self , vulnerabilities_data ):
491+ """Create and assign Vulnerabilities to this instance from provided vulnerabilities_data."""
468492 from component_catalog .models import Package
469493
470494 vulnerabilities = []
471- vulnerability_qs = Vulnerability .objects .scope (self .dataspace )
472-
473495 for vulnerability_data in vulnerabilities_data :
474- vulnerability_id = vulnerability_data ["vulnerability_id" ]
475- vulnerability = vulnerability_qs .get_or_none (vulnerability_id = vulnerability_id )
476- if not vulnerability :
477- vulnerability = Vulnerability .create_from_data (
478- dataspace = self .dataspace ,
479- data = vulnerability_data ,
480- )
496+ vulnerability = Vulnerability .get_or_create_from_data (
497+ dataspace = self .dataspace ,
498+ data = vulnerability_data ,
499+ )
481500 vulnerabilities .append (vulnerability )
482501
483- through_defaults = {"dataspace_id" : self .dataspace_id }
484- self .affected_by_vulnerabilities .add (* vulnerabilities , through_defaults = through_defaults )
502+ self .affected_by_vulnerabilities .add (
503+ * vulnerabilities ,
504+ through_defaults = {"dataspace_id" : self .dataspace_id },
505+ )
485506
486507 self .update_risk_score ()
487508 if isinstance (self , Package ):
488509 self .productpackages .update_weighted_risk_score ()
489510
511+ return vulnerabilities
512+
490513
491514class VulnerabilityAnalysis (
492515 VulnerabilityAnalysisMixin ,
0 commit comments