@@ -332,7 +332,7 @@ def get_fixing_vulnerabilities(self, obj):
332332 return [vuln .vulnerability_id for vuln in obj .fixing_vulnerabilities .all ()]
333333
334334
335- class AdvisoryPackageV2Serializer (serializers .ModelSerializer ):
335+ class PackageV3Serializer (serializers .ModelSerializer ):
336336 purl = serializers .CharField (source = "package_url" )
337337 risk_score = serializers .FloatField (read_only = True )
338338 affected_by_vulnerabilities = serializers .SerializerMethodField ()
@@ -353,26 +353,38 @@ class Meta:
353353
354354 def get_affected_by_vulnerabilities (self , package ):
355355 """Return a dictionary with advisory as keys and their details, including fixed_by_packages."""
356+ impacts = package .affected_in_impacts .select_related ("advisory" ).prefetch_related (
357+ "fixed_by_packages"
358+ )
359+
360+ avids = {impact .advisory .avid for impact in impacts if impact .advisory_id }
361+
362+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
363+ advisory_by_avid = {adv .avid : adv for adv in latest_advisories }
364+
356365 result = {}
357- request = self .context .get ("request" )
358- for impact in package .affected_in_impacts .all ():
359- advisory = impact .advisory
366+
367+ for impact in impacts :
368+ avid = impact .advisory .avid
369+ advisory = advisory_by_avid .get (avid )
370+ if not advisory :
371+ continue
360372 fixed_by_packages = [pkg .purl for pkg in impact .fixed_by_packages .all ()]
361- code_fixes = CodeFixV2 .objects .filter (advisory = advisory ).distinct ()
362- code_fix_urls = [
363- reverse ("advisory-codefix-detail" , args = [code_fix .id ], request = request )
364- for code_fix in code_fixes
365- ]
366373 result [advisory .avid ] = {
367374 "advisory_id" : advisory .avid ,
368375 "fixed_by_packages" : fixed_by_packages ,
369- "code_fixes" : code_fix_urls ,
370376 }
371377
372378 return result
373379
374380 def get_fixing_vulnerabilities (self , package ):
375- return [impact .advisory .avid for impact in package .fixed_in_impacts .all ()]
381+ impacts = package .fixed_in_impacts .select_related ("advisory" )
382+
383+ avids = {impact .advisory .avid for impact in impacts if impact .advisory_id }
384+
385+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
386+
387+ return [adv .avid for adv in latest_advisories ]
376388
377389 def get_next_non_vulnerable_version (self , package ):
378390 if next_non_vulnerable := package .get_non_vulnerable_versions ()[0 ]:
@@ -1013,9 +1025,9 @@ def get_view_name(self):
10131025 return "Pipeline Jobs"
10141026
10151027
1016- class AdvisoriesPackageV2ViewSet (viewsets .ReadOnlyModelViewSet ):
1028+ class PackageV3ViewSet (viewsets .ReadOnlyModelViewSet ):
10171029 queryset = PackageV2 .objects .all ()
1018- serializer_class = AdvisoryPackageV2Serializer
1030+ serializer_class = PackageV3Serializer
10191031 filter_backends = [filters .DjangoFilterBackend ]
10201032 filterset_class = AdvisoryPackageV2FilterSet
10211033
@@ -1039,35 +1051,42 @@ def get_queryset(self):
10391051 )
10401052
10411053 def list (self , request , * args , ** kwargs ):
1042- filtered_queryset = self .filter_queryset (self .get_queryset ())
1043- page = self .paginate_queryset (filtered_queryset )
1054+ queryset = self .filter_queryset (self .get_queryset ())
1055+ page = self .paginate_queryset (queryset )
10441056
1045- advisories = set ()
1046- if page is not None :
1047- for package in page :
1048- advisories .update ({impact .advisory for impact in package .affected_in_impacts .all ()})
1049- advisories .update ({impact .advisory for impact in package .fixed_in_impacts .all ()})
1057+ packages = page if page is not None else queryset
10501058
1051- # Serialize the vulnerabilities with advisory_id and advisory label as keys
1052- advisory_data = {f"{ adv .avid } " : AdvisoryV2Serializer (adv ).data for adv in advisories }
1059+ avids = set ()
10531060
1054- # Serialize the current page of packages
1055- serializer = self .get_serializer (page , many = True )
1056- data = serializer .data
1061+ for package in packages :
1062+ for impact in package .affected_in_impacts .all ():
1063+ if impact .advisory_id :
1064+ avids .add (impact .advisory .avid )
10571065
1058- # Use 'self.get_paginated_response' to include pagination data
1059- return self .get_paginated_response ({"advisories" : advisory_data , "packages" : data })
1066+ for impact in package .fixed_in_impacts .all ():
1067+ if impact .advisory_id :
1068+ avids .add (impact .advisory .avid )
10601069
1061- # If pagination is not applied, collect vulnerabilities for all packages
1062- for package in queryset :
1063- advisories .update ({impact .advisory for impact in package .affected_in_impacts .all ()})
1064- advisories .update ({impact .advisory for impact in package .fixed_in_impacts .all ()})
1070+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
10651071
1066- advisory_data = {f" { adv .avid } " : AdvisoryV2Serializer (adv ).data for adv in advisories }
1072+ advisory_data = {adv .avid : AdvisoryV2Serializer (adv ).data for adv in latest_advisories }
10671073
1068- serializer = self .get_serializer (queryset , many = True )
1069- data = serializer .data
1070- return Response ({"advisories" : advisory_data , "packages" : data })
1074+ serializer = self .get_serializer (packages , many = True )
1075+
1076+ if page is not None :
1077+ return self .get_paginated_response (
1078+ {
1079+ "packages" : serializer .data ,
1080+ "advisories" : advisory_data ,
1081+ }
1082+ )
1083+
1084+ return Response (
1085+ {
1086+ "packages" : serializer .data ,
1087+ "advisories" : advisory_data ,
1088+ }
1089+ )
10711090
10721091 @extend_schema (
10731092 request = PackageurlListSerializer ,
@@ -1093,17 +1112,16 @@ def bulk_lookup(self, request):
10931112 "message" : "A non-empty 'purls' list of PURLs is required." ,
10941113 },
10951114 )
1096- validated_data = serializer .validated_data
1097- purls = validated_data .get ("purls" )
10981115
1099- # Fetch packages matching the provided purls
1116+ purls = serializer .validated_data .get ("purls" )
1117+
11001118 packages = (
11011119 PackageV2 .objects .for_purls (purls )
11021120 .prefetch_related (
11031121 Prefetch (
11041122 "affected_in_impacts" ,
11051123 queryset = ImpactedPackage .objects .select_related ("advisory" ).prefetch_related (
1106- "fixed_by_packages" ,
1124+ "fixed_by_packages"
11071125 ),
11081126 ),
11091127 Prefetch (
@@ -1114,26 +1132,34 @@ def bulk_lookup(self, request):
11141132 .with_is_vulnerable ()
11151133 )
11161134
1117- # Collect vulnerabilities associated with these packages
1118- advisories = set ()
1135+ avids = set ()
1136+
11191137 for package in packages :
1120- advisories .update ({impact .advisory for impact in package .affected_in_impacts .all ()})
1121- advisories .update ({impact .advisory for impact in package .fixed_in_impacts .all ()})
1138+ for impact in package .affected_in_impacts .all ():
1139+ if impact .advisory_id :
1140+ avids .add (impact .advisory .avid )
11221141
1123- # Serialize vulnerabilities with vulnerability_id as keys
1124- advisory_data = {adv .avid : AdvisoryV2Serializer (adv ).data for adv in advisories }
1142+ for impact in package .fixed_in_impacts .all ():
1143+ if impact .advisory_id :
1144+ avids .add (impact .advisory .avid )
11251145
1126- # Serialize packages
1127- package_data = AdvisoryPackageV2Serializer (
1146+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
1147+
1148+ advisory_data = {
1149+ adv .avid : AdvisoryV2Serializer (adv , context = {"request" : request }).data
1150+ for adv in latest_advisories
1151+ }
1152+
1153+ package_data = PackageV3Serializer (
11281154 packages ,
11291155 many = True ,
11301156 context = {"request" : request },
11311157 ).data
11321158
11331159 return Response (
11341160 {
1135- "advisories" : advisory_data ,
11361161 "packages" : package_data ,
1162+ "advisories" : advisory_data ,
11371163 }
11381164 )
11391165
@@ -1161,6 +1187,7 @@ def bulk_search(self, request):
11611187 "message" : "A non-empty 'purls' list of PURLs is required." ,
11621188 },
11631189 )
1190+
11641191 validated_data = serializer .validated_data
11651192 purls = validated_data .get ("purls" )
11661193 purl_only = validated_data .get ("purl_only" , False )
@@ -1202,24 +1229,31 @@ def bulk_search(self, request):
12021229
12031230 packages = query
12041231
1205- # Collect vulnerabilities associated with these packages
1206- advisories = set ()
1232+ avids = set ()
12071233 for package in packages :
1208- advisories .update ({impact .advisory for impact in package .affected_in_impacts .all ()})
1209- advisories .update ({impact .advisory for impact in package .fixed_in_impacts .all ()})
1210-
1211- advisory_data = {adv .avid : AdvisoryV2Serializer (adv ).data for adv in advisories }
1234+ for impact in package .affected_in_impacts .all ():
1235+ if impact .advisory_id :
1236+ avids .add (impact .advisory .avid )
1237+ for impact in package .fixed_in_impacts .all ():
1238+ if impact .advisory_id :
1239+ avids .add (impact .advisory .avid )
1240+
1241+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
1242+ advisory_data = {
1243+ adv .avid : AdvisoryV2Serializer (adv , context = {"request" : request }).data
1244+ for adv in latest_advisories
1245+ }
12121246
12131247 if not purl_only :
1214- package_data = AdvisoryPackageV2Serializer (
1248+ package_data = PackageV3Serializer (
12151249 packages ,
12161250 many = True ,
12171251 context = {"request" : request },
12181252 ).data
12191253 return Response (
12201254 {
1221- "advisories" : advisory_data ,
12221255 "packages" : package_data ,
1256+ "advisories" : advisory_data ,
12231257 }
12241258 )
12251259
@@ -1249,24 +1283,31 @@ def bulk_search(self, request):
12491283 )
12501284 packages = query
12511285
1252- # Collect vulnerabilities associated with these packages
1253- advisories = set ()
1286+ avids = set ()
12541287 for package in packages :
1255- advisories .update ({impact .advisory for impact in package .affected_in_impacts .all ()})
1256- advisories .update ({impact .advisory for impact in package .fixed_in_impacts .all ()})
1257-
1258- advisory_data = {adv .advisory_id : AdvisoryV2Serializer (adv ).data for adv in advisories }
1288+ for impact in package .affected_in_impacts .all ():
1289+ if impact .advisory_id :
1290+ avids .add (impact .advisory .avid )
1291+ for impact in package .fixed_in_impacts .all ():
1292+ if impact .advisory_id :
1293+ avids .add (impact .advisory .avid )
1294+
1295+ latest_advisories = AdvisoryV2 .objects .latest_for_avids (avids )
1296+ advisory_data = {
1297+ adv .avid : AdvisoryV2Serializer (adv , context = {"request" : request }).data
1298+ for adv in latest_advisories
1299+ }
12591300
12601301 if not purl_only :
1261- package_data = AdvisoryPackageV2Serializer (
1302+ package_data = PackageV3Serializer (
12621303 packages ,
12631304 many = True ,
12641305 context = {"request" : request },
12651306 ).data
12661307 return Response (
12671308 {
1268- "advisories" : advisory_data ,
12691309 "packages" : package_data ,
1310+ "advisories" : advisory_data ,
12701311 }
12711312 )
12721313
@@ -1316,6 +1357,4 @@ def lookup(self, request):
13161357 purl = validated_data .get ("purl" )
13171358
13181359 qs = self .get_queryset ().for_purls ([purl ]).with_is_vulnerable ()
1319- return Response (
1320- AdvisoryPackageV2Serializer (qs , many = True , context = {"request" : request }).data
1321- )
1360+ return Response (PackageV3Serializer (qs , many = True , context = {"request" : request }).data )
0 commit comments