Skip to content

Commit ab9d3c4

Browse files
authored
Merge pull request #932 from TG1999/all_vulnerable_purls
Add API endpoint to get all vulnerable packages #929
2 parents 4e6c4f0 + 20305dd commit ab9d3c4

File tree

5 files changed

+110
-3
lines changed

5 files changed

+110
-3
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Release notes
22
=============
33

4+
5+
Version v31.0.0
6+
----------------
7+
8+
- We added a new "/packages/all" API endpoint to get all Package URLs know to be vulnerable.
9+
10+
411
Version v30.0.0
512
----------------
613

@@ -80,7 +87,6 @@ This is a major version that is not backward compatible.
8087
- The data license is now CC-BY-SA-4.0 as this is the highest common
8188
denominator license among all the data sources we collect and aggregate.
8289

83-
8490
Other:
8591

8692
- We dropped calver to use a plain semver.

vulnerabilities/api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ def bulk_search(self, request):
254254

255255
return Response(response)
256256

257+
@action(detail=False, methods=["get"])
258+
def all(self, request):
259+
vulnerable_packages = Package.objects.vulnerable().only(*PackageURL._fields)
260+
vulnerable_purls = [str(package.purl) for package in vulnerable_packages]
261+
return Response(vulnerable_purls)
262+
257263

258264
class VulnerabilityFilterSet(filters.FilterSet):
259265
class Meta:

vulnerabilities/models.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def vulnerable_to(self):
9494
"""
9595
Return packages that are vulnerable to this vulnerability.
9696
"""
97-
return self.packages.filter(packagerelatedvulnerability__fix=False)
97+
return self.packages.vulnerable()
9898

9999
@property
100100
def resolved_to(self):
@@ -198,6 +198,12 @@ def for_package_url_object(self, purl):
198198
else:
199199
return self.none()
200200

201+
def vulnerable(self):
202+
"""
203+
Return all vulnerable packages.
204+
"""
205+
return Package.objects.filter(packagerelatedvulnerability__fix=False).distinct()
206+
201207

202208
class Package(PackageURLMixin):
203209
"""

vulnerabilities/tests/test_fix_api.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from django.contrib.auth import get_user_model
1313
from django.test import TestCase
1414
from django.test import TransactionTestCase
15-
from django.utils.http import int_to_base36
1615
from packageurl import PackageURL
1716
from rest_framework import status
1817
from rest_framework.test import APIClient
@@ -109,6 +108,7 @@ def setUp(self):
109108
summary="test-vuln",
110109
)
111110
self.vuln = vuln
111+
self.vulnerable_packages = []
112112
for i in range(0, 10):
113113
query_kwargs = dict(
114114
type="generic",
@@ -258,6 +258,29 @@ def test_api_with_single_vulnerability_and_vulnerable_package(self):
258258
],
259259
}
260260

261+
def test_api_with_all_vulnerable_packages(self):
262+
with self.assertNumQueries(4):
263+
# There are 4 queries:
264+
# 1. SAVEPOINT
265+
# 2. Authenticating user
266+
# 3. Get all vulnerable packages
267+
# 4. RELEASE SAVEPOINT
268+
response = self.csrf_client.get(f"/api/packages/all", format="json").data
269+
assert len(response) == 11
270+
assert response == [
271+
"pkg:generic/nginx/test@0",
272+
"pkg:generic/nginx/test@1",
273+
"pkg:generic/nginx/test@11",
274+
"pkg:generic/nginx/test@2",
275+
"pkg:generic/nginx/test@3",
276+
"pkg:generic/nginx/test@4",
277+
"pkg:generic/nginx/test@5",
278+
"pkg:generic/nginx/test@6",
279+
"pkg:generic/nginx/test@7",
280+
"pkg:generic/nginx/test@8",
281+
"pkg:generic/nginx/test@9",
282+
]
283+
261284

262285
class CPEApi(TestCase):
263286
def setUp(self):
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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/nexB/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from django.contrib.auth import get_user_model
11+
from django.test import TestCase
12+
from packageurl import PackageURL
13+
14+
from vulnerabilities.models import Package
15+
from vulnerabilities.models import PackageRelatedVulnerability
16+
from vulnerabilities.models import Vulnerability
17+
18+
User = get_user_model()
19+
20+
21+
class TestPackageModel(TestCase):
22+
def setUp(self):
23+
vuln1 = Vulnerability.objects.create(
24+
summary="test-vuln",
25+
)
26+
vuln2 = Vulnerability.objects.create(
27+
summary="test-vuln1",
28+
)
29+
for i in range(0, 10):
30+
query_kwargs = dict(
31+
type="generic",
32+
namespace="nginx",
33+
name="test",
34+
version=str(i),
35+
qualifiers={},
36+
subpath="",
37+
)
38+
vuln_package = Package.objects.create(**query_kwargs)
39+
# Attaching same package to 2 vulnerabilities
40+
PackageRelatedVulnerability.objects.create(
41+
package=vuln_package,
42+
vulnerability=vuln1,
43+
fix=False,
44+
)
45+
PackageRelatedVulnerability.objects.create(
46+
package=vuln_package,
47+
vulnerability=vuln2,
48+
fix=False,
49+
)
50+
51+
def test_get_vulnerable_packages(self):
52+
vuln_packages = Package.objects.vulnerable()
53+
assert vuln_packages.count() == 10
54+
vuln_purls = [pkg.purl for pkg in vuln_packages.only(*PackageURL._fields)]
55+
assert vuln_purls == [
56+
"pkg:generic/nginx/test@0",
57+
"pkg:generic/nginx/test@1",
58+
"pkg:generic/nginx/test@2",
59+
"pkg:generic/nginx/test@3",
60+
"pkg:generic/nginx/test@4",
61+
"pkg:generic/nginx/test@5",
62+
"pkg:generic/nginx/test@6",
63+
"pkg:generic/nginx/test@7",
64+
"pkg:generic/nginx/test@8",
65+
"pkg:generic/nginx/test@9",
66+
]

0 commit comments

Comments
 (0)