Skip to content

Commit cd3d2c2

Browse files
committed
Support incomplete versions for a valid purl in search
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent f3058d1 commit cd3d2c2

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Version v31.1.1
66
---------------
77

88
- We re-enabled support for the Apache HTTPD security advisories importer.
9+
- We now support incomplete versions for a valid purl in search. For example,
10+
you can now search for ``pkg:nginx/nginx@1`` and get all versions of nginx
11+
starting with ``1``.
912

1013

1114
Version v31.1.0

vulnerabilities/models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from vulnerabilities.improver import MAX_CONFIDENCE
3737
from vulnerabilities.severity_systems import SCORING_SYSTEMS
3838
from vulnerabilities.utils import build_vcid
39+
from vulnerabilities.utils import remove_qualifiers_and_subpath
3940

4041
logger = logging.getLogger(__name__)
4142

@@ -417,7 +418,8 @@ def search(self, query=None):
417418
try:
418419
# if it's a valid purl, use it as is
419420
purl = PackageURL.from_string(query)
420-
return self.for_purl(purl, with_qualifiers_and_subpath=False)
421+
purl = str(remove_qualifiers_and_subpath(purl))
422+
return qs.filter(package_url__istartswith=purl)
421423
except ValueError:
422424
return qs.filter(package_url__icontains=query)
423425

@@ -427,10 +429,9 @@ def for_purl(self, purl, with_qualifiers_and_subpath=True):
427429
"""
428430
if not isinstance(purl, PackageURL):
429431
purl = PackageURL.from_string(purl)
430-
purl = purl_to_dict(purl)
431432
if not with_qualifiers_and_subpath:
432-
del purl["qualifiers"]
433-
del purl["subpath"]
433+
remove_qualifiers_and_subpath(purl)
434+
purl = purl_to_dict(purl)
434435
return self.filter(**purl)
435436

436437
def with_cpes(self):

vulnerabilities/tests/test_view.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def setUp(self):
3434
"pkg:nginx/nginx@1.14.1",
3535
"pkg:nginx/nginx@1.0.7",
3636
"pkg:nginx/nginx@1.0.15",
37+
"pkg:pypi/foo@1",
3738
]
3839
self.packages = packages
3940
for package in packages:
@@ -63,6 +64,65 @@ def test_package_view_with_purl_fragment(self):
6364
self.assertEqual(len(pkgs), 1)
6465
self.assertEqual(pkgs[0].purl, "pkg:nginx/nginx@1.0.15")
6566

67+
def test_package_view_with_purl_fragment(self):
68+
qs = PackageSearch().get_queryset(query="nginx/nginx")
69+
pkgs = list(qs)
70+
pkgs = [p.purl for p in pkgs]
71+
assert pkgs == [
72+
"pkg:nginx/nginx@0.6.18",
73+
"pkg:nginx/nginx@1.20.0",
74+
"pkg:nginx/nginx@1.21.0",
75+
"pkg:nginx/nginx@1.20.1",
76+
"pkg:nginx/nginx@1.9.5",
77+
"pkg:nginx/nginx@1.17.2",
78+
"pkg:nginx/nginx@1.17.3",
79+
"pkg:nginx/nginx@1.16.1",
80+
"pkg:nginx/nginx@1.15.5",
81+
"pkg:nginx/nginx@1.15.6",
82+
"pkg:nginx/nginx@1.14.1",
83+
"pkg:nginx/nginx@1.0.7",
84+
"pkg:nginx/nginx@1.0.15",
85+
]
86+
87+
def test_package_view_with_valid_purl_without_version(self):
88+
qs = PackageSearch().get_queryset(query="pkg:nginx/nginx")
89+
pkgs = list(qs)
90+
pkgs = [p.purl for p in pkgs]
91+
assert pkgs == [
92+
"pkg:nginx/nginx@0.6.18",
93+
"pkg:nginx/nginx@1.20.0",
94+
"pkg:nginx/nginx@1.21.0",
95+
"pkg:nginx/nginx@1.20.1",
96+
"pkg:nginx/nginx@1.9.5",
97+
"pkg:nginx/nginx@1.17.2",
98+
"pkg:nginx/nginx@1.17.3",
99+
"pkg:nginx/nginx@1.16.1",
100+
"pkg:nginx/nginx@1.15.5",
101+
"pkg:nginx/nginx@1.15.6",
102+
"pkg:nginx/nginx@1.14.1",
103+
"pkg:nginx/nginx@1.0.7",
104+
"pkg:nginx/nginx@1.0.15",
105+
]
106+
107+
def test_package_view_with_valid_purl_and_incomplete_version(self):
108+
qs = PackageSearch().get_queryset(query="pkg:nginx/nginx@1")
109+
pkgs = list(qs)
110+
pkgs = [p.purl for p in pkgs]
111+
assert pkgs == [
112+
"pkg:nginx/nginx@1.20.0",
113+
"pkg:nginx/nginx@1.21.0",
114+
"pkg:nginx/nginx@1.20.1",
115+
"pkg:nginx/nginx@1.9.5",
116+
"pkg:nginx/nginx@1.17.2",
117+
"pkg:nginx/nginx@1.17.3",
118+
"pkg:nginx/nginx@1.16.1",
119+
"pkg:nginx/nginx@1.15.5",
120+
"pkg:nginx/nginx@1.15.6",
121+
"pkg:nginx/nginx@1.14.1",
122+
"pkg:nginx/nginx@1.0.7",
123+
"pkg:nginx/nginx@1.0.15",
124+
]
125+
66126

67127
class VulnerabilitySearchTestCase(TestCase):
68128
def setUp(self):

vulnerabilities/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,18 @@ def fetch_response(url):
420420
if response.status_code == 200:
421421
return response
422422
raise Exception(f"Failed to fetch data from {url!r} with status code: {response.status_code!r}")
423+
424+
425+
# This should be a method on PackageURL
426+
def remove_qualifiers_and_subpath(purl):
427+
"""
428+
Return a package URL without qualifiers and subpath
429+
"""
430+
if not isinstance(purl, PackageURL):
431+
purl = PackageURL.from_string(purl)
432+
return PackageURL(
433+
type=purl.type,
434+
namespace=purl.namespace,
435+
name=purl.name,
436+
version=purl.version,
437+
)

0 commit comments

Comments
 (0)