99
1010from urllib .parse import unquote
1111
12+ from cvss .exceptions import CVSS2MalformedError
13+ from cvss .exceptions import CVSS3MalformedError
14+ from cvss .exceptions import CVSS4MalformedError
1215from django .db .models import Prefetch
1316from django_filters import rest_framework as filters
1417from drf_spectacular .utils import extend_schema
15- from drf_spectacular .utils import inline_serializer
1618from packageurl import PackageURL
1719from packageurl import normalize_qualifiers
1820from rest_framework import serializers
3234from vulnerabilities .models import VulnerabilitySeverity
3335from vulnerabilities .models import Weakness
3436from vulnerabilities .models import get_purl_query_lookups
37+ from vulnerabilities .severity_systems import EPSS
38+ from vulnerabilities .severity_systems import SCORING_SYSTEMS
3539from vulnerabilities .throttling import StaffUserRateThrottle
40+ from vulnerabilities .utils import get_severity_range
3641
3742
3843class VulnerabilitySeveritySerializer (serializers .ModelSerializer ):
@@ -82,29 +87,23 @@ def get_resource_url(self, instance):
8287 return resource_url
8388
8489
85- class MinimalPackageSerializer ( BaseResourceSerializer ):
90+ class VulnVulnIDSerializer ( serializers . Serializer ):
8691 """
87- Used for nesting inside vulnerability focused APIs .
92+ Serializer for the series of vulnerability IDs .
8893 """
8994
90- def get_affected_vulnerabilities (self , package ):
91- parent_affected_vulnerabilities = package .fixed_package_details .get ("vulnerabilities" ) or []
92-
93- affected_vulnerabilities = [
94- self .get_vulnerability (vuln ) for vuln in parent_affected_vulnerabilities
95- ]
95+ vulnerability = serializers .CharField (source = "vulnerability_id" )
9696
97- return affected_vulnerabilities
97+ class Meta :
98+ fields = ["vulnerability" ]
9899
99- def get_vulnerability (self , vuln ):
100- affected_vulnerability = {}
101100
102- vulnerability = vuln . get ( "vulnerability" )
103- if vulnerability :
104- affected_vulnerability [ " vulnerability" ] = vulnerability . vulnerability_id
105- return affected_vulnerability
101+ class MinimalPackageSerializer ( BaseResourceSerializer ):
102+ """
103+ Used for nesting inside vulnerability focused APIs.
104+ """
106105
107- affected_by_vulnerabilities = serializers . SerializerMethodField ( "get_affected_vulnerabilities" )
106+ affected_by_vulnerabilities = VulnVulnIDSerializer ( source = "affecting_vulns" , many = True )
108107
109108 purl = serializers .CharField (source = "package_url" )
110109
@@ -140,18 +139,17 @@ class VulnSerializerRefsAndSummary(BaseResourceSerializer):
140139 Lookup vulnerabilities references by aliases (such as a CVE).
141140 """
142141
143- def to_representation (self , instance ):
144- data = super ().to_representation (instance )
145- aliases = [alias ["alias" ] for alias in data ["aliases" ]]
146- data ["aliases" ] = aliases
147- return data
148-
149142 fixed_packages = MinimalPackageSerializer (
150143 many = True , source = "filtered_fixed_packages" , read_only = True
151144 )
152145
153146 references = VulnerabilityReferenceSerializer (many = True , source = "vulnerabilityreference_set" )
154- aliases = AliasSerializer (many = True , source = "alias" )
147+
148+ aliases = serializers .SerializerMethodField ()
149+
150+ def get_aliases (self , obj ):
151+ # Assuming `obj.aliases` is a queryset of `Alias` objects
152+ return [alias .alias for alias in obj .aliases .all ()]
155153
156154 class Meta :
157155 model = Vulnerability
@@ -193,6 +191,7 @@ class VulnerabilitySerializer(BaseResourceSerializer):
193191 aliases = AliasSerializer (many = True , source = "alias" )
194192 kev = KEVSerializer (read_only = True )
195193 weaknesses = WeaknessSerializer (many = True )
194+ severity_range_score = serializers .SerializerMethodField ()
196195
197196 def to_representation (self , instance ):
198197 data = super ().to_representation (instance )
@@ -206,6 +205,30 @@ def to_representation(self, instance):
206205
207206 return data
208207
208+ def get_severity_range_score (self , instance ):
209+ severity_vectors = []
210+ severity_values = set ()
211+ for s in instance .severities :
212+ if s .scoring_system == EPSS .identifier :
213+ continue
214+
215+ if s .scoring_elements and s .scoring_system in SCORING_SYSTEMS :
216+ try :
217+ vector_values = SCORING_SYSTEMS [s .scoring_system ].get (s .scoring_elements )
218+ severity_vectors .append (vector_values )
219+ except (
220+ CVSS2MalformedError ,
221+ CVSS3MalformedError ,
222+ CVSS4MalformedError ,
223+ NotImplementedError ,
224+ ):
225+ pass
226+
227+ if s .value :
228+ severity_values .add (s .value )
229+ severity_range = get_severity_range (severity_values )
230+ return severity_range
231+
209232 class Meta :
210233 model = Vulnerability
211234 fields = [
@@ -218,6 +241,7 @@ class Meta:
218241 "references" ,
219242 "weaknesses" ,
220243 "kev" ,
244+ "severity_range_score" ,
221245 ]
222246
223247
@@ -226,34 +250,22 @@ class PackageSerializer(BaseResourceSerializer):
226250 Lookup software package using Package URLs
227251 """
228252
229- def to_representation (self , instance ):
230- data = super ().to_representation (instance )
231- data ["qualifiers" ] = normalize_qualifiers (data ["qualifiers" ], encode = False )
232-
233- return data
234-
235- next_non_vulnerable_version = serializers .SerializerMethodField ("get_next_non_vulnerable" )
236-
237- def get_next_non_vulnerable (self , package ):
238- next_non_vulnerable = package .fixed_package_details .get ("next_non_vulnerable" , None )
239- if next_non_vulnerable :
240- return next_non_vulnerable .version
241-
242- latest_non_vulnerable_version = serializers .SerializerMethodField ("get_latest_non_vulnerable" )
243-
244- def get_latest_non_vulnerable (self , package ):
245- latest_non_vulnerable = package .fixed_package_details .get ("latest_non_vulnerable" , None )
246- if latest_non_vulnerable :
247- return latest_non_vulnerable .version
253+ next_non_vulnerable_version = serializers .CharField (read_only = True )
254+ latest_non_vulnerable_version = serializers .CharField (read_only = True )
248255
249256 purl = serializers .CharField (source = "package_url" )
250257
251258 affected_by_vulnerabilities = serializers .SerializerMethodField ("get_affected_vulnerabilities" )
252259
253260 fixing_vulnerabilities = serializers .SerializerMethodField ("get_fixing_vulnerabilities" )
254261
262+ qualifiers = serializers .SerializerMethodField ()
263+
255264 is_vulnerable = serializers .BooleanField ()
256265
266+ def get_qualifiers (self , package ):
267+ return normalize_qualifiers (package .qualifiers , encode = False )
268+
257269 def get_fixed_packages (self , package ):
258270 """
259271 Return a queryset of all packages that fix a vulnerability with
@@ -337,8 +349,6 @@ class Meta:
337349 "fixing_vulnerabilities" ,
338350 ]
339351
340- is_vulnerable = serializers .BooleanField ()
341-
342352
343353class PackageFilterSet (filters .FilterSet ):
344354 purl = filters .CharFilter (method = "filter_purl" )
0 commit comments