Skip to content

Commit a97b5bd

Browse files
committed
Address review comment
Signed-off-by: Tushar Goel <[email protected]>
1 parent 76c13a8 commit a97b5bd

File tree

3 files changed

+196
-4
lines changed

3 files changed

+196
-4
lines changed

vulnerabilities/api_v2.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from rest_framework import serializers
11+
from rest_framework import viewsets
12+
from rest_framework.response import Response
13+
from rest_framework.reverse import reverse
14+
15+
from vulnerabilities.api import VulnerabilitySeveritySerializer
16+
from vulnerabilities.models import Package
17+
from vulnerabilities.models import Vulnerability
18+
from vulnerabilities.models import VulnerabilityReference
19+
from vulnerabilities.models import VulnerabilitySeverity
20+
from vulnerabilities.models import Weakness
21+
22+
23+
class WeaknessV2Serializer(serializers.ModelSerializer):
24+
cwe_id = serializers.CharField()
25+
name = serializers.CharField()
26+
description = serializers.CharField()
27+
28+
class Meta:
29+
model = Weakness
30+
fields = ["cwe_id", "name", "description"]
31+
32+
33+
class VulnerabilityReferenceV2Serializer(serializers.ModelSerializer):
34+
url = serializers.CharField()
35+
reference_type = serializers.CharField()
36+
reference_id = serializers.CharField()
37+
38+
class Meta:
39+
model = VulnerabilityReference
40+
fields = ["url", "reference_type", "reference_id"]
41+
42+
43+
class VulnerabilityV2Serializer(serializers.ModelSerializer):
44+
aliases = serializers.SerializerMethodField()
45+
weaknesses = WeaknessV2Serializer(many=True)
46+
references = VulnerabilityReferenceV2Serializer(many=True, source="vulnerabilityreference_set")
47+
severities = VulnerabilitySeveritySerializer(many=True)
48+
49+
class Meta:
50+
model = Vulnerability
51+
fields = [
52+
"vulnerability_id",
53+
"aliases",
54+
"summary",
55+
"severities",
56+
"weaknesses",
57+
"references",
58+
]
59+
60+
def get_aliases(self, obj):
61+
return [alias.alias for alias in obj.aliases.all()]
62+
63+
def get_severities(self, obj):
64+
return obj.severities
65+
66+
67+
class VulnerabilityListSerializer(serializers.ModelSerializer):
68+
url = serializers.SerializerMethodField()
69+
70+
class Meta:
71+
model = Vulnerability
72+
fields = ["vulnerability_id", "url"]
73+
74+
def get_url(self, obj):
75+
request = self.context.get("request")
76+
return reverse(
77+
"vulnerability-v2-detail",
78+
kwargs={"vulnerability_id": obj.vulnerability_id},
79+
request=request,
80+
)
81+
82+
83+
class VulnerabilityV2ViewSet(viewsets.ReadOnlyModelViewSet):
84+
queryset = Vulnerability.objects.all()
85+
serializer_class = VulnerabilityV2Serializer
86+
lookup_field = "vulnerability_id"
87+
88+
def get_queryset(self):
89+
queryset = super().get_queryset()
90+
vulnerability_ids = self.request.query_params.getlist("vulnerability_id")
91+
aliases = self.request.query_params.getlist("alias")
92+
93+
if vulnerability_ids:
94+
queryset = queryset.filter(vulnerability_id__in=vulnerability_ids)
95+
96+
if aliases:
97+
queryset = queryset.filter(aliases__alias__in=aliases).distinct()
98+
99+
return queryset
100+
101+
def get_serializer_class(self):
102+
if self.action == "list":
103+
return VulnerabilityListSerializer
104+
return super().get_serializer_class()
105+
106+
def list(self, request, *args, **kwargs):
107+
queryset = self.get_queryset()
108+
vulnerability_ids = request.query_params.getlist("vulnerability_id")
109+
110+
# If exactly one vulnerability_id is provided, return the serialized data
111+
if len(vulnerability_ids) == 1:
112+
try:
113+
vulnerability = queryset.get(vulnerability_id=vulnerability_ids[0])
114+
serializer = self.get_serializer(vulnerability)
115+
return Response(serializer.data)
116+
except Vulnerability.DoesNotExist:
117+
return Response({"detail": "Not found."}, status=404)
118+
119+
# Otherwise, return a dictionary of vulnerabilities keyed by vulnerability_id
120+
page = self.paginate_queryset(queryset)
121+
if page is not None:
122+
serializer = self.get_serializer(page, many=True)
123+
data = serializer.data
124+
vulnerabilities = {item["vulnerability_id"]: item for item in data}
125+
return self.get_paginated_response({"vulnerabilities": vulnerabilities})
126+
127+
serializer = self.get_serializer(queryset, many=True)
128+
data = serializer.data
129+
vulnerabilities = {item["vulnerability_id"]: item for item in data}
130+
return Response({"vulnerabilities": vulnerabilities})
131+
132+
133+
class PackageV2Serializer(serializers.ModelSerializer):
134+
purl = serializers.CharField(source="package_url")
135+
affected_by_vulnerabilities = serializers.SerializerMethodField()
136+
fixing_vulnerabilities = serializers.SerializerMethodField()
137+
next_non_vulnerable_version = serializers.CharField(read_only=True)
138+
latest_non_vulnerable_version = serializers.CharField(read_only=True)
139+
140+
class Meta:
141+
model = Package
142+
fields = [
143+
"purl",
144+
"affected_by_vulnerabilities",
145+
"fixing_vulnerabilities",
146+
"next_non_vulnerable_version",
147+
"latest_non_vulnerable_version",
148+
]
149+
150+
def get_affected_by_vulnerabilities(self, obj):
151+
return [vuln.vulnerability_id for vuln in obj.affected_by_vulnerabilities.all()]
152+
153+
def get_fixing_vulnerabilities(self, obj):
154+
return [vuln.vulnerability_id for vuln in obj.fixing_vulnerabilities.all()]
155+
156+
157+
class PackageV2ViewSet(viewsets.ReadOnlyModelViewSet):
158+
queryset = Package.objects.all()
159+
serializer_class = PackageV2Serializer
160+
161+
def get_queryset(self):
162+
queryset = super().get_queryset()
163+
package_purls = self.request.query_params.getlist("purl")
164+
affected_by_vulnerability = self.request.query_params.get("affected_by_vulnerability")
165+
fixing_vulnerability = self.request.query_params.get("fixing_vulnerability")
166+
167+
if package_purls:
168+
queryset = queryset.filter(package_url__in=package_purls)
169+
if affected_by_vulnerability:
170+
queryset = queryset.filter(
171+
affected_by_vulnerabilities__vulnerability_id=affected_by_vulnerability
172+
)
173+
if fixing_vulnerability:
174+
queryset = queryset.filter(
175+
fixing_vulnerabilities__vulnerability_id=fixing_vulnerability
176+
)
177+
return queryset.with_is_vulnerable()
178+
179+
def list(self, request, *args, **kwargs):
180+
queryset = self.get_queryset()
181+
# Apply pagination
182+
page = self.paginate_queryset(queryset)
183+
if page is not None:
184+
serializer = self.get_serializer(page, many=True)
185+
data = serializer.data
186+
# Use 'self.get_paginated_response' to include pagination data
187+
return self.get_paginated_response({"purls": data})
188+
189+
# If pagination is not applied
190+
serializer = self.get_serializer(queryset, many=True)
191+
data = serializer.data
192+
return Response({"purls": data})

vulnerabilities/tests/test_v2_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
from vulnerabilities.models import Vulnerability
2020
from vulnerabilities.models import VulnerabilityReference
2121
from vulnerabilities.models import Weakness
22-
from vulnerabilities.v2_api import PackageV2Serializer
23-
from vulnerabilities.v2_api import VulnerabilityListSerializer
22+
from vulnerabilities.api_v2 import PackageV2Serializer
23+
from vulnerabilities.api_v2 import VulnerabilityListSerializer
2424

2525

2626
class VulnerabilityV2ViewSetTest(APITestCase):

vulnerablecode/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from vulnerabilities.api import CPEViewSet
2121
from vulnerabilities.api import PackageViewSet
2222
from vulnerabilities.api import VulnerabilityViewSet
23-
from vulnerabilities.v2_api import PackageV2ViewSet
24-
from vulnerabilities.v2_api import VulnerabilityV2ViewSet
23+
from vulnerabilities.api_v2 import PackageV2ViewSet
24+
from vulnerabilities.api_v2 import VulnerabilityV2ViewSet
2525
from vulnerabilities.views import ApiUserCreateView
2626
from vulnerabilities.views import HomePage
2727
from vulnerabilities.views import PackageDetails

0 commit comments

Comments
 (0)