Skip to content

Commit 8849f27

Browse files
fix(api_v3): validate PURLs in packages and advisories query serializers
In api_v3, passing malformed or invalid Package URLs to POST /api/v3/packages or POST /api/v3/advisories either causes an unhandled ValueError (500 Internal Server Error) when details/ignore_qualifiers_subpath is set, or silently returns empty results. Add validate_purls validation methods to both PackageQuerySerializer and AdvisoryQuerySerializer using PackageURL.from_string, ensuring consistent HTTP 400 Bad Request responses with descriptive validation error messages for malformed PURLs across all v3 query endpoints. Add test cases in test_api_v3 covering invalid PURL submissions for both endpoints. Signed-off-by: Shubham Rai <shubhamrai9122@gmail.com>
1 parent 910df62 commit 8849f27

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

‎vulnerabilities/api_v3.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ class PackageQuerySerializer(serializers.Serializer):
5757
max_advisories = serializers.IntegerField(default=100, min_value=1, max_value=10000)
5858
reachability = serializers.BooleanField(default=False)
5959

60+
def validate_purls(self, purls):
61+
for purl in purls:
62+
try:
63+
PackageURL.from_string(purl)
64+
except ValueError as e:
65+
raise serializers.ValidationError(str(e))
66+
return purls
67+
6068
def validate(self, data):
6169
if not data["purls"]:
6270
if data["details"] or data["ignore_qualifiers_subpath"]:
@@ -81,6 +89,14 @@ class AdvisoryQuerySerializer(serializers.Serializer):
8189
default=list,
8290
)
8391

92+
def validate_purls(self, purls):
93+
for purl in purls:
94+
try:
95+
PackageURL.from_string(purl)
96+
except ValueError as e:
97+
raise serializers.ValidationError(str(e))
98+
return purls
99+
84100
def validate(self, data):
85101
if not data["purls"]:
86102
raise serializers.ValidationError("purls is required")

‎vulnerabilities/tests/test_api_v3.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,38 @@ def test_packages_post_with_details_fixing_vulnerabilities(self):
140140
self.assertEqual(fixing_vulnerability["datasource_id"], "ghsa")
141141
self.assertEqual(pkg["next_non_vulnerable_version"], "2.0.0")
142142

143+
def test_packages_post_with_invalid_purl(self):
144+
url = reverse("package-v3-list")
145+
146+
for ignore_qualifiers_subpath in (False, True):
147+
with self.subTest(ignore_qualifiers_subpath=ignore_qualifiers_subpath):
148+
response = self.client.post(
149+
url,
150+
data={
151+
"purls": ["not-a-purl"],
152+
"details": True,
153+
"ignore_qualifiers_subpath": ignore_qualifiers_subpath,
154+
},
155+
format="json",
156+
HTTP_USER_AGENT="VCIO_API_AGENT",
157+
)
158+
159+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
160+
self.assertIn("purls", response.data)
161+
162+
def test_advisories_post_with_invalid_purl(self):
163+
url = reverse("advisory-v3-list")
164+
165+
response = self.client.post(
166+
url,
167+
data={"purls": ["invalid-purl-scheme"]},
168+
format="json",
169+
HTTP_USER_AGENT="VCIO_API_AGENT",
170+
)
171+
172+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
173+
self.assertIn("purls", response.data)
174+
143175
def test_advisories_post(self):
144176
url = reverse("advisory-v3-list")
145177

0 commit comments

Comments
 (0)