Skip to content

Commit b97fdf4

Browse files
committed
Merge remote-tracking branch 'origin/main' into save-risk
2 parents 86f6927 + 8a68c97 commit b97fdf4

File tree

7 files changed

+547
-2
lines changed

7 files changed

+547
-2
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ Release notes
22
=============
33

44
Version (next)
5+
-----------------------
6+
7+
8+
Version v34.2.0
59
-------------------
610

11+
- Add V2 API endpoints #1631
12+
13+
714
Version v34.1.0
815
-------------------
916

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = vulnerablecode
3-
version = 34.1.0
3+
version = 34.2.0
44
license = Apache-2.0 AND CC-BY-SA-4.0
55

66
# description must be on ONE line https://github.com/pypa/setuptools/issues/1390

vulnerabilities/api_v2.py

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

0 commit comments

Comments
 (0)