Skip to content

Commit c5b78ef

Browse files
committed
Use impact table to fetch fixed and affected pkg in API
Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent 0ae0057 commit c5b78ef

1 file changed

Lines changed: 93 additions & 44 deletions

File tree

vulnerabilities/api_v2.py

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from vulnerabilities.models import AdvisoryWeakness
3232
from vulnerabilities.models import CodeFix
3333
from vulnerabilities.models import CodeFixV2
34+
from vulnerabilities.models import ImpactedPackage
3435
from vulnerabilities.models import Package
3536
from vulnerabilities.models import PackageV2
3637
from vulnerabilities.models import PipelineRun
@@ -320,36 +321,28 @@ class Meta:
320321
"risk_score",
321322
]
322323

323-
def get_affected_by_vulnerabilities(self, obj):
324-
"""
325-
Return a dictionary with vulnerabilities as keys and their details, including fixed_by_packages.
326-
"""
324+
def get_affected_by_vulnerabilities(self, package):
325+
"""Return a dictionary with advisory as keys and their details, including fixed_by_packages."""
327326
result = {}
328327
request = self.context.get("request")
329-
for adv in getattr(obj, "prefetched_affected_advisories", []):
330-
fixed_by_package = adv.fixed_by_packages.first()
331-
purl = None
332-
if fixed_by_package:
333-
purl = fixed_by_package.package_url
334-
# Get code fixed for a vulnerability
335-
code_fixes = CodeFixV2.objects.filter(advisory=adv).distinct()
328+
for impact in package.affected_in_impacts.all():
329+
advisory = impact.advisory
330+
fixed_by_packages = [pkg.purl for pkg in impact.fixed_by_packages.all()]
331+
code_fixes = CodeFixV2.objects.filter(advisory=advisory).distinct()
336332
code_fix_urls = [
337333
reverse("advisory-codefix-detail", args=[code_fix.id], request=request)
338334
for code_fix in code_fixes
339335
]
340-
341-
result[adv.avid] = {
342-
"advisory_id": adv.avid,
343-
"fixed_by_packages": purl,
336+
result[advisory.avid] = {
337+
"advisory_id": advisory.avid,
338+
"fixed_by_packages": fixed_by_packages,
344339
"code_fixes": code_fix_urls,
345340
}
341+
346342
return result
347343

348-
def get_fixing_vulnerabilities(self, obj):
349-
# Ghost package should not fix any vulnerability.
350-
if obj.is_ghost:
351-
return []
352-
return [adv.avid for adv in obj.fixing_advisories.all()]
344+
def get_fixing_vulnerabilities(self, package):
345+
return [impact.advisory.avid for impact in package.fixed_in_impacts.all()]
353346

354347

355348
class PackageurlListSerializer(serializers.Serializer):
@@ -968,12 +961,21 @@ def get_view_name(self):
968961

969962

970963
class AdvisoriesPackageV2ViewSet(viewsets.ReadOnlyModelViewSet):
971-
queryset = PackageV2.objects.all().prefetch_related(
972-
Prefetch(
973-
"affected_by_advisories",
974-
queryset=AdvisoryV2.objects.prefetch_related("fixed_by_packages"),
975-
to_attr="prefetched_affected_advisories",
964+
queryset = (
965+
PackageV2.objects.all()
966+
.prefetch_related(
967+
Prefetch(
968+
"affected_in_impacts",
969+
queryset=ImpactedPackage.objects.select_related("advisory").prefetch_related(
970+
"fixed_by_packages",
971+
),
972+
),
973+
Prefetch(
974+
"fixed_in_impacts",
975+
queryset=ImpactedPackage.objects.select_related("advisory"),
976+
),
976977
)
978+
.with_is_vulnerable()
977979
)
978980
serializer_class = AdvisoryPackageV2Serializer
979981
filter_backends = (filters.DjangoFilterBackend,)
@@ -994,30 +996,28 @@ def get_queryset(self):
994996

995997
def list(self, request, *args, **kwargs):
996998
queryset = self.get_queryset()
997-
# Apply pagination
998999
page = self.paginate_queryset(queryset)
1000+
1001+
advisories = set()
9991002
if page is not None:
1000-
# Collect only vulnerabilities for packages in the current page
1001-
advisories = set()
10021003
for package in page:
1003-
advisories.update(package.affected_by_advisories.all())
1004-
advisories.update(package.fixing_advisories.all())
1004+
advisories.update({impact.advisory for impact in package.affected_in_impacts.all()})
1005+
advisories.update({impact.advisory for impact in package.fixed_in_impacts.all()})
10051006

10061007
# Serialize the vulnerabilities with advisory_id and advisory label as keys
10071008
advisory_data = {f"{adv.avid}": AdvisoryV2Serializer(adv).data for adv in advisories}
10081009

10091010
# Serialize the current page of packages
10101011
serializer = self.get_serializer(page, many=True)
10111012
data = serializer.data
1012-
print(data)
1013+
10131014
# Use 'self.get_paginated_response' to include pagination data
10141015
return self.get_paginated_response({"advisories": advisory_data, "packages": data})
10151016

10161017
# If pagination is not applied, collect vulnerabilities for all packages
1017-
advisories = set()
10181018
for package in queryset:
1019-
advisories.update(package.affected_by_vulnerabilities.all())
1020-
advisories.update(package.fixing_vulnerabilities.all())
1019+
advisories.update({impact.advisory for impact in package.affected_in_impacts.all()})
1020+
advisories.update({impact.advisory for impact in package.fixed_in_impacts.all()})
10211021

10221022
advisory_data = {f"{adv.avid}": AdvisoryV2Serializer(adv).data for adv in advisories}
10231023

@@ -1053,13 +1053,28 @@ def bulk_lookup(self, request):
10531053
purls = validated_data.get("purls")
10541054

10551055
# Fetch packages matching the provided purls
1056-
packages = PackageV2.objects.for_purls(purls).with_is_vulnerable()
1056+
packages = (
1057+
PackageV2.objects.for_purls(purls)
1058+
.prefetch_related(
1059+
Prefetch(
1060+
"affected_in_impacts",
1061+
queryset=ImpactedPackage.objects.select_related("advisory").prefetch_related(
1062+
"fixed_by_packages",
1063+
),
1064+
),
1065+
Prefetch(
1066+
"fixed_in_impacts",
1067+
queryset=ImpactedPackage.objects.select_related("advisory"),
1068+
),
1069+
)
1070+
.with_is_vulnerable()
1071+
)
10571072

10581073
# Collect vulnerabilities associated with these packages
10591074
advisories = set()
10601075
for package in packages:
1061-
advisories.update(package.affected_by_advisories.all())
1062-
advisories.update(package.fixing_advisories.all())
1076+
advisories.update({impact.advisory for impact in package.affected_in_impacts.all()})
1077+
advisories.update({impact.advisory for impact in package.fixed_in_impacts.all()})
10631078

10641079
# Serialize vulnerabilities with vulnerability_id as keys
10651080
advisory_data = {adv.avid: AdvisoryV2Serializer(adv).data for adv in advisories}
@@ -1124,6 +1139,20 @@ def bulk_search(self, request):
11241139
PackageV2.objects.filter(plain_package_url__in=plain_purls)
11251140
.order_by("plain_package_url")
11261141
.distinct("plain_package_url")
1142+
.prefetch_related(
1143+
Prefetch(
1144+
"affected_in_impacts",
1145+
queryset=ImpactedPackage.objects.select_related(
1146+
"advisory"
1147+
).prefetch_related(
1148+
"fixed_by_packages",
1149+
),
1150+
),
1151+
Prefetch(
1152+
"fixed_in_impacts",
1153+
queryset=ImpactedPackage.objects.select_related("advisory"),
1154+
),
1155+
)
11271156
.with_is_vulnerable()
11281157
)
11291158

@@ -1132,14 +1161,16 @@ def bulk_search(self, request):
11321161
# Collect vulnerabilities associated with these packages
11331162
advisories = set()
11341163
for package in packages:
1135-
advisories.update(package.affected_by_advisories.all())
1136-
advisories.update(package.fixing_advisories.all())
1164+
advisories.update({impact.advisory for impact in package.affected_in_impacts.all()})
1165+
advisories.update({impact.advisory for impact in package.fixed_in_impacts.all()})
11371166

11381167
advisory_data = {adv.avid: AdvisoryV2Serializer(adv).data for adv in advisories}
11391168

11401169
if not purl_only:
11411170
package_data = AdvisoryPackageV2Serializer(
1142-
packages, many=True, context={"request": request}
1171+
packages,
1172+
many=True,
1173+
context={"request": request},
11431174
).data
11441175
return Response(
11451176
{
@@ -1154,20 +1185,38 @@ def bulk_search(self, request):
11541185
vulnerable_purls = [str(package.plain_package_url) for package in vulnerable_purls]
11551186
return Response(data=vulnerable_purls)
11561187

1157-
query = PackageV2.objects.filter(package_url__in=purls).distinct().with_is_vulnerable()
1188+
query = (
1189+
PackageV2.objects.filter(package_url__in=purls)
1190+
.distinct()
1191+
.prefetch_related(
1192+
Prefetch(
1193+
"affected_in_impacts",
1194+
queryset=ImpactedPackage.objects.select_related("advisory").prefetch_related(
1195+
"fixed_by_packages",
1196+
),
1197+
),
1198+
Prefetch(
1199+
"fixed_in_impacts",
1200+
queryset=ImpactedPackage.objects.select_related("advisory"),
1201+
),
1202+
)
1203+
.with_is_vulnerable()
1204+
)
11581205
packages = query
11591206

11601207
# Collect vulnerabilities associated with these packages
11611208
advisories = set()
11621209
for package in packages:
1163-
advisories.update(package.affected_by_advisories.all())
1164-
advisories.update(package.fixing_advisories.all())
1210+
advisories.update({impact.advisory for impact in package.affected_in_impacts.all()})
1211+
advisories.update({impact.advisory for impact in package.fixed_in_impacts.all()})
11651212

11661213
advisory_data = {adv.advisory_id: AdvisoryV2Serializer(adv).data for adv in advisories}
11671214

11681215
if not purl_only:
11691216
package_data = AdvisoryPackageV2Serializer(
1170-
packages, many=True, context={"request": request}
1217+
packages,
1218+
many=True,
1219+
context={"request": request},
11711220
).data
11721221
return Response(
11731222
{

0 commit comments

Comments
 (0)