88#
99
1010
11+ from django_filters import rest_framework as filters
1112from drf_spectacular .utils import OpenApiParameter
1213from drf_spectacular .utils import extend_schema
1314from drf_spectacular .utils import extend_schema_view
1920from rest_framework .response import Response
2021from rest_framework .reverse import reverse
2122
23+ from vulnerabilities .api import PackageFilterSet
24+ from vulnerabilities .api import VulnerabilitySeveritySerializer
2225from vulnerabilities .models import Package
2326from vulnerabilities .models import Vulnerability
2427from vulnerabilities .models import VulnerabilityReference
@@ -210,9 +213,19 @@ class LookupRequestSerializer(serializers.Serializer):
210213 )
211214
212215
216+ class PackageV2FilterSet (filters .FilterSet ):
217+ affected_by_vulnerability = filters .CharFilter (
218+ field_name = "affected_by_vulnerabilities__vulnerability_id"
219+ )
220+ fixing_vulnerability = filters .CharFilter (field_name = "fixing_vulnerabilities__vulnerability_id" )
221+ purl = filters .CharFilter (field_name = "package_url" )
222+
223+
213224class PackageV2ViewSet (viewsets .ReadOnlyModelViewSet ):
214225 queryset = Package .objects .all ()
215226 serializer_class = PackageV2Serializer
227+ filter_backends = (filters .DjangoFilterBackend ,)
228+ filterset_class = PackageV2FilterSet
216229
217230 def get_queryset (self ):
218231 queryset = super ().get_queryset ()
@@ -234,18 +247,45 @@ def get_queryset(self):
234247
235248 def list (self , request , * args , ** kwargs ):
236249 queryset = self .get_queryset ()
250+
237251 # Apply pagination
238252 page = self .paginate_queryset (queryset )
239253 if page is not None :
254+ # Collect only vulnerabilities for packages in the current page
255+ vulnerabilities = set ()
256+ for package in page :
257+ vulnerabilities .update (package .affected_by_vulnerabilities .all ())
258+ vulnerabilities .update (package .fixing_vulnerabilities .all ())
259+
260+ # Serialize the vulnerabilities with vulnerability_id as keys
261+ vulnerability_data = {
262+ vuln .vulnerability_id : VulnerabilityV2Serializer (vuln ).data
263+ for vuln in vulnerabilities
264+ }
265+
266+ # Serialize the current page of packages
240267 serializer = self .get_serializer (page , many = True )
241268 data = serializer .data
269+
242270 # Use 'self.get_paginated_response' to include pagination data
243- return self .get_paginated_response ({"packages" : data })
271+ return self .get_paginated_response (
272+ {"vulnerabilities" : vulnerability_data , "packages" : data }
273+ )
274+
275+ # If pagination is not applied, collect vulnerabilities for all packages
276+ vulnerabilities = set ()
277+ for package in queryset :
278+ vulnerabilities .update (package .affected_by_vulnerabilities .all ())
279+ vulnerabilities .update (package .fixing_vulnerabilities .all ())
280+
281+ vulnerability_data = {
282+ vuln .vulnerability_id : VulnerabilityV2Serializer (vuln ).data for vuln in vulnerabilities
283+ }
244284
245- # If pagination is not applied
285+ # Serialize all packages when pagination is not applied
246286 serializer = self .get_serializer (queryset , many = True )
247287 data = serializer .data
248- return Response ({"packages" : data })
288+ return Response ({"vulnerabilities" : vulnerability_data , " packages" : data })
249289
250290 @extend_schema (
251291 request = PackageurlListSerializer ,
@@ -274,12 +314,32 @@ def bulk_lookup(self, request):
274314 validated_data = serializer .validated_data
275315 purls = validated_data .get ("purls" )
276316
317+ # Fetch packages matching the provided purls
318+ packages = Package .objects .for_purls (purls ).with_is_vulnerable ()
319+
320+ # Collect vulnerabilities associated with these packages
321+ vulnerabilities = set ()
322+ for package in packages :
323+ vulnerabilities .update (package .affected_by_vulnerabilities .all ())
324+ vulnerabilities .update (package .fixing_vulnerabilities .all ())
325+
326+ # Serialize vulnerabilities with vulnerability_id as keys
327+ vulnerability_data = {
328+ vuln .vulnerability_id : VulnerabilityV2Serializer (vuln ).data for vuln in vulnerabilities
329+ }
330+
331+ # Serialize packages
332+ package_data = PackageV2Serializer (
333+ packages ,
334+ many = True ,
335+ context = {"request" : request },
336+ ).data
337+
277338 return Response (
278- PackageV2Serializer (
279- Package .objects .for_purls (purls ).with_is_vulnerable (),
280- many = True ,
281- context = {"request" : request },
282- ).data
339+ {
340+ "vulnerabilities" : vulnerability_data ,
341+ "packages" : package_data ,
342+ }
283343 )
284344
285345 @extend_schema (
@@ -331,22 +391,58 @@ def bulk_search(self, request):
331391 .with_is_vulnerable ()
332392 )
333393
394+ packages = query
395+
396+ # Collect vulnerabilities associated with these packages
397+ vulnerabilities = set ()
398+ for package in packages :
399+ vulnerabilities .update (package .affected_by_vulnerabilities .all ())
400+ vulnerabilities .update (package .fixing_vulnerabilities .all ())
401+
402+ vulnerability_data = {
403+ vuln .vulnerability_id : VulnerabilityV2Serializer (vuln ).data
404+ for vuln in vulnerabilities
405+ }
406+
334407 if not purl_only :
408+ package_data = PackageV2Serializer (
409+ packages , many = True , context = {"request" : request }
410+ ).data
335411 return Response (
336- PackageV2Serializer (query , many = True , context = {"request" : request }).data
412+ {
413+ "vulnerabilities" : vulnerability_data ,
414+ "packages" : package_data ,
415+ }
337416 )
338417
339- # using order by and distinct because there will be
418+ # Using order by and distinct because there will be
340419 # many fully qualified purl for a single plain purl
341420 vulnerable_purls = query .vulnerable ().only ("plain_package_url" )
342421 vulnerable_purls = [str (package .plain_package_url ) for package in vulnerable_purls ]
343422 return Response (data = vulnerable_purls )
344423
345424 query = Package .objects .filter (package_url__in = purls ).distinct ().with_is_vulnerable ()
425+ packages = query
426+
427+ # Collect vulnerabilities associated with these packages
428+ vulnerabilities = set ()
429+ for package in packages :
430+ vulnerabilities .update (package .affected_by_vulnerabilities .all ())
431+ vulnerabilities .update (package .fixing_vulnerabilities .all ())
432+
433+ vulnerability_data = {
434+ vuln .vulnerability_id : VulnerabilityV2Serializer (vuln ).data for vuln in vulnerabilities
435+ }
346436
347437 if not purl_only :
438+ package_data = PackageV2Serializer (
439+ packages , many = True , context = {"request" : request }
440+ ).data
348441 return Response (
349- PackageV2Serializer (query , many = True , context = {"request" : request }).data
442+ {
443+ "vulnerabilities" : vulnerability_data ,
444+ "packages" : package_data ,
445+ }
350446 )
351447
352448 vulnerable_purls = query .vulnerable ().only ("package_url" )
0 commit comments