66# See https://aboutcode.org for more information about AboutCode FOSS projects.
77#
88
9+ import decimal
910import logging
1011import re
1112from contextlib import suppress
3738from cyclonedx .model import component as cyclonedx_component
3839from cyclonedx .model import contact as cyclonedx_contact
3940from cyclonedx .model import license as cyclonedx_license
41+ from cyclonedx .model import vulnerability as cdx_vulnerability
4042from license_expression import ExpressionError
4143from packageurl import PackageURL
4244from packageurl .contrib import purl2url
@@ -930,7 +932,7 @@ def as_cyclonedx(self, license_expression_spdx=None):
930932 name = self .name ,
931933 type = component_type ,
932934 version = self .version ,
933- bom_ref = str ( self .uuid ) ,
935+ bom_ref = self .cyclonedx_bom_ref ,
934936 supplier = supplier ,
935937 licenses = licenses ,
936938 copyright = self .copyright ,
@@ -1324,6 +1326,10 @@ def details_url(self):
13241326 def get_extra_relational_fields ():
13251327 return ["external_references" ]
13261328
1329+ @property
1330+ def cyclonedx_bom_ref (self ):
1331+ return str (self .uuid )
1332+
13271333 @property
13281334 def permission_protected_fields (self ):
13291335 return {"usage_policy" : "change_usage_policy_on_component" }
@@ -2361,6 +2367,10 @@ def as_spdx(self, license_concluded=None):
23612367 def get_spdx_packages (self ):
23622368 return [self ]
23632369
2370+ @property
2371+ def cyclonedx_bom_ref (self ):
2372+ return self .package_url or str (self .uuid )
2373+
23642374 def as_cyclonedx (self , license_expression_spdx = None ):
23652375 """Return this Package as an CycloneDX Component entry."""
23662376 expression_spdx = license_expression_spdx or self .concluded_license_expression_spdx
@@ -2383,12 +2393,11 @@ def as_cyclonedx(self, license_expression_spdx=None):
23832393 if (hash_value := getattr (self , field_name ))
23842394 ]
23852395
2386- package_url = self .get_package_url ()
23872396 return cyclonedx_component .Component (
23882397 name = self .name ,
23892398 version = self .version ,
2390- bom_ref = str ( package_url ) or str ( self .uuid ) ,
2391- purl = package_url ,
2399+ bom_ref = self .cyclonedx_bom_ref ,
2400+ purl = self . get_package_url () ,
23922401 licenses = licenses ,
23932402 copyright = self .copyright ,
23942403 description = self .description ,
@@ -2747,3 +2756,62 @@ def get_severity_scores(severities):
27472756 consolidated_scores .extend (score_range )
27482757
27492758 return consolidated_scores
2759+
2760+ def as_cyclonedx (self , affected_instances ):
2761+ affects = [
2762+ cdx_vulnerability .BomTarget (ref = instance .cyclonedx_bom_ref )
2763+ for instance in affected_instances
2764+ ]
2765+
2766+ source_url = f"https://public.vulnerablecode.io/vulnerabilities/{ self .vulnerability_id } "
2767+ source = cdx_vulnerability .VulnerabilitySource (
2768+ name = "VulnerableCode" ,
2769+ url = source_url ,
2770+ )
2771+
2772+ references = []
2773+ ratings = []
2774+ for reference in self .references :
2775+ reference_source = cdx_vulnerability .VulnerabilitySource (
2776+ url = reference .get ("reference_url" ),
2777+ )
2778+ references .append (
2779+ cdx_vulnerability .VulnerabilityReference (
2780+ id = reference .get ("reference_id" ),
2781+ source = reference_source ,
2782+ )
2783+ )
2784+
2785+ for score_entry in reference .get ("scores" , []):
2786+ # CycloneDX only support a float value for the score field,
2787+ # where on the VulnerableCode data it can be either a score float value
2788+ # or a severity string value.
2789+ score_value = score_entry .get ("value" )
2790+ try :
2791+ score = decimal .Decimal (score_value )
2792+ severity = None
2793+ except decimal .DecimalException :
2794+ score = None
2795+ severity = getattr (
2796+ cdx_vulnerability .VulnerabilitySeverity ,
2797+ score_value .upper (),
2798+ None ,
2799+ )
2800+
2801+ ratings .append (
2802+ cdx_vulnerability .VulnerabilityRating (
2803+ source = reference_source ,
2804+ score = score ,
2805+ severity = severity ,
2806+ vector = score_entry .get ("scoring_elements" ),
2807+ )
2808+ )
2809+
2810+ return cdx_vulnerability .Vulnerability (
2811+ id = self .vulnerability_id ,
2812+ source = source ,
2813+ description = self .summary ,
2814+ affects = affects ,
2815+ references = sorted (references ),
2816+ ratings = ratings ,
2817+ )
0 commit comments