Skip to content

Commit caf3354

Browse files
authored
Merge pull request #980 from TG1999/ignore_qualifiers_and_subpath
Ignore qualifiers and subpath from PURL search lookups #978
2 parents 6f8b968 + be3dc7c commit caf3354

File tree

6 files changed

+81
-11
lines changed

6 files changed

+81
-11
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Version v30.2.1
77
----------------
88

99
- We refactored and fixed the LaunchPad API code.
10+
- We now ignore qualifiers and subpath from PURL search lookups.
1011

1112

1213
Version v30.2.0

vulnerabilities/api.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from vulnerabilities.models import Vulnerability
2323
from vulnerabilities.models import VulnerabilityReference
2424
from vulnerabilities.models import VulnerabilitySeverity
25+
from vulnerabilities.models import get_purl_query_lookups
2526

2627

2728
class VulnerabilitySeveritySerializer(serializers.ModelSerializer):
@@ -210,8 +211,8 @@ def filter_purl(self, queryset, name, value):
210211
detail={"error": f'"{purl}" is not a valid Package URL: {ve}'},
211212
)
212213

213-
attrs = {k: v for k, v in purl.to_dict().items() if v}
214-
return self.queryset.filter(**attrs)
214+
lookups = get_purl_query_lookups(purl)
215+
return self.queryset.filter(**lookups)
215216

216217

217218
class PackageViewSet(viewsets.ReadOnlyModelViewSet):
@@ -236,12 +237,11 @@ def bulk_search(self, request):
236237
for purl in request.data["purls"]:
237238
try:
238239
purl_string = purl
239-
purl = PackageURL.from_string(purl).to_dict()
240+
purl = PackageURL.from_string(purl)
240241
except ValueError:
241242
return Response(status=400, data={"Error": f"Invalid Package URL: {purl}"})
242-
purl_data = Package.objects.filter(
243-
**{key: value for key, value in purl.items() if value}
244-
)
243+
lookups = get_purl_query_lookups(purl)
244+
purl_data = Package.objects.filter(**lookups)
245245
purl_response = {}
246246
if purl_data:
247247
purl_response = PackageSerializer(purl_data[0], context={"request": request}).data

vulnerabilities/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@ def with_vulnerability_counts(self):
218218
)
219219

220220

221+
def get_purl_query_lookups(purl):
222+
"""
223+
Do not reference all the possible qualifiers and relax the
224+
purl matching to only lookup the type, namespace, name and version fields.
225+
"""
226+
lookup_fields = ["type", "namespace", "name", "version"]
227+
return {
228+
field_name: value
229+
for field_name, value in purl.to_dict().items()
230+
if value and field_name in lookup_fields
231+
}
232+
233+
221234
class Package(PackageURLMixin):
222235
"""
223236
A software package with related vulnerabilities.

vulnerabilities/tests/test_fix_api.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@ def test_api_with_all_vulnerable_packages(self):
281281
"pkg:generic/nginx/test@9",
282282
]
283283

284+
def test_api_with_ignorning_qualifiers(self):
285+
response = self.csrf_client.get(
286+
f"/api/packages/?purl=pkg:generic/nginx/test@9?foo=bar", format="json"
287+
).data
288+
assert response["count"] == 1
289+
assert response["results"][0]["purl"] == "pkg:generic/nginx/test@9"
290+
284291

285292
class CPEApi(TestCase):
286293
def setUp(self):
@@ -353,7 +360,7 @@ def setUp(self):
353360
attrs = {k: v for k, v in purl.to_dict().items() if v}
354361
Package.objects.create(**attrs)
355362

356-
def test_api_response(self):
363+
def test_bulk_api_response(self):
357364
request_body = {
358365
"purls": self.packages,
359366
}
@@ -364,6 +371,30 @@ def test_api_response(self):
364371
).json()
365372
assert len(response) == 13
366373

374+
def test_bulk_api_response_with_ignoring_qualifiers(self):
375+
request_body = {
376+
"purls": ["pkg:nginx/[email protected]?qualifiers=dev"],
377+
}
378+
response = self.csrf_client.post(
379+
"/api/packages/bulk_search",
380+
data=json.dumps(request_body),
381+
content_type="application/json",
382+
).json()
383+
assert len(response) == 1
384+
assert response[0]["purl"] == "pkg:nginx/[email protected]"
385+
386+
def test_bulk_api_response_with_ignoring_subpath(self):
387+
request_body = {
388+
"purls": ["pkg:nginx/[email protected]#dev/subpath"],
389+
}
390+
response = self.csrf_client.post(
391+
"/api/packages/bulk_search",
392+
data=json.dumps(request_body),
393+
content_type="application/json",
394+
).json()
395+
assert len(response) == 1
396+
assert response[0]["purl"] == "pkg:nginx/[email protected]"
397+
367398

368399
class BulkSearchAPICPE(TestCase):
369400
def setUp(self):

vulnerabilities/tests/test_view.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,37 @@
99

1010
from django.test import Client
1111
from django.test import TestCase
12+
from packageurl import PackageURL
1213

1314
from vulnerabilities.models import Alias
15+
from vulnerabilities.models import Package
1416
from vulnerabilities.models import Vulnerability
17+
from vulnerabilities.views import PackageSearch
1518

1619

1720
class PackageSearchTestCase(TestCase):
1821
def setUp(self):
1922
self.client = Client()
23+
packages = [
24+
"pkg:nginx/[email protected]",
25+
"pkg:nginx/[email protected]",
26+
"pkg:nginx/[email protected]",
27+
"pkg:nginx/[email protected]",
28+
"pkg:nginx/[email protected]",
29+
"pkg:nginx/[email protected]",
30+
"pkg:nginx/[email protected]",
31+
"pkg:nginx/[email protected]",
32+
"pkg:nginx/[email protected]",
33+
"pkg:nginx/[email protected]",
34+
"pkg:nginx/[email protected]",
35+
"pkg:nginx/[email protected]",
36+
"pkg:nginx/[email protected]",
37+
]
38+
self.packages = packages
39+
for package in packages:
40+
purl = PackageURL.from_string(package)
41+
attrs = {k: v for k, v in purl.to_dict().items() if v}
42+
Package.objects.create(**attrs)
2043

2144
def test_packages_search_view_paginator(self):
2245
response = self.client.get("/packages/search?type=deb&name=&page=1")
@@ -28,6 +51,12 @@ def test_packages_search_view_paginator(self):
2851
response = self.client.get("/packages/search?type=&name=&page=")
2952
self.assertEqual(response.status_code, 200)
3053

54+
def test_package_view(self):
55+
qs = PackageSearch().get_queryset(query="pkg:nginx/[email protected]?foo=bar")
56+
pkgs = list(qs)
57+
self.assertEqual(len(pkgs), 1)
58+
self.assertEqual(pkgs[0].purl, "pkg:nginx/[email protected]")
59+
3160

3261
class VulnerabilitySearchTestCase(TestCase):
3362
def setUp(self):

vulnerabilities/views.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ def get_queryset(self, query=None):
9393
qs = qs.filter(name__iexact=name)
9494
if version:
9595
qs = qs.filter(version__iexact=version)
96-
if qualifiers:
97-
qs = qs.filter(qualifiers=qualifiers)
98-
if subpath:
99-
qs = qs.filter(subpath__iexact=subpath)
10096

10197
return qs.annotate(
10298
vulnerability_count=Count(

0 commit comments

Comments
 (0)