From 656e84f5f9179d9cdb27034731c1a93fdd681b56 Mon Sep 17 00:00:00 2001 From: Arshad Date: Fri, 7 Mar 2025 16:43:12 +0530 Subject: [PATCH 1/2] expose severities inside the affected by packages Signed-off-by: Arshad --- vulnerabilities/api_v2.py | 7 ++++++- vulnerabilities/tests/test_api_v2.py | 11 +++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/vulnerabilities/api_v2.py b/vulnerabilities/api_v2.py index 10ffb6d98..3113a624a 100644 --- a/vulnerabilities/api_v2.py +++ b/vulnerabilities/api_v2.py @@ -205,6 +205,10 @@ def get_affected_by_vulnerabilities(self, obj): purl = None if fixed_by_package: purl = fixed_by_package.package_url + + #exposing severities inside the affectedbyvulnerabilities + severities = VulnerabilitySeverityV2Serializer(vuln.severities.all(), many=True).data + # Get code fixed for a vulnerability code_fixes = CodeFix.objects.filter( affected_package_vulnerability__vulnerability=vuln @@ -216,6 +220,7 @@ def get_affected_by_vulnerabilities(self, obj): result[vuln.vulnerability_id] = { "vulnerability_id": vuln.vulnerability_id, + "severities" : severities, "fixed_by_packages": purl, "code_fixes": code_fix_urls, } @@ -260,7 +265,7 @@ class PackageV2ViewSet(viewsets.ReadOnlyModelViewSet): queryset = Package.objects.all().prefetch_related( Prefetch( "affected_by_vulnerabilities", - queryset=Vulnerability.objects.prefetch_related("fixed_by_packages"), + queryset=Vulnerability.objects.prefetch_related("fixed_by_packages","severities"), to_attr="prefetched_affected_vulnerabilities", ) ) diff --git a/vulnerabilities/tests/test_api_v2.py b/vulnerabilities/tests/test_api_v2.py index e3434c6a9..2099d0e92 100644 --- a/vulnerabilities/tests/test_api_v2.py +++ b/vulnerabilities/tests/test_api_v2.py @@ -216,7 +216,7 @@ def test_list_packages(self): Should return a list of packages with their details and associated vulnerabilities. """ url = reverse("package-v2-list") - with self.assertNumQueries(32): + with self.assertNumQueries(33): response = self.client.get(url, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn("results", response.data) @@ -238,7 +238,7 @@ def test_filter_packages_by_purl(self): Test filtering packages by one or more PURLs. """ url = reverse("package-v2-list") - with self.assertNumQueries(20): + with self.assertNumQueries(21): response = self.client.get(url, {"purl": "pkg:pypi/django@3.2"}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data["results"]["packages"]), 1) @@ -249,7 +249,7 @@ def test_filter_packages_by_affected_vulnerability(self): Test filtering packages by affected_by_vulnerability. """ url = reverse("package-v2-list") - with self.assertNumQueries(20): + with self.assertNumQueries(21): response = self.client.get( url, {"affected_by_vulnerability": "VCID-1234"}, format="json" ) @@ -311,6 +311,7 @@ def test_package_serializer_fields(self): "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", + "severities": [], "fixed_by_packages": None, } } @@ -395,6 +396,7 @@ def test_get_affected_by_vulnerabilities(self): "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", + "severities": [], "fixed_by_packages": None, } }, @@ -601,7 +603,7 @@ def test_lookup_with_valid_purl(self): """ url = reverse("package-v2-lookup") data = {"purl": "pkg:pypi/django@3.2"} - with self.assertNumQueries(13): + with self.assertNumQueries(14): response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(1, len(response.data)) @@ -617,6 +619,7 @@ def test_lookup_with_valid_purl(self): "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", + "severities": [], "fixed_by_packages": None, } }, From 5a57c74b975c927572290384ecd9ce9ed12a7e5c Mon Sep 17 00:00:00 2001 From: Arshad Date: Thu, 20 Mar 2025 02:00:42 +0530 Subject: [PATCH 2/2] modifies tests Signed-off-by: Arshad --- vulnerabilities/tests/test_api_v2.py | 120 ++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 13 deletions(-) diff --git a/vulnerabilities/tests/test_api_v2.py b/vulnerabilities/tests/test_api_v2.py index 2099d0e92..30d5b0203 100644 --- a/vulnerabilities/tests/test_api_v2.py +++ b/vulnerabilities/tests/test_api_v2.py @@ -20,6 +20,7 @@ from vulnerabilities.models import ApiUser from vulnerabilities.models import Package from vulnerabilities.models import Vulnerability +from vulnerabilities.models import VulnerabilitySeverity from vulnerabilities.models import VulnerabilityReference from vulnerabilities.models import Weakness @@ -210,6 +211,16 @@ def setUp(self): self.client = APIClient(enforce_csrf_checks=True) self.client.credentials(HTTP_AUTHORIZATION=self.auth) + #create vulnerability severities + self.severity = VulnerabilitySeverity.objects.create( + scoring_system="CVSSv3", + scoring_elements="", + url="https://example.com", + value="7.5" + ) + + self.vuln1.severities.add(self.severity) + def test_list_packages(self): """ Test listing packages without filters. @@ -275,13 +286,22 @@ def test_package_serializer_fields(self): # Fetch the package package = Package.objects.get(package_url="pkg:pypi/django@3.2") + #retrieving the vulnerability + Vulnerability_with_severities = Vulnerability.objects.get(vulnerability_id="VCID-1234") + Vulnerability_without_severities = Vulnerability.objects.get(vulnerability_id="VCID-5678") + + severity_created = self.severity + Vulnerability_with_severities.severities.add(severity_created) + + package.affected_by_vulnerabilities.add(Vulnerability_with_severities, Vulnerability_without_severities) + # Ensure prefetched data is available for the serializer package = ( Package.objects.filter(package_url="pkg:pypi/django@3.2") .prefetch_related( Prefetch( "affected_by_vulnerabilities", - queryset=Vulnerability.objects.prefetch_related("fixed_by_packages"), + queryset=Vulnerability.objects.prefetch_related("fixed_by_packages","severities"), to_attr="prefetched_affected_vulnerabilities", ) ) @@ -311,9 +331,22 @@ def test_package_serializer_fields(self): "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", - "severities": [], + "severities": [ + { + "url":"https://example.com", + "value": "7.5", + "scoring_system": "CVSSv3", + "scoring_elements": "", + } + ], "fixed_by_packages": None, - } + }, + "VCID-5678": { + "code_fixes": [], + "vulnerability_id": "VCID-5678", + "severities": [], + "fixed_by_packages": "pkg:npm/lodash@4.17.20", + }, } self.assertEqual(data["affected_by_vulnerabilities"], expected_affected_by_vulnerabilities) @@ -376,31 +409,61 @@ def test_get_affected_by_vulnerabilities(self): """ Test the get_affected_by_vulnerabilities method in the serializer. """ + serializer = PackageV2Serializer() + package = Package.objects.get(package_url="pkg:pypi/django@3.2") + + vulnerability_with_severities, vulnerability_without_severities = list(Vulnerability.objects.filter(vulnerability_id__in=["VCID-1234", "VCID-5678"] + ).prefetch_related("fixed_by_packages", "severities")) + package.affected_by_vulnerabilities.add(vulnerability_with_severities, vulnerability_without_severities) + package = ( Package.objects.filter(package_url="pkg:pypi/django@3.2") .prefetch_related( Prefetch( "affected_by_vulnerabilities", - queryset=Vulnerability.objects.prefetch_related("fixed_by_packages"), + queryset=Vulnerability.objects.prefetch_related("fixed_by_packages","severities"), to_attr="prefetched_affected_vulnerabilities", ) ) .first() ) - serializer = PackageV2Serializer() + severity_created = self.severity + vulnerability_with_severities.severities.add(severity_created) + vulnerabilities = serializer.get_affected_by_vulnerabilities(package) + + + for vuln_data in vulnerabilities.values(): + for severity in vuln_data["severities"]: + self.assertIn("url", severity) + self.assertIn("value", severity) + self.assertIn("scoring_system", severity) + self.assertIn("scoring_elements", severity) + self.assertEqual( vulnerabilities, { "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", - "severities": [], + "severities": [ + { + "url":"https://example.com", + "value": "7.5", + "scoring_system": "CVSSv3", + "scoring_elements": "", + } + ], "fixed_by_packages": None, - } + }, + "VCID-5678": { + "code_fixes": [], + "vulnerability_id": "VCID-5678", + "severities": [], #No severity for this vulnerability + "fixed_by_packages": "pkg:npm/lodash@4.17.20", }, - ) + },) def test_get_fixing_vulnerabilities(self): """ @@ -603,7 +666,25 @@ def test_lookup_with_valid_purl(self): """ url = reverse("package-v2-lookup") data = {"purl": "pkg:pypi/django@3.2"} - with self.assertNumQueries(14): + package = Package.objects.filter(package_url="pkg:pypi/django@3.2").prefetch_related( + Prefetch( + "affected_by_vulnerabilities", + queryset=Vulnerability.objects.prefetch_related(Prefetch("fixed_by_packages",queryset=Package.objects.all()), + Prefetch("severities",queryset=VulnerabilitySeverity.objects.all())), + to_attr="prefetched_affected_vulnerabilities", + ) + ).first() + + vulnerability_with_severities, vulnerability_without_severities = list(Vulnerability.objects.filter(vulnerability_id__in=["VCID-1234", "VCID-5678"] + ).prefetch_related("fixed_by_packages", "severities")) + + severity_created = self.severity + vulnerability_with_severities.severities.add(severity_created) + + package.affected_by_vulnerabilities.add(vulnerability_with_severities, vulnerability_without_severities) + + + with self.assertNumQueries(15): response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(1, len(response.data)) @@ -619,11 +700,24 @@ def test_lookup_with_valid_purl(self): "VCID-1234": { "code_fixes": [], "vulnerability_id": "VCID-1234", - "severities": [], + "severities": [ + { + "url":"https://example.com", + "value": "7.5", + "scoring_system": "CVSSv3", + "scoring_elements": '', + } + ], "fixed_by_packages": None, - } - }, - ) + }, + "VCID-5678": { + "code_fixes": [], + "vulnerability_id": "VCID-5678", + "severities": [], + "fixed_by_packages": "pkg:npm/lodash@4.17.20", + }, + }) + self.assertEqual(response.data[0]["fixing_vulnerabilities"], []) def test_lookup_with_invalid_purl(self):