diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py
index 5ff64a1ff..a33e6d714 100644
--- a/vulnerabilities/importers/__init__.py
+++ b/vulnerabilities/importers/__init__.py
@@ -7,38 +7,7 @@
# See https://aboutcode.org for more information about nexB OSS projects.
#
-from vulnerabilities.importers import apache_httpd
-from vulnerabilities.importers import apache_kafka
-from vulnerabilities.importers import apache_tomcat
-from vulnerabilities.importers import archlinux
-from vulnerabilities.importers import curl
-from vulnerabilities.importers import debian
-from vulnerabilities.importers import debian_oval
-from vulnerabilities.importers import elixir_security
-from vulnerabilities.importers import epss
-from vulnerabilities.importers import fireeye
-from vulnerabilities.importers import gentoo
-from vulnerabilities.importers import github_osv
-from vulnerabilities.importers import istio
-from vulnerabilities.importers import mozilla
-from vulnerabilities.importers import oss_fuzz
-from vulnerabilities.importers import postgresql
-from vulnerabilities.importers import project_kb_msr2019
-from vulnerabilities.importers import redhat
-from vulnerabilities.importers import retiredotnet
-from vulnerabilities.importers import ruby
-from vulnerabilities.importers import suse_scores
-from vulnerabilities.importers import ubuntu_usn
-from vulnerabilities.importers import vulnrichment
-from vulnerabilities.importers import xen
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
-from vulnerabilities.pipelines import alpine_linux_importer
-from vulnerabilities.pipelines import github_importer
-from vulnerabilities.pipelines import gitlab_importer
-from vulnerabilities.pipelines import nginx_importer
-from vulnerabilities.pipelines import npm_importer
-from vulnerabilities.pipelines import pypa_importer
-from vulnerabilities.pipelines import pysec_importer
from vulnerabilities.pipelines.v2_importers import alpine_linux_importer as alpine_linux_importer_v2
from vulnerabilities.pipelines.v2_importers import aosp_importer as aosp_importer_v2
from vulnerabilities.pipelines.v2_importers import apache_httpd_importer as apache_httpd_v2
@@ -159,37 +128,6 @@
collect_fix_commits_v2.CollectGitFixCommitsPipeline,
collect_fix_commits_v2.CollectJenkinsFixCommitsPipeline,
collect_fix_commits_v2.CollectGitlabFixCommitsPipeline,
- github_importer.GitHubAPIImporterPipeline,
- gitlab_importer.GitLabImporterPipeline,
- github_osv.GithubOSVImporter,
- pypa_importer.PyPaImporterPipeline,
- npm_importer.NpmImporterPipeline,
- nginx_importer.NginxImporterPipeline,
- pysec_importer.PyPIImporterPipeline,
- apache_tomcat.ApacheTomcatImporter,
- postgresql.PostgreSQLImporter,
- debian.DebianImporter,
- curl.CurlImporter,
- epss.EPSSImporter,
- vulnrichment.VulnrichImporter,
- alpine_linux_importer.AlpineLinuxImporterPipeline,
- apache_kafka.ApacheKafkaImporter,
- ruby.RubyImporter,
- redhat.RedhatImporter,
- archlinux.ArchlinuxImporter,
- debian_oval.DebianOvalImporter,
- retiredotnet.RetireDotnetImporter,
- apache_httpd.ApacheHTTPDImporter,
- mozilla.MozillaImporter,
- gentoo.GentooImporter,
- istio.IstioImporter,
- project_kb_msr2019.ProjectKBMSRImporter,
- suse_scores.SUSESeverityScoreImporter,
- elixir_security.ElixirSecurityImporter,
- xen.XenImporter,
- ubuntu_usn.UbuntuUSNImporter,
- fireeye.FireyeImporter,
- oss_fuzz.OSSFuzzImporter,
]
)
diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py
index 05d08cbbe..51694bde9 100644
--- a/vulnerabilities/improvers/__init__.py
+++ b/vulnerabilities/improvers/__init__.py
@@ -7,15 +7,6 @@
# See https://aboutcode.org for more information about nexB OSS projects.
#
-from vulnerabilities.improvers import valid_versions
-from vulnerabilities.improvers import vulnerability_status
-from vulnerabilities.pipelines import compute_package_risk
-from vulnerabilities.pipelines import compute_package_version_rank
-from vulnerabilities.pipelines import enhance_with_exploitdb
-from vulnerabilities.pipelines import enhance_with_kev
-from vulnerabilities.pipelines import enhance_with_metasploit
-from vulnerabilities.pipelines import flag_ghost_packages
-from vulnerabilities.pipelines import populate_vulnerability_summary_pipeline
from vulnerabilities.pipelines.v2_improvers import archive_urls
from vulnerabilities.pipelines.v2_improvers import collect_ssvc_trees
from vulnerabilities.pipelines.v2_improvers import compute_advisory_todo as compute_advisory_todo_v2
@@ -51,29 +42,6 @@
reference_collect_commits.CollectReferencesFixCommitsPipeline,
enhance_with_github_poc.GithubPocsImproverPipeline,
mark_unfurl_version_range.MarkUnfurlVersionRangePipeline,
- valid_versions.GitHubBasicImprover,
- valid_versions.GitLabBasicImprover,
- valid_versions.NginxBasicImprover,
- valid_versions.ApacheHTTPDImprover,
- valid_versions.DebianBasicImprover,
- valid_versions.NpmImprover,
- valid_versions.ElixirImprover,
- valid_versions.ApacheTomcatImprover,
- valid_versions.ApacheKafkaImprover,
- valid_versions.IstioImprover,
- valid_versions.DebianOvalImprover,
- valid_versions.OSSFuzzImprover,
- valid_versions.RubyImprover,
- valid_versions.GithubOSVImprover,
- vulnerability_status.VulnerabilityStatusImprover,
- valid_versions.CurlImprover,
- flag_ghost_packages.FlagGhostPackagePipeline,
- enhance_with_kev.VulnerabilityKevPipeline,
- enhance_with_metasploit.MetasploitImproverPipeline,
- enhance_with_exploitdb.ExploitDBImproverPipeline,
- compute_package_risk.ComputePackageRiskPipeline,
- compute_package_version_rank.ComputeVersionRankPipeline,
- populate_vulnerability_summary_pipeline.PopulateVulnerabilitySummariesPipeline,
group_advisories_for_packages_v2.GroupAdvisoriesForPackages,
]
)
diff --git a/vulnerabilities/pipelines/__init__.py b/vulnerabilities/pipelines/__init__.py
index 6f03a6d36..5940363b0 100644
--- a/vulnerabilities/pipelines/__init__.py
+++ b/vulnerabilities/pipelines/__init__.py
@@ -291,7 +291,7 @@ class VulnerableCodeBaseImporterPipelineV2(VulnerableCodePipeline):
# To rerun onetime pipeline reset is_active field to True via migration.
run_once = False
# Interval between runs in minutes.
- run_interval = 1440
+ run_interval = 720
run_priority = PipelineSchedule.ExecutionPriority.DEFAULT
@classmethod
diff --git a/vulnerabilities/templates/index.html b/vulnerabilities/templates/index.html
index 77ad8d1c0..962b5f79f 100644
--- a/vulnerabilities/templates/index.html
+++ b/vulnerabilities/templates/index.html
@@ -10,12 +10,7 @@
- {% include "package_search_box.html" %}
-
-
-
-
- {% include "vulnerability_search_box.html" %}
+ {% include "package_search_box_v2.html" %}
@@ -33,9 +28,6 @@
-
- ATTENTION: We will be deprecating V1 and V2 API by 30th June 2026. V3 endpoint is live now. Please migrate to V3 API before the deprecation date. For more details, please refer to this
blog.
-
{% endblock %}
\ No newline at end of file
diff --git a/vulnerabilities/templates/index_v2.html b/vulnerabilities/templates/index_v2.html
deleted file mode 100644
index 962b5f79f..000000000
--- a/vulnerabilities/templates/index_v2.html
+++ /dev/null
@@ -1,33 +0,0 @@
-{% extends "base.html" %}
-{% load widget_tweaks %}
-
-{% block title %}
-VulnerableCode Home
-{% endblock %}
-
-{% block content %}
-
-
-
-
- {% include "package_search_box_v2.html" %}
-
-
-
-
- VulnerableCode aggregates software
- vulnerabilities from multiple public advisory sources
- and presents their details along with their affected
- packages and fixed-by packages identified by
- Package URLs (PURLs).
-
-
- What's new in this Release:
-
- Check out latest updates here!
-
-
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/vulnerabilities/templates/navbar.html b/vulnerabilities/templates/navbar.html
index 815cd6b76..67cf4664e 100644
--- a/vulnerabilities/templates/navbar.html
+++ b/vulnerabilities/templates/navbar.html
@@ -20,15 +20,9 @@
-
-
diff --git a/vulnerabilities/tests/test_api.py b/vulnerabilities/tests/test_api.py
index 63c674547..06443bea3 100644
--- a/vulnerabilities/tests/test_api.py
+++ b/vulnerabilities/tests/test_api.py
@@ -1,1415 +1,1415 @@
-#
-# Copyright (c) nexB Inc. and others. All rights reserved.
-# VulnerableCode is a trademark of nexB Inc.
-# SPDX-License-Identifier: Apache-2.0
-# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
-# See https://github.com/aboutcode-org/vulnerablecode for support or download.
-# See https://aboutcode.org for more information about nexB OSS projects.
-#
-
-import json
-import os
-from urllib.parse import quote
-
-from django.core.cache import cache
-from django.test import TestCase
-from django.test import TransactionTestCase
-from django.test.client import RequestFactory
-from rest_framework import status
-from rest_framework.test import APIClient
-
-from vulnerabilities.api import PackageSerializer
-from vulnerabilities.api import VulnerabilityReferenceSerializer
-from vulnerabilities.models import AffectedByPackageRelatedVulnerability
-from vulnerabilities.models import Alias
-from vulnerabilities.models import ApiUser
-from vulnerabilities.models import FixingPackageRelatedVulnerability
-from vulnerabilities.models import Package
-from vulnerabilities.models import Vulnerability
-from vulnerabilities.models import VulnerabilityReference
-from vulnerabilities.models import VulnerabilityRelatedReference
-from vulnerabilities.models import VulnerabilitySeverity
-from vulnerabilities.models import Weakness
-from vulnerabilities.severity_systems import EPSS
-
-BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-TEST_DATA = os.path.join(BASE_DIR, "test_data")
-TEST_DIR = os.path.join(TEST_DATA, "api")
-
-
-def cleaned_response(response):
- """
- Return a cleaned response suitable for comparison in tests in particular:
- - sort lists with a stable order
- """
- cleaned_response = []
- response_copy = sorted(response, key=lambda x: x.get("purl", ""))
- for package_data in response_copy:
- package_data["unresolved_vulnerabilities"] = sorted(
- package_data["unresolved_vulnerabilities"], key=lambda x: x["vulnerability_id"]
- )
- for index, vulnerability in enumerate(package_data["unresolved_vulnerabilities"]):
- package_data["unresolved_vulnerabilities"][index]["references"] = sorted(
- vulnerability["references"], key=lambda x: (x["reference_id"], x["url"])
- )
- for index2, reference in enumerate(
- package_data["unresolved_vulnerabilities"][index]["references"]
- ):
- reference["scores"] = sorted(
- reference["scores"], key=lambda x: (x["value"], x["scoring_system"])
- )
- package_data["unresolved_vulnerabilities"][index]["references"][index2][
- "scores"
- ] = reference["scores"]
-
- package_data["resolved_vulnerabilities"] = sorted(
- package_data["resolved_vulnerabilities"], key=lambda x: x["vulnerability_id"]
- )
- for index, vulnerability in enumerate(package_data["resolved_vulnerabilities"]):
- package_data["resolved_vulnerabilities"][index]["references"] = sorted(
- vulnerability["references"], key=lambda x: (x["reference_id"], x["url"])
- )
- for index2, reference in enumerate(
- package_data["resolved_vulnerabilities"][index]["references"]
- ):
- reference["scores"] = sorted(
- reference["scores"], key=lambda x: (x["value"], x["scoring_system"])
- )
- package_data["resolved_vulnerabilities"][index]["references"][index2]["scores"] = (
- reference["scores"]
- )
-
- cleaned_response.append(package_data)
-
- return cleaned_response
-
-
-class TestDebianResponse(TransactionTestCase):
- def setUp(self):
- # create one non-debian package called "mimetex" to verify filtering
- Package.objects.create(name="mimetex", version="1.50-1.1", type="deb", namespace="ubuntu")
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.client = APIClient(enforce_csrf_checks=True)
- self.client.credentials(HTTP_AUTHORIZATION=self.auth)
-
- def test_query_qualifier_filtering(self):
- # packages to check filtering with single/multiple and unordered qualifier filtering
- pk_multi_qf = Package.objects.create(
- name="vlc", version="1.50-1.1", type="deb", qualifiers={"foo": "bar", "tar": "ball"}
- )
- pk_single_qf = Package.objects.create(
- name="vlc", version="1.50-1.1", type="deb", qualifiers={"foo": "bar"}
- )
-
- # check filtering when qualifiers are not normalized
- test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar&tar=ball")
- response = self.client.get(
- f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- self.assertEqual(2, response["count"])
-
- test_purl = quote("pkg:deb/vlc@1.50-1.1?tar=ball&foo=bar")
- response = self.client.get(
- f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- self.assertEqual(2, response["count"])
-
- # check filtering when there is intersection of qualifiers between packages
- test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar")
- response = self.client.get(
- f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- self.assertEqual(2, response["count"])
-
- def test_query_by_name(self):
- response = self.client.get(
- "/api/packages/?name=mimetex", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- self.assertEqual(1, response["count"])
-
- first_result = response["results"][0]
- self.assertEqual("mimetex", first_result["name"])
-
- versions = {r["version"] for r in response["results"]}
- self.assertIn("1.50-1.1", versions)
-
- purls = {r["purl"] for r in response["results"]}
- self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1", purls)
-
- def test_query_by_invalid_package_url(self):
- url = "/api/packages/?purl=invalid_purl"
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
-
- self.assertEqual(400, response.status_code)
- self.assertIn("error", response.data)
- error = response.data["error"]
- self.assertIn("invalid_purl", error)
-
- def test_query_by_package_url_without_namespace(self):
- url = "/api/packages/?purl=pkg:deb/mimetex@1.50-1.1"
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT").data
-
- self.assertEqual(1, response["count"])
-
- first_result = response["results"][0]
- self.assertEqual("mimetex", first_result["name"])
-
- purls = {r["purl"] for r in response["results"]}
- self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1", purls)
-
-
-class TestSerializers(TransactionTestCase):
- def setUp(self):
- Package.objects.create(
- name="mimetex",
- version="1.50-1.1",
- type="deb",
- namespace="ubuntu",
- qualifiers={"distro": "jessie"},
- )
- self.ref = VulnerabilityReference.objects.create(
- reference_type="advisory", reference_id="CVE-xxx-xxx", url="https://example.com"
- )
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.client = APIClient(enforce_csrf_checks=True)
- self.client.credentials(HTTP_AUTHORIZATION=self.auth)
-
- def test_package_serializer(self):
- pk = Package.objects.filter(name="mimetex").with_is_vulnerable()
- mock_request = RequestFactory().get("/api")
- response = PackageSerializer(pk, many=True, context={"request": mock_request}).data
- self.assertEqual(1, len(response))
-
- first_result = response[0]
- self.assertEqual("mimetex", first_result["name"])
-
- versions = {r["version"] for r in response}
- self.assertIn("1.50-1.1", versions)
-
- purls = {r["purl"] for r in response}
- self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1?distro=jessie", purls)
-
- def test_vulnerability_reference_serializer(self):
- response = VulnerabilityReferenceSerializer(instance=self.ref).data
- assert response == {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [],
- "url": "https://example.com",
- }
-
-
-class APITestCaseVulnerability(TransactionTestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- for i in range(0, 200):
- Vulnerability.objects.create(
- summary=str(i),
- )
- self.vulnerability = Vulnerability.objects.create(summary="test")
- self.pkg1 = Package.objects.create(name="flask", type="pypi", version="0.1.2")
- self.pkg2 = Package.objects.create(name="flask", type="deb", version="0.1.2")
- for pkg in [self.pkg1, self.pkg2]:
- FixingPackageRelatedVulnerability.objects.create(
- package=pkg, vulnerability=self.vulnerability
- )
-
- self.reference1 = VulnerabilityReference.objects.create(
- reference_id="",
- url="https://.com",
- )
-
- severity = VulnerabilitySeverity.objects.create(
- url="https://.com",
- scoring_system=EPSS.identifier,
- scoring_elements=".0016",
- value="0.526",
- )
-
- VulnerabilityRelatedReference.objects.create(
- reference=self.reference1, vulnerability=self.vulnerability
- )
-
- self.weaknesses = Weakness.objects.create(cwe_id=119)
- self.weaknesses.vulnerabilities.add(self.vulnerability)
- self.invalid_weaknesses = Weakness.objects.create(
- cwe_id=10000
- ) # cwe not present in weaknesses_db
- self.invalid_weaknesses.vulnerabilities.add(self.vulnerability)
- self.vulnerability.severities.add(severity)
-
- def test_api_status(self):
- response = self.csrf_client.get("/api/vulnerabilities/", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- def test_api_response(self):
- response = self.csrf_client.get(
- "/api/vulnerabilities/", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
- self.assertEqual(response["count"], 201)
-
- def test_api_with_single_vulnerability(self):
- response = self.csrf_client.get(
- f"/api/vulnerabilities/{self.vulnerability.id}",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).data
-
- assert response == {
- "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
- "vulnerability_id": self.vulnerability.vulnerability_id,
- "summary": "test",
- "severity_range_score": None,
- "aliases": [],
- "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
- "fixed_packages": [
- {
- "url": f"http://testserver/api/packages/{self.pkg2.id}",
- "purl": "pkg:deb/flask@0.1.2",
- "is_vulnerable": False,
- "affected_by_vulnerabilities": [],
- "resource_url": f"http://testserver/packages/{self.pkg2.purl}",
- },
- {
- "url": f"http://testserver/api/packages/{self.pkg1.id}",
- "purl": "pkg:pypi/flask@0.1.2",
- "is_vulnerable": False,
- "affected_by_vulnerabilities": [],
- "resource_url": f"http://testserver/packages/{self.pkg1.purl}",
- },
- ],
- "affected_packages": [],
- "references": [
- {
- "reference_url": "https://.com",
- "reference_id": "",
- "reference_type": "",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://.com",
- }
- ],
- "weaknesses": [
- {
- "cwe_id": 119,
- "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
- "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
- },
- ],
- "exploits": [],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- }
-
- def test_api_with_single_vulnerability_with_filters(self):
- response = self.csrf_client.get(
- f"/api/vulnerabilities/{self.vulnerability.id}?type=pypi",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).data
- assert response == {
- "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
- "vulnerability_id": self.vulnerability.vulnerability_id,
- "summary": "test",
- "severity_range_score": None,
- "aliases": [],
- "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
- "fixed_packages": [
- {
- "url": f"http://testserver/api/packages/{self.pkg1.id}",
- "purl": "pkg:pypi/flask@0.1.2",
- "is_vulnerable": False,
- "resource_url": f"http://testserver/packages/{self.pkg1.purl}",
- "affected_by_vulnerabilities": [],
- },
- ],
- "affected_packages": [],
- "references": [
- {
- "reference_url": "https://.com",
- "reference_id": "",
- "reference_type": "",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://.com",
- }
- ],
- "weaknesses": [
- {
- "cwe_id": 119,
- "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
- "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
- },
- ],
- "exploits": [],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- }
-
- def test_api_with_single_vulnerability_no_ghost_fix(self):
- self.pkg2.is_ghost = True
- self.pkg1.is_ghost = True
- self.pkg2.save()
- self.pkg1.save()
-
- response = self.csrf_client.get(
- f"/api/vulnerabilities/{self.vulnerability.id}",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).data
-
- expected = {
- "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
- "vulnerability_id": self.vulnerability.vulnerability_id,
- "summary": "test",
- "severity_range_score": None,
- "aliases": [],
- "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
- "fixed_packages": [],
- "affected_packages": [],
- "references": [
- {
- "reference_url": "https://.com",
- "reference_id": "",
- "reference_type": "",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://.com",
- }
- ],
- "weaknesses": [
- {
- "cwe_id": 119,
- "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
- "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
- },
- ],
- "exploits": [],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- }
-
- assert expected == response
-
-
-def set_as_affected_by(package, vulnerability):
- """
- Set the ``package`` Package as affected by the ``vulnerability`` Vulnerability.
- """
- _set_pkg_as(package, vulnerability, fixing=False)
-
-
-def set_as_fixing(package, vulnerability):
- """
- Set the ``package`` Package as fixing the ``vulnerability`` Vulnerability.
- """
- _set_pkg_as(package, vulnerability, fixing=True)
-
-
-def _set_pkg_as(package, vulnerability, fixing=False):
- """
- Set the ``package`` Package as affected or fixing the ``vulnerability`` Vulnerability.
- """
- if fixing:
- FixingPackageRelatedVulnerability.objects.create(
- package=package,
- vulnerability=vulnerability,
- )
- else:
- AffectedByPackageRelatedVulnerability.objects.create(
- package=package,
- vulnerability=vulnerability,
- )
-
-
-def create_vuln(vcid, aliases=()):
- """
- Return a test Vulnerability using the ``vcid`` string as VCID, using optional aliases.
- """
- vuln = Vulnerability.objects.create(summary=f"This is {vcid}", vulnerability_id=vcid)
- add_aliases(vuln, aliases)
- return vuln
-
-
-def add_aliases(vuln, aliases):
- """
- Add aliases to ``vuln`` Vulnerability.
- """
- for alias in aliases:
- Alias.objects.create(alias=alias, vulnerability=vuln)
-
-
-class APIPerformanceTest(TestCase):
- def setUp(self):
- cache.clear()
- self.csrf_client = APIClient(enforce_csrf_checks=True)
-
- # This setup creates the following data:
- # vulnerabilities: vul1, vul2, vul3
- # pkg:maven/com.fasterxml.jackson.core/jackson-databind
- # with these versions:
- # pkg_2_12_6: @ 2.12.6 affected by fixing vul3
- # pkg_2_12_6_1: @ 2.12.6.1 affected by vul2 fixing vul1
- # pkg_2_13_1: @ 2.13.1 affected by vul1 fixing vul3
- # pkg_2_13_2: @ 2.13.2 affected by vul2 fixing vul1
- # pkg_2_14_0_rc1: @ 2.14.0-rc1 affected by fixing
-
- # searched-for pkg's vuln
- self.vul1 = create_vuln("VCID-vul1-vul1-vul1", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"])
- self.vul2 = create_vuln("VCID-vul2-vul2-vul2")
- # This is the vuln fixed by the searched-for pkg -- and by a lesser version (created below),
- # which WILL be included in the API
- self.vul3 = create_vuln("VCID-vul3-vul3-vul3", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"])
-
- from_purl = Package.objects.from_purl
- # lesser-version pkg that also fixes the vuln fixed by the searched-for pkg
- self.pkg_2_12_6 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6")
- # this is a lesser version omitted from the API that fixes searched-for pkg's vuln
- self.pkg_2_12_6_1 = from_purl(
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1"
- )
- # searched-for pkg
- self.pkg_2_13_1 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1")
- # this is a greater version that fixes searched-for pkg's vuln
- self.pkg_2_13_2 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2")
- # This addresses both next and latest non-vulnerable pkg
- self.pkg_2_14_0_rc1 = from_purl(
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
- )
- self.pkg_2_12_6.calculate_version_rank
-
- set_as_fixing(package=self.pkg_2_12_6, vulnerability=self.vul3)
-
- set_as_affected_by(package=self.pkg_2_12_6_1, vulnerability=self.vul2)
- set_as_fixing(package=self.pkg_2_12_6_1, vulnerability=self.vul1)
-
- set_as_affected_by(package=self.pkg_2_13_1, vulnerability=self.vul1)
- set_as_fixing(package=self.pkg_2_13_1, vulnerability=self.vul3)
-
- set_as_affected_by(package=self.pkg_2_13_2, vulnerability=self.vul2)
- set_as_fixing(package=self.pkg_2_13_2, vulnerability=self.vul1)
-
- def test_api_packages_all_num_queries(self):
- with self.assertNumQueries(3):
- # There are 4 queries:
- # 1. SAVEPOINT
- # 2. Authenticating user
- # 3. Get all vulnerable packages
- # 4. RELEASE SAVEPOINT
- response = self.csrf_client.get(
- f"/api/packages/all", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- assert len(response) == 3
- assert list(response) == [
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1",
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- ]
-
- def test_api_packages_single_num_queries(self):
- with self.assertNumQueries(7):
- self.csrf_client.get(
- f"/api/packages/{self.pkg_2_14_0_rc1.id}",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
-
- def test_api_packages_single_with_purl_in_query_num_queries(self):
- with self.assertNumQueries(8):
- self.csrf_client.get(
- f"/api/packages/?purl={self.pkg_2_14_0_rc1.purl}",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
-
- def test_api_packages_single_with_purl_no_version_in_query_num_queries(self):
- with self.assertNumQueries(63):
- self.csrf_client.get(
- f"/api/packages/?purl=pkg:maven/com.fasterxml.jackson.core/jackson-databind",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
-
- def test_api_packages_bulk_search(self):
- with self.assertNumQueries(44):
- packages = [self.pkg_2_12_6, self.pkg_2_12_6_1, self.pkg_2_13_1]
- purls = [p.purl for p in packages]
-
- data = {"purls": purls, "purl_only": False, "plain_purl": True}
-
- resp = self.csrf_client.post(
- f"/api/packages/bulk_search",
- data=json.dumps(data),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- def test_api_packages_with_lookup(self):
- with self.assertNumQueries(13):
- data = {"purl": self.pkg_2_12_6.purl}
-
- resp = self.csrf_client.post(
- f"/api/packages/lookup",
- data=json.dumps(data),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- def test_api_packages_bulk_lookup(self):
- with self.assertNumQueries(44):
- packages = [self.pkg_2_12_6, self.pkg_2_12_6_1, self.pkg_2_13_1]
- purls = [p.purl for p in packages]
-
- data = {"purls": purls}
-
- resp = self.csrf_client.post(
- f"/api/packages/bulk_lookup",
- data=json.dumps(data),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
-
-class APITestCasePackage(TestCase):
- def setUp(self):
- cache.clear()
- self.csrf_client = APIClient(enforce_csrf_checks=True)
-
- # This setup creates the following data:
- # vulnerabilities: vul1, vul2, vul3
- # pkg:maven/com.fasterxml.jackson.core/jackson-databind
- # with these versions:
- # pkg_2_12_6: @ 2.12.6 affected by fixing vul3
- # pkg_2_12_6_1: @ 2.12.6.1 affected by vul2 fixing vul1
- # pkg_2_13_1: @ 2.13.1 affected by vul1 fixing vul3
- # pkg_2_13_2: @ 2.13.2 affected by vul2 fixing vul1
- # pkg_2_14_0_rc1: @ 2.14.0-rc1 affected by fixing
-
- # searched-for pkg's vuln
- self.vul1 = create_vuln("VCID-vul1-vul1-vul1", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"])
- self.vul2 = create_vuln("VCID-vul2-vul2-vul2")
- # This is the vuln fixed by the searched-for pkg -- and by a lesser version (created below),
- # which WILL be included in the API
- self.vul3 = create_vuln("VCID-vul3-vul3-vul3", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"])
-
- from_purl = Package.objects.from_purl
- # lesser-version pkg that also fixes the vuln fixed by the searched-for pkg
- self.pkg_2_12_6 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6")
- # this is a lesser version omitted from the API that fixes searched-for pkg's vuln
- self.pkg_2_12_6_1 = from_purl(
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1"
- )
- # searched-for pkg
- self.pkg_2_13_1 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1")
- # this is a greater version that fixes searched-for pkg's vuln
- self.pkg_2_13_2 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2")
- # This addresses both next and latest non-vulnerable pkg
- self.pkg_2_14_0_rc1 = from_purl(
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
- )
- self.pkg_2_12_6.calculate_version_rank
-
- self.ref = VulnerabilityReference.objects.create(
- reference_type="advisory", reference_id="CVE-xxx-xxx", url="https://example.com"
- )
-
- self.severity = VulnerabilitySeverity.objects.create(
- url="https://example.com",
- scoring_system=EPSS.identifier,
- scoring_elements=".0016",
- value="0.526",
- )
- self.vul1.references.add(self.ref)
- self.vul1.severities.add(self.severity)
-
- self.vul3.references.add(self.ref)
- self.vul3.severities.add(self.severity)
-
- set_as_fixing(package=self.pkg_2_12_6, vulnerability=self.vul3)
-
- set_as_affected_by(package=self.pkg_2_12_6_1, vulnerability=self.vul2)
- set_as_fixing(package=self.pkg_2_12_6_1, vulnerability=self.vul1)
-
- set_as_affected_by(package=self.pkg_2_13_1, vulnerability=self.vul1)
- set_as_fixing(package=self.pkg_2_13_1, vulnerability=self.vul3)
-
- set_as_affected_by(package=self.pkg_2_13_2, vulnerability=self.vul2)
- set_as_fixing(package=self.pkg_2_13_2, vulnerability=self.vul1)
-
- def test_api_with_lesser_and_greater_fixed_by_packages(self):
- response = self.csrf_client.get(
- f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- expected = {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "type": "maven",
- "namespace": "com.fasterxml.jackson.core",
- "name": "jackson-databind",
- "version": "2.13.1",
- "qualifiers": {},
- "subpath": "",
- "is_vulnerable": True,
- "next_non_vulnerable_version": "2.14.0-rc1",
- "latest_non_vulnerable_version": "2.14.0-rc1",
- "affected_by_vulnerabilities": [
- {
- "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
- "vulnerability_id": "VCID-vul1-vul1-vul1",
- "summary": "This is VCID-vul1-vul1-vul1",
- "references": [
- {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://example.com",
- }
- ],
- "fixed_packages": [
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- "is_vulnerable": True,
- "affected_by_vulnerabilities": [
- {"vulnerability": "VCID-vul2-vul2-vul2"}
- ],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- }
- ],
- "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
- }
- ],
- "fixing_vulnerabilities": [
- {
- "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul3.id),
- "vulnerability_id": "VCID-vul3-vul3-vul3",
- "summary": "This is VCID-vul3-vul3-vul3",
- "references": [
- {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://example.com",
- }
- ],
- "fixed_packages": [
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_12_6.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
- "is_vulnerable": False,
- "affected_by_vulnerabilities": [],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
- },
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "is_vulnerable": True,
- "affected_by_vulnerabilities": [
- {"vulnerability": "VCID-vul1-vul1-vul1"}
- ],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- },
- ],
- "aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- "resource_url": "http://testserver/vulnerabilities/VCID-vul3-vul3-vul3",
- }
- ],
- "risk_score": None,
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- }
-
- assert response == expected
-
- def test_is_vulnerable_attribute_only_exists_on_queryset(self):
- assert not hasattr(self.pkg_2_13_1, "is_vulnerable")
- pkgs = Package.objects.filter(pk=self.pkg_2_13_1.pk).with_is_vulnerable()
- assert all(hasattr(p, "is_vulnerable") for p in pkgs)
-
- def test_api_status(self):
- response = self.csrf_client.get(
- "/api/packages/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- def test_api_response(self):
- response = self.csrf_client.get(
- "/api/packages/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
- self.assertEqual(response["count"], 5)
-
- def test_api_with_namespace_filter(self):
- response = self.csrf_client.get(
- "/api/packages/?namespace=com.fasterxml.jackson.core",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).data
- self.assertEqual(response["count"], 5)
-
- def test_api_with_wrong_namespace_filter(self):
- response = self.csrf_client.get(
- "/api/packages/?namespace=foo-bar", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
- self.assertEqual(response["count"], 0)
-
- def test_api_with_all_vulnerable_packages(self):
- with self.assertNumQueries(3):
- # There are 4 queries:
- # 1. SAVEPOINT
- # 2. Authenticating user
- # 3. Get all vulnerable packages
- # 4. RELEASE SAVEPOINT
- response = self.csrf_client.get(
- f"/api/packages/all", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- assert len(response) == 3
- assert list(response) == [
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1",
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- ]
-
- def test_api_with_ignorning_qualifiers(self):
- response = self.csrf_client.get(
- f"/api/packages/?purl=pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1?foo=bar",
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).data
- assert response["count"] == 1
- assert (
- response["results"][0]["purl"]
- == "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
- )
-
- def test_api_with_ghost_package_no_fixing_vulnerabilities(self):
- self.pkg_2_13_1.is_ghost = True
- self.pkg_2_13_1.save()
-
- response = self.csrf_client.get(
- f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- expected = {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "type": "maven",
- "namespace": "com.fasterxml.jackson.core",
- "name": "jackson-databind",
- "version": "2.13.1",
- "qualifiers": {},
- "subpath": "",
- "is_vulnerable": True,
- "next_non_vulnerable_version": "2.12.6",
- "latest_non_vulnerable_version": "2.14.0-rc1",
- "affected_by_vulnerabilities": [
- {
- "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
- "vulnerability_id": "VCID-vul1-vul1-vul1",
- "summary": "This is VCID-vul1-vul1-vul1",
- "references": [
- {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://example.com",
- }
- ],
- "fixed_packages": [
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- "is_vulnerable": True,
- "affected_by_vulnerabilities": [
- {"vulnerability": "VCID-vul2-vul2-vul2"}
- ],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- }
- ],
- "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
- }
- ],
- "fixing_vulnerabilities": [],
- "risk_score": None,
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- }
-
- assert response == expected
-
- def test_api_with_ghost_package_no_next_latest_non_vulnerabilities(self):
- self.pkg_2_14_0_rc1.is_ghost = True
- self.pkg_2_14_0_rc1.save()
-
- response = self.csrf_client.get(
- f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
-
- expected = {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "type": "maven",
- "namespace": "com.fasterxml.jackson.core",
- "name": "jackson-databind",
- "version": "2.13.1",
- "qualifiers": {},
- "subpath": "",
- "is_vulnerable": True,
- "next_non_vulnerable_version": None,
- "latest_non_vulnerable_version": None,
- "affected_by_vulnerabilities": [
- {
- "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
- "vulnerability_id": "VCID-vul1-vul1-vul1",
- "summary": "This is VCID-vul1-vul1-vul1",
- "references": [
- {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://example.com",
- }
- ],
- "fixed_packages": [
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- "is_vulnerable": True,
- "affected_by_vulnerabilities": [
- {"vulnerability": "VCID-vul2-vul2-vul2"}
- ],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
- }
- ],
- "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
- }
- ],
- "fixing_vulnerabilities": [
- {
- "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul3.id),
- "vulnerability_id": "VCID-vul3-vul3-vul3",
- "summary": "This is VCID-vul3-vul3-vul3",
- "references": [
- {
- "reference_url": "https://example.com",
- "reference_id": "CVE-xxx-xxx",
- "reference_type": "advisory",
- "scores": [
- {
- "value": "0.526",
- "scoring_system": "epss",
- "scoring_elements": ".0016",
- }
- ],
- "url": "https://example.com",
- }
- ],
- "fixed_packages": [
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_12_6.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
- "is_vulnerable": False,
- "affected_by_vulnerabilities": [],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
- },
- {
- "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
- "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- "is_vulnerable": True,
- "affected_by_vulnerabilities": [
- {"vulnerability": "VCID-vul1-vul1-vul1"}
- ],
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- },
- ],
- "aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"],
- "risk_score": None,
- "exploitability": None,
- "weighted_severity": None,
- "resource_url": "http://testserver/vulnerabilities/VCID-vul3-vul3-vul3",
- }
- ],
- "risk_score": None,
- "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
- }
-
- assert response == expected
-
-
-class CPEApi(TestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- self.vulnerability = Vulnerability.objects.create(summary="test")
- for i in range(0, 10):
- ref, _ = VulnerabilityReference.objects.get_or_create(
- reference_id=f"cpe:/a:nginx:{i}",
- url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query=cpe:/a:nginx:{i}",
- )
- VulnerabilityRelatedReference.objects.create(
- reference=ref, vulnerability=self.vulnerability
- )
-
- def test_api_status(self):
- response = self.csrf_client.get(
- "/api/cpes/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- def test_api_response(self):
- response = self.csrf_client.get(
- "/api/cpes/?cpe=cpe:/a:nginx:9", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
- self.assertEqual(response["count"], 1)
-
-
-class TestCPEApiWithPackageVulnerabilityRelation(TestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- self.vulnerability = Vulnerability.objects.create(summary="test")
- self.affected_package, _ = Package.objects.get_or_create_from_purl(
- purl="pkg:nginx/nginx@v3.4"
- )
- self.fixed_package, _ = Package.objects.get_or_create_from_purl(purl="pkg:nginx/nginx@v4.0")
- AffectedByPackageRelatedVulnerability.objects.create(
- vulnerability=self.vulnerability,
- created_by="test",
- package=self.affected_package,
- confidence=100,
- )
- FixingPackageRelatedVulnerability.objects.create(
- vulnerability=self.vulnerability,
- created_by="test",
- package=self.fixed_package,
- confidence=100,
- )
- for i in range(0, 10):
- ref, _ = VulnerabilityReference.objects.get_or_create(
- reference_id=f"cpe:/a:nginx:{i}",
- url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query=cpe:/a:nginx:{i}",
- )
- VulnerabilityRelatedReference.objects.create(
- reference=ref, vulnerability=self.vulnerability
- )
-
- def test_cpe_api(self):
- response = self.csrf_client.get(
- "/api/cpes/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- response_data = response.json()
- self.assertEqual(1, response_data["count"])
-
-
-class AliasApi(TestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- self.vulnerability = Vulnerability.objects.create(summary="test")
- for i in range(0, 10):
- Alias.objects.create(alias=f"CVE-{i}", vulnerability=self.vulnerability)
-
- def test_api_status(self):
- response = self.csrf_client.get(
- "/api/aliases/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- def test_api_response(self):
- response = self.csrf_client.get(
- "/api/aliases?alias=CVE-9", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- ).data
- self.assertEqual(response["count"], 1)
-
-
-class BulkSearchAPIPackage(TestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- packages = self.packages = [
- "pkg:nginx/nginx@0.6.18",
- "pkg:nginx/nginx@1.20.0",
- "pkg:nginx/nginx@1.21.0",
- "pkg:nginx/nginx@1.20.1",
- "pkg:nginx/nginx@1.9.5",
- "pkg:nginx/nginx@1.17.2",
- "pkg:nginx/nginx@1.17.3",
- "pkg:nginx/nginx@1.16.1",
- "pkg:nginx/nginx@1.15.5",
- "pkg:nginx/nginx@1.15.6",
- "pkg:nginx/nginx@1.14.1",
- "pkg:nginx/nginx@1.0.7",
- "pkg:nginx/nginx@1.0.15",
- ]
- self.pkgs = [Package.objects.from_purl(p) for p in packages]
-
- vulnerable_packages = [
- "pkg:nginx/nginx@1.0.15?foo=bar",
- "pkg:nginx/nginx@1.0.15?foo=baz",
- ]
-
- vulnerability = Vulnerability.objects.create(summary="test")
-
- for purl in vulnerable_packages:
- package = Package.objects.from_purl(purl)
- set_as_affected_by(package, vulnerability)
-
- def test_bulk_api_response(self):
- request_body = {
- "purls": self.packages,
- }
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 13
-
- def test_bulk_api_response_with_ignoring_qualifiers(self):
- request_body = {"purls": ["pkg:nginx/nginx@1.0.15?qualifiers=dev"], "plain_purl": True}
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
- assert response[0]["purl"] == "pkg:nginx/nginx@1.0.15"
-
- def test_bulk_api_response_with_ignoring_subpath(self):
- request_body = {"purls": ["pkg:nginx/nginx@1.0.15#dev/subpath"], "plain_purl": True}
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
- assert response[0]["purl"] == "pkg:nginx/nginx@1.0.15"
-
- def test_bulk_api_with_purl_only_option(self):
- request_body = {
- "purls": ["pkg:nginx/nginx@1.0.15#dev/subpath"],
- "purl_only": True,
- "plain_purl": True,
- }
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
- assert response[0] == "pkg:nginx/nginx@1.0.15"
-
- def test_bulk_api_without_purls_list(self):
- request_body = {
- "purls": None,
- }
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- expected = {
- "error": {"purls": ["This field may not be null."]},
- "message": "A non-empty 'purls' list of PURLs is required.",
- }
-
- self.assertEqual(response, expected)
-
- def test_bulk_api_without_purls_empty_list(self):
- request_body = {
- "purls": [],
- }
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- expected = {
- "error": {"purls": ["This list may not be empty."]},
- "message": "A non-empty 'purls' list of PURLs is required.",
- }
-
- self.assertEqual(response, expected)
-
- def test_bulk_api_with_empty_request_body(self):
- request_body = {}
- response = self.csrf_client.post(
- "/api/packages/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- expected = {
- "error": {"purls": ["This field is required."]},
- "message": "A non-empty 'purls' list of PURLs is required.",
- }
-
- self.assertEqual(response, expected)
-
-
-class BulkSearchAPICPE(TestCase):
- def setUp(self):
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
- self.exclusive_cpes = [
- "cpe:/a:nginx:1.0.7",
- "cpe:/a:nginx:1.0.15",
- "cpe:/a:nginx:1.14.1",
- "cpe:/a:nginx:1.15.5",
- "cpe:/a:nginx:1.15.6",
- ]
- vuln = Vulnerability.objects.create(summary="test")
- for cpe in self.exclusive_cpes:
- ref = VulnerabilityReference.objects.create(
- reference_id=cpe,
- url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query={cpe}",
- )
- VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=vuln)
- second_vuln = Vulnerability.objects.create(summary="test-A")
- self.non_exclusive_cpes = [
- "cpe:/a:nginx:1.16.1",
- "cpe:/a:nginx:1.17.2",
- "cpe:/a:nginx:1.17.3",
- "cpe:/a:nginx:1.9.5",
- "cpe:/a:nginx:1.20.1",
- "cpe:/a:nginx:1.20.0",
- "cpe:/a:nginx:1.21.0",
- ]
- third_vuln = Vulnerability.objects.create(summary="test-B")
- for cpe in self.non_exclusive_cpes:
- ref = VulnerabilityReference.objects.create(
- reference_id=cpe,
- url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query={cpe}",
- )
- VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=second_vuln)
- VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=third_vuln)
-
- def test_api_response_with_with_exclusive_cpes_associated_with_two_vulnerabilities(self):
- request_body = {
- "cpes": self.exclusive_cpes,
- }
- response = self.csrf_client.post(
- "/api/cpes/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
- assert response[0]["summary"] == "test"
- references_in_vuln = response[0]["references"]
- cpes = [ref["reference_id"] for ref in references_in_vuln]
- assert set(cpes) == set(self.exclusive_cpes)
-
- def test_api_response_with_no_cpe_associated(self):
- request_body = {
- "cpes": ["cpe:/a:nginx:1.10.7"],
- }
- response = self.csrf_client.post(
- "/api/cpes/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 0
-
- def test_api_response_with_with_non_exclusive_cpes_associated_with_two_vulnerabilities(self):
- request_body = {
- "cpes": self.non_exclusive_cpes,
- }
- response = self.csrf_client.post(
- "/api/cpes/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 2
-
- def test_with_empty_list(self):
- request_body = {
- "cpes": [],
- }
- response = self.csrf_client.post(
- "/api/cpes/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert response == {"Error": "A non-empty 'cpes' list of CPEs is required."}
-
- def test_with_invalid_cpes(self):
- request_body = {"cpes": ["CVE-2022-2022"]}
- response = self.csrf_client.post(
- "/api/cpes/bulk_search",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert response == {"Error": "Invalid CPE: CVE-2022-2022"}
-
-
-class TesBanUserAgent(TestCase):
- def test_ban_request_with_bytedance_user_agent(self):
- response = self.client.get(f"/api/packages", format="json", HTTP_USER_AGENT="bytedance")
- assert 404 == response.status_code
-
-
-class TestLookup(TestCase):
- def setUp(self):
- Package.objects.create(
- type="pypi", namespace="", name="microweber/microweber", version="1.2"
- )
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.csrf_client = APIClient(enforce_csrf_checks=True)
- self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
-
- def test_lookup_endpoint_failure(self):
- request_body = {"purl": None}
- response = self.csrf_client.post(
- "/api/packages/lookup",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- expected = {
- "error": {"purl": ["This field may not be null."]},
- "message": "A 'purl' is required.",
- }
-
- self.assertEqual(response, expected)
-
- def test_lookup_endpoint(self):
- request_body = {"purl": "pkg:pypi/microweber/microweber@1.2"}
- response = self.csrf_client.post(
- "/api/packages/lookup",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
- assert response[0]["purl"] == "pkg:pypi/microweber/microweber@1.2"
-
- def test_bulk_lookup_endpoint(self):
- request_body = {
- "purls": [
- "pkg:pypi/microweber/microweber@1.2?foo=bar",
- "pkg:pypi/microweber/microweber@1.2",
- "pkg:pypi/foo/bar@1.0",
- ],
- }
- response = self.csrf_client.post(
- "/api/packages/bulk_lookup",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
- assert len(response) == 1
-
- def test_bulk_lookup_endpoint_failure(self):
- request_body = {"purls": None}
- response = self.csrf_client.post(
- "/api/packages/bulk_lookup",
- data=json.dumps(request_body),
- content_type="application/json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- ).json()
-
- expected = {
- "error": {"purls": ["This field may not be null."]},
- "message": "A non-empty 'purls' list of PURLs is required.",
- }
-
- self.assertEqual(response, expected)
+# #
+# # Copyright (c) nexB Inc. and others. All rights reserved.
+# # VulnerableCode is a trademark of nexB Inc.
+# # SPDX-License-Identifier: Apache-2.0
+# # See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
+# # See https://github.com/aboutcode-org/vulnerablecode for support or download.
+# # See https://aboutcode.org for more information about nexB OSS projects.
+# #
+
+# import json
+# import os
+# from urllib.parse import quote
+
+# from django.core.cache import cache
+# from django.test import TestCase
+# from django.test import TransactionTestCase
+# from django.test.client import RequestFactory
+# from rest_framework import status
+# from rest_framework.test import APIClient
+
+# from vulnerabilities.api import PackageSerializer
+# from vulnerabilities.api import VulnerabilityReferenceSerializer
+# from vulnerabilities.models import AffectedByPackageRelatedVulnerability
+# from vulnerabilities.models import Alias
+# from vulnerabilities.models import ApiUser
+# from vulnerabilities.models import FixingPackageRelatedVulnerability
+# from vulnerabilities.models import Package
+# from vulnerabilities.models import Vulnerability
+# from vulnerabilities.models import VulnerabilityReference
+# from vulnerabilities.models import VulnerabilityRelatedReference
+# from vulnerabilities.models import VulnerabilitySeverity
+# from vulnerabilities.models import Weakness
+# from vulnerabilities.severity_systems import EPSS
+
+# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+# TEST_DATA = os.path.join(BASE_DIR, "test_data")
+# TEST_DIR = os.path.join(TEST_DATA, "api")
+
+
+# def cleaned_response(response):
+# """
+# Return a cleaned response suitable for comparison in tests in particular:
+# - sort lists with a stable order
+# """
+# cleaned_response = []
+# response_copy = sorted(response, key=lambda x: x.get("purl", ""))
+# for package_data in response_copy:
+# package_data["unresolved_vulnerabilities"] = sorted(
+# package_data["unresolved_vulnerabilities"], key=lambda x: x["vulnerability_id"]
+# )
+# for index, vulnerability in enumerate(package_data["unresolved_vulnerabilities"]):
+# package_data["unresolved_vulnerabilities"][index]["references"] = sorted(
+# vulnerability["references"], key=lambda x: (x["reference_id"], x["url"])
+# )
+# for index2, reference in enumerate(
+# package_data["unresolved_vulnerabilities"][index]["references"]
+# ):
+# reference["scores"] = sorted(
+# reference["scores"], key=lambda x: (x["value"], x["scoring_system"])
+# )
+# package_data["unresolved_vulnerabilities"][index]["references"][index2][
+# "scores"
+# ] = reference["scores"]
+
+# package_data["resolved_vulnerabilities"] = sorted(
+# package_data["resolved_vulnerabilities"], key=lambda x: x["vulnerability_id"]
+# )
+# for index, vulnerability in enumerate(package_data["resolved_vulnerabilities"]):
+# package_data["resolved_vulnerabilities"][index]["references"] = sorted(
+# vulnerability["references"], key=lambda x: (x["reference_id"], x["url"])
+# )
+# for index2, reference in enumerate(
+# package_data["resolved_vulnerabilities"][index]["references"]
+# ):
+# reference["scores"] = sorted(
+# reference["scores"], key=lambda x: (x["value"], x["scoring_system"])
+# )
+# package_data["resolved_vulnerabilities"][index]["references"][index2]["scores"] = (
+# reference["scores"]
+# )
+
+# cleaned_response.append(package_data)
+
+# return cleaned_response
+
+
+# class TestDebianResponse(TransactionTestCase):
+# def setUp(self):
+# # create one non-debian package called "mimetex" to verify filtering
+# Package.objects.create(name="mimetex", version="1.50-1.1", type="deb", namespace="ubuntu")
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.client = APIClient(enforce_csrf_checks=True)
+# self.client.credentials(HTTP_AUTHORIZATION=self.auth)
+
+# def test_query_qualifier_filtering(self):
+# # packages to check filtering with single/multiple and unordered qualifier filtering
+# pk_multi_qf = Package.objects.create(
+# name="vlc", version="1.50-1.1", type="deb", qualifiers={"foo": "bar", "tar": "ball"}
+# )
+# pk_single_qf = Package.objects.create(
+# name="vlc", version="1.50-1.1", type="deb", qualifiers={"foo": "bar"}
+# )
+
+# # check filtering when qualifiers are not normalized
+# test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar&tar=ball")
+# response = self.client.get(
+# f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# self.assertEqual(2, response["count"])
+
+# test_purl = quote("pkg:deb/vlc@1.50-1.1?tar=ball&foo=bar")
+# response = self.client.get(
+# f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# self.assertEqual(2, response["count"])
+
+# # check filtering when there is intersection of qualifiers between packages
+# test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar")
+# response = self.client.get(
+# f"/api/packages/?purl={test_purl}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# self.assertEqual(2, response["count"])
+
+# def test_query_by_name(self):
+# response = self.client.get(
+# "/api/packages/?name=mimetex", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# self.assertEqual(1, response["count"])
+
+# first_result = response["results"][0]
+# self.assertEqual("mimetex", first_result["name"])
+
+# versions = {r["version"] for r in response["results"]}
+# self.assertIn("1.50-1.1", versions)
+
+# purls = {r["purl"] for r in response["results"]}
+# self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1", purls)
+
+# def test_query_by_invalid_package_url(self):
+# url = "/api/packages/?purl=invalid_purl"
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+
+# self.assertEqual(400, response.status_code)
+# self.assertIn("error", response.data)
+# error = response.data["error"]
+# self.assertIn("invalid_purl", error)
+
+# def test_query_by_package_url_without_namespace(self):
+# url = "/api/packages/?purl=pkg:deb/mimetex@1.50-1.1"
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT").data
+
+# self.assertEqual(1, response["count"])
+
+# first_result = response["results"][0]
+# self.assertEqual("mimetex", first_result["name"])
+
+# purls = {r["purl"] for r in response["results"]}
+# self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1", purls)
+
+
+# class TestSerializers(TransactionTestCase):
+# def setUp(self):
+# Package.objects.create(
+# name="mimetex",
+# version="1.50-1.1",
+# type="deb",
+# namespace="ubuntu",
+# qualifiers={"distro": "jessie"},
+# )
+# self.ref = VulnerabilityReference.objects.create(
+# reference_type="advisory", reference_id="CVE-xxx-xxx", url="https://example.com"
+# )
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.client = APIClient(enforce_csrf_checks=True)
+# self.client.credentials(HTTP_AUTHORIZATION=self.auth)
+
+# def test_package_serializer(self):
+# pk = Package.objects.filter(name="mimetex").with_is_vulnerable()
+# mock_request = RequestFactory().get("/api")
+# response = PackageSerializer(pk, many=True, context={"request": mock_request}).data
+# self.assertEqual(1, len(response))
+
+# first_result = response[0]
+# self.assertEqual("mimetex", first_result["name"])
+
+# versions = {r["version"] for r in response}
+# self.assertIn("1.50-1.1", versions)
+
+# purls = {r["purl"] for r in response}
+# self.assertIn("pkg:deb/ubuntu/mimetex@1.50-1.1?distro=jessie", purls)
+
+# def test_vulnerability_reference_serializer(self):
+# response = VulnerabilityReferenceSerializer(instance=self.ref).data
+# assert response == {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [],
+# "url": "https://example.com",
+# }
+
+
+# class APITestCaseVulnerability(TransactionTestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# for i in range(0, 200):
+# Vulnerability.objects.create(
+# summary=str(i),
+# )
+# self.vulnerability = Vulnerability.objects.create(summary="test")
+# self.pkg1 = Package.objects.create(name="flask", type="pypi", version="0.1.2")
+# self.pkg2 = Package.objects.create(name="flask", type="deb", version="0.1.2")
+# for pkg in [self.pkg1, self.pkg2]:
+# FixingPackageRelatedVulnerability.objects.create(
+# package=pkg, vulnerability=self.vulnerability
+# )
+
+# self.reference1 = VulnerabilityReference.objects.create(
+# reference_id="",
+# url="https://.com",
+# )
+
+# severity = VulnerabilitySeverity.objects.create(
+# url="https://.com",
+# scoring_system=EPSS.identifier,
+# scoring_elements=".0016",
+# value="0.526",
+# )
+
+# VulnerabilityRelatedReference.objects.create(
+# reference=self.reference1, vulnerability=self.vulnerability
+# )
+
+# self.weaknesses = Weakness.objects.create(cwe_id=119)
+# self.weaknesses.vulnerabilities.add(self.vulnerability)
+# self.invalid_weaknesses = Weakness.objects.create(
+# cwe_id=10000
+# ) # cwe not present in weaknesses_db
+# self.invalid_weaknesses.vulnerabilities.add(self.vulnerability)
+# self.vulnerability.severities.add(severity)
+
+# def test_api_status(self):
+# response = self.csrf_client.get("/api/vulnerabilities/", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+# def test_api_response(self):
+# response = self.csrf_client.get(
+# "/api/vulnerabilities/", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+# self.assertEqual(response["count"], 201)
+
+# def test_api_with_single_vulnerability(self):
+# response = self.csrf_client.get(
+# f"/api/vulnerabilities/{self.vulnerability.id}",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).data
+
+# assert response == {
+# "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
+# "vulnerability_id": self.vulnerability.vulnerability_id,
+# "summary": "test",
+# "severity_range_score": None,
+# "aliases": [],
+# "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
+# "fixed_packages": [
+# {
+# "url": f"http://testserver/api/packages/{self.pkg2.id}",
+# "purl": "pkg:deb/flask@0.1.2",
+# "is_vulnerable": False,
+# "affected_by_vulnerabilities": [],
+# "resource_url": f"http://testserver/packages/{self.pkg2.purl}",
+# },
+# {
+# "url": f"http://testserver/api/packages/{self.pkg1.id}",
+# "purl": "pkg:pypi/flask@0.1.2",
+# "is_vulnerable": False,
+# "affected_by_vulnerabilities": [],
+# "resource_url": f"http://testserver/packages/{self.pkg1.purl}",
+# },
+# ],
+# "affected_packages": [],
+# "references": [
+# {
+# "reference_url": "https://.com",
+# "reference_id": "",
+# "reference_type": "",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://.com",
+# }
+# ],
+# "weaknesses": [
+# {
+# "cwe_id": 119,
+# "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
+# "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
+# },
+# ],
+# "exploits": [],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# }
+
+# def test_api_with_single_vulnerability_with_filters(self):
+# response = self.csrf_client.get(
+# f"/api/vulnerabilities/{self.vulnerability.id}?type=pypi",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).data
+# assert response == {
+# "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
+# "vulnerability_id": self.vulnerability.vulnerability_id,
+# "summary": "test",
+# "severity_range_score": None,
+# "aliases": [],
+# "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
+# "fixed_packages": [
+# {
+# "url": f"http://testserver/api/packages/{self.pkg1.id}",
+# "purl": "pkg:pypi/flask@0.1.2",
+# "is_vulnerable": False,
+# "resource_url": f"http://testserver/packages/{self.pkg1.purl}",
+# "affected_by_vulnerabilities": [],
+# },
+# ],
+# "affected_packages": [],
+# "references": [
+# {
+# "reference_url": "https://.com",
+# "reference_id": "",
+# "reference_type": "",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://.com",
+# }
+# ],
+# "weaknesses": [
+# {
+# "cwe_id": 119,
+# "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
+# "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
+# },
+# ],
+# "exploits": [],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# }
+
+# def test_api_with_single_vulnerability_no_ghost_fix(self):
+# self.pkg2.is_ghost = True
+# self.pkg1.is_ghost = True
+# self.pkg2.save()
+# self.pkg1.save()
+
+# response = self.csrf_client.get(
+# f"/api/vulnerabilities/{self.vulnerability.id}",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).data
+
+# expected = {
+# "url": f"http://testserver/api/vulnerabilities/{self.vulnerability.id}",
+# "vulnerability_id": self.vulnerability.vulnerability_id,
+# "summary": "test",
+# "severity_range_score": None,
+# "aliases": [],
+# "resource_url": f"http://testserver/vulnerabilities/{self.vulnerability.vulnerability_id}",
+# "fixed_packages": [],
+# "affected_packages": [],
+# "references": [
+# {
+# "reference_url": "https://.com",
+# "reference_id": "",
+# "reference_type": "",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://.com",
+# }
+# ],
+# "weaknesses": [
+# {
+# "cwe_id": 119,
+# "name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
+# "description": "The product performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
+# },
+# ],
+# "exploits": [],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# }
+
+# assert expected == response
+
+
+# def set_as_affected_by(package, vulnerability):
+# """
+# Set the ``package`` Package as affected by the ``vulnerability`` Vulnerability.
+# """
+# _set_pkg_as(package, vulnerability, fixing=False)
+
+
+# def set_as_fixing(package, vulnerability):
+# """
+# Set the ``package`` Package as fixing the ``vulnerability`` Vulnerability.
+# """
+# _set_pkg_as(package, vulnerability, fixing=True)
+
+
+# def _set_pkg_as(package, vulnerability, fixing=False):
+# """
+# Set the ``package`` Package as affected or fixing the ``vulnerability`` Vulnerability.
+# """
+# if fixing:
+# FixingPackageRelatedVulnerability.objects.create(
+# package=package,
+# vulnerability=vulnerability,
+# )
+# else:
+# AffectedByPackageRelatedVulnerability.objects.create(
+# package=package,
+# vulnerability=vulnerability,
+# )
+
+
+# def create_vuln(vcid, aliases=()):
+# """
+# Return a test Vulnerability using the ``vcid`` string as VCID, using optional aliases.
+# """
+# vuln = Vulnerability.objects.create(summary=f"This is {vcid}", vulnerability_id=vcid)
+# add_aliases(vuln, aliases)
+# return vuln
+
+
+# def add_aliases(vuln, aliases):
+# """
+# Add aliases to ``vuln`` Vulnerability.
+# """
+# for alias in aliases:
+# Alias.objects.create(alias=alias, vulnerability=vuln)
+
+
+# class APIPerformanceTest(TestCase):
+# def setUp(self):
+# cache.clear()
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+
+# # This setup creates the following data:
+# # vulnerabilities: vul1, vul2, vul3
+# # pkg:maven/com.fasterxml.jackson.core/jackson-databind
+# # with these versions:
+# # pkg_2_12_6: @ 2.12.6 affected by fixing vul3
+# # pkg_2_12_6_1: @ 2.12.6.1 affected by vul2 fixing vul1
+# # pkg_2_13_1: @ 2.13.1 affected by vul1 fixing vul3
+# # pkg_2_13_2: @ 2.13.2 affected by vul2 fixing vul1
+# # pkg_2_14_0_rc1: @ 2.14.0-rc1 affected by fixing
+
+# # searched-for pkg's vuln
+# self.vul1 = create_vuln("VCID-vul1-vul1-vul1", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"])
+# self.vul2 = create_vuln("VCID-vul2-vul2-vul2")
+# # This is the vuln fixed by the searched-for pkg -- and by a lesser version (created below),
+# # which WILL be included in the API
+# self.vul3 = create_vuln("VCID-vul3-vul3-vul3", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"])
+
+# from_purl = Package.objects.from_purl
+# # lesser-version pkg that also fixes the vuln fixed by the searched-for pkg
+# self.pkg_2_12_6 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6")
+# # this is a lesser version omitted from the API that fixes searched-for pkg's vuln
+# self.pkg_2_12_6_1 = from_purl(
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1"
+# )
+# # searched-for pkg
+# self.pkg_2_13_1 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1")
+# # this is a greater version that fixes searched-for pkg's vuln
+# self.pkg_2_13_2 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2")
+# # This addresses both next and latest non-vulnerable pkg
+# self.pkg_2_14_0_rc1 = from_purl(
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
+# )
+# self.pkg_2_12_6.calculate_version_rank
+
+# set_as_fixing(package=self.pkg_2_12_6, vulnerability=self.vul3)
+
+# set_as_affected_by(package=self.pkg_2_12_6_1, vulnerability=self.vul2)
+# set_as_fixing(package=self.pkg_2_12_6_1, vulnerability=self.vul1)
+
+# set_as_affected_by(package=self.pkg_2_13_1, vulnerability=self.vul1)
+# set_as_fixing(package=self.pkg_2_13_1, vulnerability=self.vul3)
+
+# set_as_affected_by(package=self.pkg_2_13_2, vulnerability=self.vul2)
+# set_as_fixing(package=self.pkg_2_13_2, vulnerability=self.vul1)
+
+# def test_api_packages_all_num_queries(self):
+# with self.assertNumQueries(3):
+# # There are 4 queries:
+# # 1. SAVEPOINT
+# # 2. Authenticating user
+# # 3. Get all vulnerable packages
+# # 4. RELEASE SAVEPOINT
+# response = self.csrf_client.get(
+# f"/api/packages/all", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# assert len(response) == 3
+# assert list(response) == [
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1",
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# ]
+
+# def test_api_packages_single_num_queries(self):
+# with self.assertNumQueries(7):
+# self.csrf_client.get(
+# f"/api/packages/{self.pkg_2_14_0_rc1.id}",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+
+# def test_api_packages_single_with_purl_in_query_num_queries(self):
+# with self.assertNumQueries(8):
+# self.csrf_client.get(
+# f"/api/packages/?purl={self.pkg_2_14_0_rc1.purl}",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+
+# def test_api_packages_single_with_purl_no_version_in_query_num_queries(self):
+# with self.assertNumQueries(63):
+# self.csrf_client.get(
+# f"/api/packages/?purl=pkg:maven/com.fasterxml.jackson.core/jackson-databind",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+
+# def test_api_packages_bulk_search(self):
+# with self.assertNumQueries(44):
+# packages = [self.pkg_2_12_6, self.pkg_2_12_6_1, self.pkg_2_13_1]
+# purls = [p.purl for p in packages]
+
+# data = {"purls": purls, "purl_only": False, "plain_purl": True}
+
+# resp = self.csrf_client.post(
+# f"/api/packages/bulk_search",
+# data=json.dumps(data),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# def test_api_packages_with_lookup(self):
+# with self.assertNumQueries(13):
+# data = {"purl": self.pkg_2_12_6.purl}
+
+# resp = self.csrf_client.post(
+# f"/api/packages/lookup",
+# data=json.dumps(data),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# def test_api_packages_bulk_lookup(self):
+# with self.assertNumQueries(44):
+# packages = [self.pkg_2_12_6, self.pkg_2_12_6_1, self.pkg_2_13_1]
+# purls = [p.purl for p in packages]
+
+# data = {"purls": purls}
+
+# resp = self.csrf_client.post(
+# f"/api/packages/bulk_lookup",
+# data=json.dumps(data),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+
+# class APITestCasePackage(TestCase):
+# def setUp(self):
+# cache.clear()
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+
+# # This setup creates the following data:
+# # vulnerabilities: vul1, vul2, vul3
+# # pkg:maven/com.fasterxml.jackson.core/jackson-databind
+# # with these versions:
+# # pkg_2_12_6: @ 2.12.6 affected by fixing vul3
+# # pkg_2_12_6_1: @ 2.12.6.1 affected by vul2 fixing vul1
+# # pkg_2_13_1: @ 2.13.1 affected by vul1 fixing vul3
+# # pkg_2_13_2: @ 2.13.2 affected by vul2 fixing vul1
+# # pkg_2_14_0_rc1: @ 2.14.0-rc1 affected by fixing
+
+# # searched-for pkg's vuln
+# self.vul1 = create_vuln("VCID-vul1-vul1-vul1", ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"])
+# self.vul2 = create_vuln("VCID-vul2-vul2-vul2")
+# # This is the vuln fixed by the searched-for pkg -- and by a lesser version (created below),
+# # which WILL be included in the API
+# self.vul3 = create_vuln("VCID-vul3-vul3-vul3", ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"])
+
+# from_purl = Package.objects.from_purl
+# # lesser-version pkg that also fixes the vuln fixed by the searched-for pkg
+# self.pkg_2_12_6 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6")
+# # this is a lesser version omitted from the API that fixes searched-for pkg's vuln
+# self.pkg_2_12_6_1 = from_purl(
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1"
+# )
+# # searched-for pkg
+# self.pkg_2_13_1 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1")
+# # this is a greater version that fixes searched-for pkg's vuln
+# self.pkg_2_13_2 = from_purl("pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2")
+# # This addresses both next and latest non-vulnerable pkg
+# self.pkg_2_14_0_rc1 = from_purl(
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
+# )
+# self.pkg_2_12_6.calculate_version_rank
+
+# self.ref = VulnerabilityReference.objects.create(
+# reference_type="advisory", reference_id="CVE-xxx-xxx", url="https://example.com"
+# )
+
+# self.severity = VulnerabilitySeverity.objects.create(
+# url="https://example.com",
+# scoring_system=EPSS.identifier,
+# scoring_elements=".0016",
+# value="0.526",
+# )
+# self.vul1.references.add(self.ref)
+# self.vul1.severities.add(self.severity)
+
+# self.vul3.references.add(self.ref)
+# self.vul3.severities.add(self.severity)
+
+# set_as_fixing(package=self.pkg_2_12_6, vulnerability=self.vul3)
+
+# set_as_affected_by(package=self.pkg_2_12_6_1, vulnerability=self.vul2)
+# set_as_fixing(package=self.pkg_2_12_6_1, vulnerability=self.vul1)
+
+# set_as_affected_by(package=self.pkg_2_13_1, vulnerability=self.vul1)
+# set_as_fixing(package=self.pkg_2_13_1, vulnerability=self.vul3)
+
+# set_as_affected_by(package=self.pkg_2_13_2, vulnerability=self.vul2)
+# set_as_fixing(package=self.pkg_2_13_2, vulnerability=self.vul1)
+
+# def test_api_with_lesser_and_greater_fixed_by_packages(self):
+# response = self.csrf_client.get(
+# f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# expected = {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "type": "maven",
+# "namespace": "com.fasterxml.jackson.core",
+# "name": "jackson-databind",
+# "version": "2.13.1",
+# "qualifiers": {},
+# "subpath": "",
+# "is_vulnerable": True,
+# "next_non_vulnerable_version": "2.14.0-rc1",
+# "latest_non_vulnerable_version": "2.14.0-rc1",
+# "affected_by_vulnerabilities": [
+# {
+# "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
+# "vulnerability_id": "VCID-vul1-vul1-vul1",
+# "summary": "This is VCID-vul1-vul1-vul1",
+# "references": [
+# {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://example.com",
+# }
+# ],
+# "fixed_packages": [
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# "is_vulnerable": True,
+# "affected_by_vulnerabilities": [
+# {"vulnerability": "VCID-vul2-vul2-vul2"}
+# ],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# }
+# ],
+# "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
+# }
+# ],
+# "fixing_vulnerabilities": [
+# {
+# "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul3.id),
+# "vulnerability_id": "VCID-vul3-vul3-vul3",
+# "summary": "This is VCID-vul3-vul3-vul3",
+# "references": [
+# {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://example.com",
+# }
+# ],
+# "fixed_packages": [
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_12_6.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
+# "is_vulnerable": False,
+# "affected_by_vulnerabilities": [],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
+# },
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "is_vulnerable": True,
+# "affected_by_vulnerabilities": [
+# {"vulnerability": "VCID-vul1-vul1-vul1"}
+# ],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# },
+# ],
+# "aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# "resource_url": "http://testserver/vulnerabilities/VCID-vul3-vul3-vul3",
+# }
+# ],
+# "risk_score": None,
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# }
+
+# assert response == expected
+
+# def test_is_vulnerable_attribute_only_exists_on_queryset(self):
+# assert not hasattr(self.pkg_2_13_1, "is_vulnerable")
+# pkgs = Package.objects.filter(pk=self.pkg_2_13_1.pk).with_is_vulnerable()
+# assert all(hasattr(p, "is_vulnerable") for p in pkgs)
+
+# def test_api_status(self):
+# response = self.csrf_client.get(
+# "/api/packages/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+# def test_api_response(self):
+# response = self.csrf_client.get(
+# "/api/packages/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+# self.assertEqual(response["count"], 5)
+
+# def test_api_with_namespace_filter(self):
+# response = self.csrf_client.get(
+# "/api/packages/?namespace=com.fasterxml.jackson.core",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).data
+# self.assertEqual(response["count"], 5)
+
+# def test_api_with_wrong_namespace_filter(self):
+# response = self.csrf_client.get(
+# "/api/packages/?namespace=foo-bar", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+# self.assertEqual(response["count"], 0)
+
+# def test_api_with_all_vulnerable_packages(self):
+# with self.assertNumQueries(3):
+# # There are 4 queries:
+# # 1. SAVEPOINT
+# # 2. Authenticating user
+# # 3. Get all vulnerable packages
+# # 4. RELEASE SAVEPOINT
+# response = self.csrf_client.get(
+# f"/api/packages/all", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# assert len(response) == 3
+# assert list(response) == [
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6.1",
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# ]
+
+# def test_api_with_ignorning_qualifiers(self):
+# response = self.csrf_client.get(
+# f"/api/packages/?purl=pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1?foo=bar",
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).data
+# assert response["count"] == 1
+# assert (
+# response["results"][0]["purl"]
+# == "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1"
+# )
+
+# def test_api_with_ghost_package_no_fixing_vulnerabilities(self):
+# self.pkg_2_13_1.is_ghost = True
+# self.pkg_2_13_1.save()
+
+# response = self.csrf_client.get(
+# f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# expected = {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "type": "maven",
+# "namespace": "com.fasterxml.jackson.core",
+# "name": "jackson-databind",
+# "version": "2.13.1",
+# "qualifiers": {},
+# "subpath": "",
+# "is_vulnerable": True,
+# "next_non_vulnerable_version": "2.12.6",
+# "latest_non_vulnerable_version": "2.14.0-rc1",
+# "affected_by_vulnerabilities": [
+# {
+# "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
+# "vulnerability_id": "VCID-vul1-vul1-vul1",
+# "summary": "This is VCID-vul1-vul1-vul1",
+# "references": [
+# {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://example.com",
+# }
+# ],
+# "fixed_packages": [
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# "is_vulnerable": True,
+# "affected_by_vulnerabilities": [
+# {"vulnerability": "VCID-vul2-vul2-vul2"}
+# ],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# }
+# ],
+# "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
+# }
+# ],
+# "fixing_vulnerabilities": [],
+# "risk_score": None,
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# }
+
+# assert response == expected
+
+# def test_api_with_ghost_package_no_next_latest_non_vulnerabilities(self):
+# self.pkg_2_14_0_rc1.is_ghost = True
+# self.pkg_2_14_0_rc1.save()
+
+# response = self.csrf_client.get(
+# f"/api/packages/{self.pkg_2_13_1.id}", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+
+# expected = {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "type": "maven",
+# "namespace": "com.fasterxml.jackson.core",
+# "name": "jackson-databind",
+# "version": "2.13.1",
+# "qualifiers": {},
+# "subpath": "",
+# "is_vulnerable": True,
+# "next_non_vulnerable_version": None,
+# "latest_non_vulnerable_version": None,
+# "affected_by_vulnerabilities": [
+# {
+# "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul1.id),
+# "vulnerability_id": "VCID-vul1-vul1-vul1",
+# "summary": "This is VCID-vul1-vul1-vul1",
+# "references": [
+# {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://example.com",
+# }
+# ],
+# "fixed_packages": [
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_2.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# "is_vulnerable": True,
+# "affected_by_vulnerabilities": [
+# {"vulnerability": "VCID-vul2-vul2-vul2"}
+# ],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.2",
+# }
+# ],
+# "aliases": ["CVE-2020-36518", "GHSA-57j2-w4cx-62h2"],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# "resource_url": "http://testserver/vulnerabilities/VCID-vul1-vul1-vul1",
+# }
+# ],
+# "fixing_vulnerabilities": [
+# {
+# "url": "http://testserver/api/vulnerabilities/{0}".format(self.vul3.id),
+# "vulnerability_id": "VCID-vul3-vul3-vul3",
+# "summary": "This is VCID-vul3-vul3-vul3",
+# "references": [
+# {
+# "reference_url": "https://example.com",
+# "reference_id": "CVE-xxx-xxx",
+# "reference_type": "advisory",
+# "scores": [
+# {
+# "value": "0.526",
+# "scoring_system": "epss",
+# "scoring_elements": ".0016",
+# }
+# ],
+# "url": "https://example.com",
+# }
+# ],
+# "fixed_packages": [
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_12_6.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
+# "is_vulnerable": False,
+# "affected_by_vulnerabilities": [],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.12.6",
+# },
+# {
+# "url": "http://testserver/api/packages/{0}".format(self.pkg_2_13_1.id),
+# "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# "is_vulnerable": True,
+# "affected_by_vulnerabilities": [
+# {"vulnerability": "VCID-vul1-vul1-vul1"}
+# ],
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# },
+# ],
+# "aliases": ["CVE-2021-46877", "GHSA-3x8x-79m2-3w2w"],
+# "risk_score": None,
+# "exploitability": None,
+# "weighted_severity": None,
+# "resource_url": "http://testserver/vulnerabilities/VCID-vul3-vul3-vul3",
+# }
+# ],
+# "risk_score": None,
+# "resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
+# }
+
+# assert response == expected
+
+
+# class CPEApi(TestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# self.vulnerability = Vulnerability.objects.create(summary="test")
+# for i in range(0, 10):
+# ref, _ = VulnerabilityReference.objects.get_or_create(
+# reference_id=f"cpe:/a:nginx:{i}",
+# url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query=cpe:/a:nginx:{i}",
+# )
+# VulnerabilityRelatedReference.objects.create(
+# reference=ref, vulnerability=self.vulnerability
+# )
+
+# def test_api_status(self):
+# response = self.csrf_client.get(
+# "/api/cpes/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+# def test_api_response(self):
+# response = self.csrf_client.get(
+# "/api/cpes/?cpe=cpe:/a:nginx:9", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+# self.assertEqual(response["count"], 1)
+
+
+# class TestCPEApiWithPackageVulnerabilityRelation(TestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# self.vulnerability = Vulnerability.objects.create(summary="test")
+# self.affected_package, _ = Package.objects.get_or_create_from_purl(
+# purl="pkg:nginx/nginx@v3.4"
+# )
+# self.fixed_package, _ = Package.objects.get_or_create_from_purl(purl="pkg:nginx/nginx@v4.0")
+# AffectedByPackageRelatedVulnerability.objects.create(
+# vulnerability=self.vulnerability,
+# created_by="test",
+# package=self.affected_package,
+# confidence=100,
+# )
+# FixingPackageRelatedVulnerability.objects.create(
+# vulnerability=self.vulnerability,
+# created_by="test",
+# package=self.fixed_package,
+# confidence=100,
+# )
+# for i in range(0, 10):
+# ref, _ = VulnerabilityReference.objects.get_or_create(
+# reference_id=f"cpe:/a:nginx:{i}",
+# url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query=cpe:/a:nginx:{i}",
+# )
+# VulnerabilityRelatedReference.objects.create(
+# reference=ref, vulnerability=self.vulnerability
+# )
+
+# def test_cpe_api(self):
+# response = self.csrf_client.get(
+# "/api/cpes/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+# response_data = response.json()
+# self.assertEqual(1, response_data["count"])
+
+
+# class AliasApi(TestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# self.vulnerability = Vulnerability.objects.create(summary="test")
+# for i in range(0, 10):
+# Alias.objects.create(alias=f"CVE-{i}", vulnerability=self.vulnerability)
+
+# def test_api_status(self):
+# response = self.csrf_client.get(
+# "/api/aliases/", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# self.assertEqual(status.HTTP_200_OK, response.status_code)
+
+# def test_api_response(self):
+# response = self.csrf_client.get(
+# "/api/aliases?alias=CVE-9", format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# ).data
+# self.assertEqual(response["count"], 1)
+
+
+# class BulkSearchAPIPackage(TestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# packages = self.packages = [
+# "pkg:nginx/nginx@0.6.18",
+# "pkg:nginx/nginx@1.20.0",
+# "pkg:nginx/nginx@1.21.0",
+# "pkg:nginx/nginx@1.20.1",
+# "pkg:nginx/nginx@1.9.5",
+# "pkg:nginx/nginx@1.17.2",
+# "pkg:nginx/nginx@1.17.3",
+# "pkg:nginx/nginx@1.16.1",
+# "pkg:nginx/nginx@1.15.5",
+# "pkg:nginx/nginx@1.15.6",
+# "pkg:nginx/nginx@1.14.1",
+# "pkg:nginx/nginx@1.0.7",
+# "pkg:nginx/nginx@1.0.15",
+# ]
+# self.pkgs = [Package.objects.from_purl(p) for p in packages]
+
+# vulnerable_packages = [
+# "pkg:nginx/nginx@1.0.15?foo=bar",
+# "pkg:nginx/nginx@1.0.15?foo=baz",
+# ]
+
+# vulnerability = Vulnerability.objects.create(summary="test")
+
+# for purl in vulnerable_packages:
+# package = Package.objects.from_purl(purl)
+# set_as_affected_by(package, vulnerability)
+
+# def test_bulk_api_response(self):
+# request_body = {
+# "purls": self.packages,
+# }
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 13
+
+# def test_bulk_api_response_with_ignoring_qualifiers(self):
+# request_body = {"purls": ["pkg:nginx/nginx@1.0.15?qualifiers=dev"], "plain_purl": True}
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+# assert response[0]["purl"] == "pkg:nginx/nginx@1.0.15"
+
+# def test_bulk_api_response_with_ignoring_subpath(self):
+# request_body = {"purls": ["pkg:nginx/nginx@1.0.15#dev/subpath"], "plain_purl": True}
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+# assert response[0]["purl"] == "pkg:nginx/nginx@1.0.15"
+
+# def test_bulk_api_with_purl_only_option(self):
+# request_body = {
+# "purls": ["pkg:nginx/nginx@1.0.15#dev/subpath"],
+# "purl_only": True,
+# "plain_purl": True,
+# }
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+# assert response[0] == "pkg:nginx/nginx@1.0.15"
+
+# def test_bulk_api_without_purls_list(self):
+# request_body = {
+# "purls": None,
+# }
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# expected = {
+# "error": {"purls": ["This field may not be null."]},
+# "message": "A non-empty 'purls' list of PURLs is required.",
+# }
+
+# self.assertEqual(response, expected)
+
+# def test_bulk_api_without_purls_empty_list(self):
+# request_body = {
+# "purls": [],
+# }
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# expected = {
+# "error": {"purls": ["This list may not be empty."]},
+# "message": "A non-empty 'purls' list of PURLs is required.",
+# }
+
+# self.assertEqual(response, expected)
+
+# def test_bulk_api_with_empty_request_body(self):
+# request_body = {}
+# response = self.csrf_client.post(
+# "/api/packages/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# expected = {
+# "error": {"purls": ["This field is required."]},
+# "message": "A non-empty 'purls' list of PURLs is required.",
+# }
+
+# self.assertEqual(response, expected)
+
+
+# class BulkSearchAPICPE(TestCase):
+# def setUp(self):
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+# self.exclusive_cpes = [
+# "cpe:/a:nginx:1.0.7",
+# "cpe:/a:nginx:1.0.15",
+# "cpe:/a:nginx:1.14.1",
+# "cpe:/a:nginx:1.15.5",
+# "cpe:/a:nginx:1.15.6",
+# ]
+# vuln = Vulnerability.objects.create(summary="test")
+# for cpe in self.exclusive_cpes:
+# ref = VulnerabilityReference.objects.create(
+# reference_id=cpe,
+# url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query={cpe}",
+# )
+# VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=vuln)
+# second_vuln = Vulnerability.objects.create(summary="test-A")
+# self.non_exclusive_cpes = [
+# "cpe:/a:nginx:1.16.1",
+# "cpe:/a:nginx:1.17.2",
+# "cpe:/a:nginx:1.17.3",
+# "cpe:/a:nginx:1.9.5",
+# "cpe:/a:nginx:1.20.1",
+# "cpe:/a:nginx:1.20.0",
+# "cpe:/a:nginx:1.21.0",
+# ]
+# third_vuln = Vulnerability.objects.create(summary="test-B")
+# for cpe in self.non_exclusive_cpes:
+# ref = VulnerabilityReference.objects.create(
+# reference_id=cpe,
+# url=f"https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query={cpe}",
+# )
+# VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=second_vuln)
+# VulnerabilityRelatedReference.objects.create(reference=ref, vulnerability=third_vuln)
+
+# def test_api_response_with_with_exclusive_cpes_associated_with_two_vulnerabilities(self):
+# request_body = {
+# "cpes": self.exclusive_cpes,
+# }
+# response = self.csrf_client.post(
+# "/api/cpes/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+# assert response[0]["summary"] == "test"
+# references_in_vuln = response[0]["references"]
+# cpes = [ref["reference_id"] for ref in references_in_vuln]
+# assert set(cpes) == set(self.exclusive_cpes)
+
+# def test_api_response_with_no_cpe_associated(self):
+# request_body = {
+# "cpes": ["cpe:/a:nginx:1.10.7"],
+# }
+# response = self.csrf_client.post(
+# "/api/cpes/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 0
+
+# def test_api_response_with_with_non_exclusive_cpes_associated_with_two_vulnerabilities(self):
+# request_body = {
+# "cpes": self.non_exclusive_cpes,
+# }
+# response = self.csrf_client.post(
+# "/api/cpes/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 2
+
+# def test_with_empty_list(self):
+# request_body = {
+# "cpes": [],
+# }
+# response = self.csrf_client.post(
+# "/api/cpes/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert response == {"Error": "A non-empty 'cpes' list of CPEs is required."}
+
+# def test_with_invalid_cpes(self):
+# request_body = {"cpes": ["CVE-2022-2022"]}
+# response = self.csrf_client.post(
+# "/api/cpes/bulk_search",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert response == {"Error": "Invalid CPE: CVE-2022-2022"}
+
+
+# class TesBanUserAgent(TestCase):
+# def test_ban_request_with_bytedance_user_agent(self):
+# response = self.client.get(f"/api/packages", format="json", HTTP_USER_AGENT="bytedance")
+# assert 404 == response.status_code
+
+
+# class TestLookup(TestCase):
+# def setUp(self):
+# Package.objects.create(
+# type="pypi", namespace="", name="microweber/microweber", version="1.2"
+# )
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.csrf_client = APIClient(enforce_csrf_checks=True)
+# self.csrf_client.credentials(HTTP_AUTHORIZATION=self.auth)
+
+# def test_lookup_endpoint_failure(self):
+# request_body = {"purl": None}
+# response = self.csrf_client.post(
+# "/api/packages/lookup",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# expected = {
+# "error": {"purl": ["This field may not be null."]},
+# "message": "A 'purl' is required.",
+# }
+
+# self.assertEqual(response, expected)
+
+# def test_lookup_endpoint(self):
+# request_body = {"purl": "pkg:pypi/microweber/microweber@1.2"}
+# response = self.csrf_client.post(
+# "/api/packages/lookup",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+# assert response[0]["purl"] == "pkg:pypi/microweber/microweber@1.2"
+
+# def test_bulk_lookup_endpoint(self):
+# request_body = {
+# "purls": [
+# "pkg:pypi/microweber/microweber@1.2?foo=bar",
+# "pkg:pypi/microweber/microweber@1.2",
+# "pkg:pypi/foo/bar@1.0",
+# ],
+# }
+# response = self.csrf_client.post(
+# "/api/packages/bulk_lookup",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+# assert len(response) == 1
+
+# def test_bulk_lookup_endpoint_failure(self):
+# request_body = {"purls": None}
+# response = self.csrf_client.post(
+# "/api/packages/bulk_lookup",
+# data=json.dumps(request_body),
+# content_type="application/json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# ).json()
+
+# expected = {
+# "error": {"purls": ["This field may not be null."]},
+# "message": "A non-empty 'purls' list of PURLs is required.",
+# }
+
+# self.assertEqual(response, expected)
diff --git a/vulnerabilities/tests/test_api_v2.py b/vulnerabilities/tests/test_api_v2.py
index 1ba8443a9..649b9ec03 100644
--- a/vulnerabilities/tests/test_api_v2.py
+++ b/vulnerabilities/tests/test_api_v2.py
@@ -1,913 +1,913 @@
-#
-# Copyright (c) nexB Inc. and others. All rights reserved.
-# VulnerableCode is a trademark of nexB Inc.
-# SPDX-License-Identifier: Apache-2.0
-# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
-# See https://github.com/aboutcode-org/vulnerablecode for support or download.
-# See https://aboutcode.org for more information about nexB OSS projects.
-#
-
-from unittest.mock import patch
-
-from django.contrib.auth.models import User
-from django.core.cache import cache
-from django.db.models import Prefetch
-from django.urls import reverse
-from rest_framework import status
-from rest_framework.test import APIClient
-from rest_framework.test import APITestCase
-
-from vulnerabilities.api_v2 import PackageV2Serializer
-from vulnerabilities.api_v2 import VulnerabilityListSerializer
-from vulnerabilities.models import AdvisoryV2
-from vulnerabilities.models import Alias
-from vulnerabilities.models import ApiUser
-from vulnerabilities.models import CodeFixV2
-from vulnerabilities.models import Package
-from vulnerabilities.models import PackageV2
-from vulnerabilities.models import PipelineRun
-from vulnerabilities.models import PipelineSchedule
-from vulnerabilities.models import Vulnerability
-from vulnerabilities.models import VulnerabilityReference
-from vulnerabilities.models import Weakness
-
-
-class VulnerabilityV2ViewSetTest(APITestCase):
- def setUp(self):
- # Create vulnerabilities
- self.vuln1 = Vulnerability.objects.create(
- vulnerability_id="VCID-1234", summary="Test vulnerability 1"
- )
- self.vuln2 = Vulnerability.objects.create(
- vulnerability_id="VCID-5678", summary="Test vulnerability 2"
- )
-
- # Create aliases
- Alias.objects.create(alias="CVE-2021-1234", vulnerability=self.vuln1)
- Alias.objects.create(alias="CVE-2021-5678", vulnerability=self.vuln2)
-
- # Create weaknesses
- self.weakness1 = Weakness.objects.create(cwe_id=79)
- self.weakness1.vulnerabilities.add(self.vuln1)
-
- self.weakness2 = Weakness.objects.create(cwe_id=89)
- self.weakness2.vulnerabilities.add(self.vuln2)
-
- # Create references
- self.reference1 = VulnerabilityReference.objects.create(
- url="https://example.com/ref1", reference_type="advisory", reference_id="REF-1"
- )
- self.reference1.vulnerabilities.add(self.vuln1)
-
- self.reference2 = VulnerabilityReference.objects.create(
- url="https://example.com/ref2", reference_type="exploit", reference_id="REF-2"
- )
- self.reference2.vulnerabilities.add(self.vuln2)
-
- cache.clear()
- self.client = APIClient(enforce_csrf_checks=True)
-
- def test_list_vulnerabilities(self):
- """
- Test listing vulnerabilities without filters.
- Should return a paginated response with vulnerabilities dictionary.
- """
- url = reverse("vulnerability-v2-list")
- response = self.client.get(url, format="json")
- with self.assertNumQueries(4):
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("results", response.data)
- self.assertIn("vulnerabilities", response.data["results"])
- self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
- self.assertIn("VCID-1234", response.data["results"]["vulnerabilities"])
- self.assertIn("VCID-5678", response.data["results"]["vulnerabilities"])
- self.assertTrue("url" in response.data["results"]["vulnerabilities"]["VCID-1234"])
-
- def test_retrieve_vulnerability_detail(self):
- """
- Test retrieving vulnerability details by vulnerability_id.
- """
- url = reverse("vulnerability-v2-detail", kwargs={"vulnerability_id": "VCID-1234"})
- with self.assertNumQueries(7):
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data["vulnerability_id"], "VCID-1234")
- self.assertEqual(response.data["summary"], "Test vulnerability 1")
- self.assertEqual(response.data["aliases"], ["CVE-2021-1234"])
- self.assertEqual(len(response.data["weaknesses"]), 1)
- self.assertEqual(len(response.data["references"]), 1)
-
- def test_filter_vulnerability_by_vulnerability_id(self):
- """
- Test filtering vulnerabilities by vulnerability_id.
- """
- url = reverse("vulnerability-v2-list")
- with self.assertNumQueries(3):
- response = self.client.get(
- url,
- {"vulnerability_id": "VCID-1234"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data["vulnerability_id"], "VCID-1234")
-
- def test_filter_vulnerability_by_alias(self):
- """
- Test filtering vulnerabilities by alias.
- """
- url = reverse("vulnerability-v2-list")
- with self.assertNumQueries(4):
- response = self.client.get(
- url,
- {"alias": "CVE-2021-5678"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("results", response.data)
- self.assertIn("vulnerabilities", response.data["results"])
- self.assertEqual(
- response.data["results"]["vulnerabilities"]["VCID-5678"]["vulnerability_id"],
- "VCID-5678",
- )
-
- def test_filter_vulnerabilities_multiple_ids(self):
- """
- Test filtering vulnerabilities by multiple vulnerability_ids.
- """
- url = reverse("vulnerability-v2-list")
- with self.assertNumQueries(4):
- response = self.client.get(
- url,
- {"vulnerability_id": ["VCID-1234", "VCID-5678"]},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
-
- def test_filter_vulnerabilities_multiple_aliases(self):
- """
- Test filtering vulnerabilities by multiple aliases.
- """
- url = reverse("vulnerability-v2-list")
- with self.assertNumQueries(4):
- response = self.client.get(
- url,
- {"alias": ["CVE-2021-1234", "CVE-2021-5678"]},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
-
- def test_invalid_vulnerability_id(self):
- """
- Test retrieving a vulnerability with an invalid vulnerability_id.
- Should return 404 Not Found.
- """
- url = reverse("vulnerability-v2-detail", kwargs={"vulnerability_id": "VCID-9999"})
- with self.assertNumQueries(4):
- response = self.client.get(
- url,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
- def test_get_url_in_serializer(self):
- """
- Test that the serializer correctly includes the URL field.
- """
- vulnerability = Vulnerability.objects.get(vulnerability_id="VCID-1234")
- serializer = VulnerabilityListSerializer(vulnerability, context={"request": None})
- self.assertIn("url", serializer.data)
- self.assertEqual(serializer.data["vulnerability_id"], "VCID-1234")
-
- def test_list_vulnerabilities_pagination(self):
- """
- Test listing vulnerabilities with pagination.
- """
- # Create additional vulnerabilities to trigger pagination
- for i in range(3, 15):
- Vulnerability.objects.create(
- vulnerability_id=f"VCID-{i}", summary=f"Test vulnerability {i}"
- )
-
- url = reverse("vulnerability-v2-list")
- response = self.client.get(
- url,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("results", response.data)
- self.assertIn("vulnerabilities", response.data["results"])
- self.assertIn("next", response.data)
- self.assertIn("previous", response.data)
- # The 'vulnerabilities' dictionary should contain vulnerabilities up to the page limit
- self.assertEqual(
- len(response.data["results"]["vulnerabilities"]), 14
- ) # Assuming default page size is 100
-
-
-class PackageV2ViewSetTest(APITestCase):
- def setUp(self):
- # Create packages
- self.package1 = Package.objects.create(
- package_url="pkg:pypi/django@3.2", name="django", version="3.2", type="pypi"
- )
- self.package2 = Package.objects.create(
- package_url="pkg:npm/lodash@4.17.20", name="lodash", version="4.17.20", type="npm"
- )
-
- # Create vulnerabilities
- self.vuln1 = Vulnerability.objects.create(
- vulnerability_id="VCID-1234", summary="Test vulnerability 1"
- )
- self.vuln2 = Vulnerability.objects.create(
- vulnerability_id="VCID-5678", summary="Test vulnerability 2"
- )
-
- # Associate packages with vulnerabilities
- self.package1.affected_by_vulnerabilities.add(self.vuln1)
- self.package2.fixing_vulnerabilities.add(self.vuln2)
-
- cache.clear()
- self.client = APIClient(enforce_csrf_checks=True)
-
- def test_list_packages(self):
- """
- Test listing packages without filters.
- Should return a list of packages with their details and associated vulnerabilities.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(31):
- response = self.client.get(
- url,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("results", response.data)
- self.assertIn("packages", response.data["results"])
- self.assertIn("vulnerabilities", response.data["results"])
- self.assertEqual(len(response.data["results"]["packages"]), 2)
- # Verify that vulnerabilities are included
- self.assertIsInstance(response.data["results"]["vulnerabilities"], dict)
- package_vulns = set()
- for package in response.data["results"]["packages"]:
- package_vulns.update(package["affected_by_vulnerabilities"])
- package_vulns.update(package["fixing_vulnerabilities"])
- self.assertTrue(
- all(vuln_id in response.data["results"]["vulnerabilities"] for vuln_id in package_vulns)
- )
-
- def test_filter_packages_by_purl(self):
- """
- Test filtering packages by one or more PURLs.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(19):
- response = self.client.get(
- url,
- {"purl": "pkg:pypi/django@3.2"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["packages"]), 1)
- self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:pypi/django@3.2")
-
- def test_filter_packages_by_affected_vulnerability(self):
- """
- Test filtering packages by affected_by_vulnerability.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(19):
- response = self.client.get(
- url,
- {"affected_by_vulnerability": "VCID-1234"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["packages"]), 1)
- self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:pypi/django@3.2")
-
- def test_filter_packages_by_fixing_vulnerability(self):
- """
- Test filtering packages by fixing_vulnerability.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(17):
- response = self.client.get(
- url,
- {"fixing_vulnerability": "VCID-5678"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["packages"]), 1)
- self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:npm/lodash@4.17.20")
-
- def test_package_serializer_fields(self):
- """
- Test that the PackageV2Serializer returns the correct fields and formats them correctly.
- """
- # Fetch the package
- package = Package.objects.get(package_url="pkg:pypi/django@3.2")
-
- # 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"),
- to_attr="prefetched_affected_vulnerabilities",
- )
- )
- .first()
- )
-
- # Serialize the package
- serializer = PackageV2Serializer(package)
- data = serializer.data
-
- # Verify the presence of required fields
- self.assertIn("purl", data)
- self.assertIn("affected_by_vulnerabilities", data)
- self.assertIn("fixing_vulnerabilities", data)
- self.assertIn("next_non_vulnerable_version", data)
- self.assertIn("latest_non_vulnerable_version", data)
- self.assertIn("risk_score", data)
-
- # Verify field values
- self.assertEqual(data["purl"], "pkg:pypi/django@3.2")
- self.assertEqual(data["next_non_vulnerable_version"], None)
- self.assertEqual(data["latest_non_vulnerable_version"], None)
- self.assertEqual(data["risk_score"], None)
-
- # Verify affected_by_vulnerabilities structure
- expected_affected_by_vulnerabilities = {
- "VCID-1234": {
- "code_fixes": [],
- "vulnerability_id": "VCID-1234",
- "fixed_by_packages": None,
- }
- }
- self.assertEqual(data["affected_by_vulnerabilities"], expected_affected_by_vulnerabilities)
-
- # Verify fixing_vulnerabilities structure
- expected_fixing_vulnerabilities = []
- self.assertEqual(data["fixing_vulnerabilities"], expected_fixing_vulnerabilities)
-
- def test_list_packages_pagination(self):
- """
- Test listing packages with pagination.
- """
- # Create additional packages to trigger pagination
- for i in range(3, 15):
- Package.objects.create(
- package_url=f"pkg:pypi/package{i}@1.0.{i}",
- name=f"package{i}",
- version=f"1.0.{i}",
- type="pypi",
- )
-
- url = reverse("package-v2-list")
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("results", response.data)
- self.assertIn("packages", response.data["results"])
- self.assertIn("vulnerabilities", response.data["results"])
- self.assertIn("next", response.data)
- self.assertIn("previous", response.data)
- self.assertEqual(
- len(response.data["results"]["packages"]), 14
- ) # Assuming default page size is 100
-
- def test_invalid_vulnerability_filter(self):
- """
- Test filtering packages with an invalid vulnerability ID.
- Should return an empty list.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(3):
- response = self.client.get(
- url,
- {"affected_by_vulnerability": "VCID-9999"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["packages"]), 0)
-
- def test_invalid_purl_filter(self):
- """
- Test filtering packages with an invalid PURL.
- Should return an empty list.
- """
- url = reverse("package-v2-list")
- with self.assertNumQueries(3):
- response = self.client.get(
- url,
- {"purl": "pkg:nonexistent/package@1.0.0"},
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data["results"]["packages"]), 0)
-
- def test_get_affected_by_vulnerabilities(self):
- """
- Test the get_affected_by_vulnerabilities method in 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"),
- to_attr="prefetched_affected_vulnerabilities",
- )
- )
- .first()
- )
-
- serializer = PackageV2Serializer()
- vulnerabilities = serializer.get_affected_by_vulnerabilities(package)
- self.assertEqual(
- vulnerabilities,
- {
- "VCID-1234": {
- "code_fixes": [],
- "vulnerability_id": "VCID-1234",
- "fixed_by_packages": None,
- }
- },
- )
-
- def test_get_fixing_vulnerabilities(self):
- """
- Test the get_fixing_vulnerabilities method in the serializer.
- """
- package = Package.objects.get(package_url="pkg:npm/lodash@4.17.20")
- serializer = PackageV2Serializer()
- vulnerabilities = serializer.get_fixing_vulnerabilities(package)
- self.assertEqual(vulnerabilities, ["VCID-5678"])
-
- def test_bulk_lookup_with_valid_purls(self):
- """
- Test bulk lookup with valid PURLs.
- Should return packages and their associated vulnerabilities.
- """
- url = reverse("package-v2-bulk-lookup")
- data = {"purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"]}
- with self.assertNumQueries(27):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("packages", response.data)
- self.assertIn("vulnerabilities", response.data)
- self.assertEqual(len(response.data["packages"]), 2)
- # Verify that the returned data matches the packages
- purls = [package["purl"] for package in response.data["packages"]]
- self.assertIn("pkg:pypi/django@3.2", purls)
- self.assertIn("pkg:npm/lodash@4.17.20", purls)
- # Verify that vulnerabilities are included
- package_vulns = set()
- for package in response.data["packages"]:
- package_vulns.update(package["affected_by_vulnerabilities"])
- package_vulns.update(package["fixing_vulnerabilities"])
- self.assertTrue(
- all(vuln_id in response.data["vulnerabilities"] for vuln_id in package_vulns)
- )
-
- def test_bulk_lookup_with_invalid_purls(self):
- """
- Test bulk lookup with invalid PURLs.
- """
- url = reverse("package-v2-bulk-lookup")
- data = {"purls": ["pkg:pypi/nonexistent@1.0.0", "pkg:npm/unknown@0.0.1"]}
- with self.assertNumQueries(3):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # Since the packages don't exist, the response should be empty
- self.assertEqual(len(response.data["packages"]), 0)
- self.assertEqual(len(response.data["vulnerabilities"]), 0)
-
- def test_bulk_lookup_with_empty_purls(self):
- """
- Test bulk lookup with empty purls list.
- Should return 400 Bad Request.
- """
- url = reverse("package-v2-bulk-lookup")
- data = {"purls": []}
- with self.assertNumQueries(2):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
- self.assertIn("error", response.data)
- self.assertIn("message", response.data)
- self.assertEqual(response.data["message"], "A non-empty 'purls' list of PURLs is required.")
-
- def test_bulk_search_with_valid_purls(self):
- """
- Test bulk search with valid PURLs.
- Should return packages and their associated vulnerabilities.
- """
- url = reverse("package-v2-bulk-search")
- data = {"purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"]}
- with self.assertNumQueries(27):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("packages", response.data)
- self.assertIn("vulnerabilities", response.data)
- self.assertEqual(len(response.data["packages"]), 2)
- purls = [package["purl"] for package in response.data["packages"]]
- self.assertIn("pkg:pypi/django@3.2", purls)
- self.assertIn("pkg:npm/lodash@4.17.20", purls)
- # Verify that vulnerabilities are included
- package_vulns = set()
- for package in response.data["packages"]:
- package_vulns.update(package["affected_by_vulnerabilities"])
- package_vulns.update(package["fixing_vulnerabilities"])
- self.assertTrue(
- all(vuln_id in response.data["vulnerabilities"] for vuln_id in package_vulns)
- )
-
- def test_bulk_search_with_purl_only_true(self):
- """
- Test bulk search with purl_only set to True.
- Should return only the PURLs of vulnerable packages.
- """
- url = reverse("package-v2-bulk-search")
- data = {
- "purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"],
- "purl_only": True,
- }
- with self.assertNumQueries(16):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # Since purl_only=True, response should be a list of PURLs
- self.assertIsInstance(response.data, list)
- # Only vulnerable packages should be included
- self.assertEqual(len(response.data), 1)
- self.assertEqual(response.data, ["pkg:pypi/django@3.2"])
-
- def test_bulk_search_with_plain_purl_true(self):
- """
- Test bulk search with plain_purl set to True.
- Should return packages grouped by plain PURLs.
- """
- # Create another package with the same name and version but different qualifiers
- Package.objects.create(
- name="django",
- version="3.2",
- type="pypi",
- qualifiers={"extension": "tar.gz"},
- )
-
- url = reverse("package-v2-bulk-search")
- data = {
- "purls": ["pkg:pypi/django@3.2", "pkg:pypi/django@3.2?extension=tar.gz"],
- "plain_purl": True,
- }
- with self.assertNumQueries(15):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn("packages", response.data)
- self.assertIn("vulnerabilities", response.data)
- # Since plain_purl=True, packages with the same type, namespace, name, version are grouped
- self.assertEqual(len(response.data["packages"]), 1)
- purl = response.data["packages"][0]["purl"]
- self.assertTrue(purl.startswith("pkg:pypi/django@3.2"))
-
- def test_bulk_search_with_purl_only_and_plain_purl_true(self):
- """
- Test bulk search with purl_only and plain_purl both set to True.
- Should return only the plain PURLs of vulnerable packages.
- """
- url = reverse("package-v2-bulk-search")
- data = {
- "purls": ["pkg:pypi/django@3.2", "pkg:pypi/django@3.1"],
- "purl_only": True,
- "plain_purl": True,
- }
- with self.assertNumQueries(10):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # Response should be a list of plain PURLs
- self.assertIsInstance(response.data, list)
- # Only one plain PURL should be returned for vulnerable packages
- self.assertEqual(len(response.data), 1)
- self.assertEqual(response.data, ["pkg:pypi/django@3.2"])
-
- def test_bulk_search_with_invalid_purls(self):
- """
- Test bulk search with invalid PURLs.
- Should return an empty response.
- """
- url = reverse("package-v2-bulk-search")
- data = {"purls": ["pkg:pypi/nonexistent@1.0.0", "pkg:npm/unknown@0.0.1"]}
- with self.assertNumQueries(3):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # Since the packages don't exist, the response should be empty
- self.assertEqual(len(response.data["packages"]), 0)
- self.assertEqual(len(response.data["vulnerabilities"]), 0)
-
- def test_bulk_search_with_empty_purls(self):
- """
- Test bulk search with empty purls list.
- Should return 400 Bad Request.
- """
- url = reverse("package-v2-bulk-search")
- data = {"purls": []}
- with self.assertNumQueries(2):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
- self.assertIn("error", response.data)
- self.assertIn("message", response.data)
- self.assertEqual(response.data["message"], "A non-empty 'purls' list of PURLs is required.")
-
- def test_all_vulnerable_packages(self):
- """
- Test the 'all' endpoint that returns all vulnerable package URLs.
- """
- url = reverse("package-v2-all")
- with self.assertNumQueries(3):
- response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # Since package1 is vulnerable, it should be returned
- expected_purls = ["pkg:pypi/django@3.2"]
- self.assertEqual(sorted(response.data), sorted(expected_purls))
-
- def test_lookup_with_valid_purl(self):
- """
- Test the 'lookup' endpoint with a valid PURL.
- Should return the package and its associated vulnerabilities.
- """
- url = reverse("package-v2-lookup")
- data = {"purl": "pkg:pypi/django@3.2"}
- with self.assertNumQueries(12):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(1, len(response.data))
- self.assertIn("purl", response.data[0])
- self.assertIn("affected_by_vulnerabilities", response.data[0])
- self.assertIn("fixing_vulnerabilities", response.data[0])
- self.assertIn("next_non_vulnerable_version", response.data[0])
- self.assertIn("latest_non_vulnerable_version", response.data[0])
- self.assertEqual(response.data[0]["purl"], "pkg:pypi/django@3.2")
- self.assertEqual(
- response.data[0]["affected_by_vulnerabilities"],
- {
- "VCID-1234": {
- "code_fixes": [],
- "vulnerability_id": "VCID-1234",
- "fixed_by_packages": None,
- }
- },
- )
- self.assertEqual(response.data[0]["fixing_vulnerabilities"], [])
-
- def test_lookup_with_invalid_purl(self):
- """
- Test the 'lookup' endpoint with a PURL that does not exist.
- Should return empty packages and vulnerabilities.
- """
- url = reverse("package-v2-lookup")
- data = {"purl": "pkg:pypi/nonexistent@1.0.0"}
- with self.assertNumQueries(3):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # No packages or vulnerabilities should be returned
- self.assertEqual(len(response.data), 0)
-
- def test_lookup_with_missing_purl(self):
- """
- Test the 'lookup' endpoint without providing a 'purl'.
- Should return 400 Bad Request.
- """
- url = reverse("package-v2-lookup")
- data = {}
- with self.assertNumQueries(2):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
- self.assertIn("error", response.data)
- self.assertIn("message", response.data)
- self.assertEqual(response.data["message"], "A 'purl' is required.")
-
- def test_lookup_with_invalid_purl_format(self):
- """
- Test the 'lookup' endpoint with an invalid PURL format.
- Should return empty packages and vulnerabilities.
- """
- url = reverse("package-v2-lookup")
- data = {"purl": "invalid_purl_format"}
- with self.assertNumQueries(3):
- response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- # No packages or vulnerabilities should be returned
- self.assertEqual(len(response.data), 0)
-
-
-class PipelineScheduleV2ViewSetTest(APITestCase):
- def setUp(self):
- # Reset the api throttling for anon user to properly test the
- # access on schedule endpoint.
- # DRF stores throttling state in cache, clear cache to reset throttling.
- # See https://www.django-rest-framework.org/api-guide/throttling/#setting-up-the-cache
- cache.clear()
-
- patcher = patch.object(PipelineSchedule, "create_new_job")
- self.mock_create_new_job = patcher.start()
- self.addCleanup(patcher.stop)
-
- self.mock_create_new_job.return_value = "work-id"
-
- self.schedule1 = PipelineSchedule.objects.create(
- pipeline_id="test_pipeline",
- )
- self.run1 = PipelineRun.objects.create(
- pipeline=self.schedule1,
- )
-
- self.admin_user = User.objects.create_superuser(
- username="admin_with_session",
- password="adminpassword",
- email="admin@test.com",
- )
-
- self.admin_token_only_user = ApiUser.objects.create_api_user(
- username="staff_with_token",
- is_staff=True,
- )
- self.admin_token_auth = f"Token {self.admin_token_only_user.auth_token.key}"
-
- def test_schedule_list_anon_user_permitted(self):
- response = self.client.get("/api/v2/pipelines/", HTTP_USER_AGENT="VCIO_API_AGENT")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
-
- def test_schedule_retrieve_anon_user_permitted(self):
- response = self.client.get(
- "/api/v2/pipelines/test_pipeline/", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- self.assertEqual(response.status_code, status.HTTP_200_OK)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_create_schedule_anon_user_not_permitted(self, mock_create_new_job):
- mock_create_new_job.return_value = "work-id2"
-
- data = {"pipeline_id": "test_pipeline2"}
- response = self.client.post(
- "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
-
- self.assertNotEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertEqual(PipelineSchedule.objects.count(), 1)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_create_schedule_with_staff_token_not_permitted(self, mock_create_new_job):
- self.client = APIClient(enforce_csrf_checks=True)
- self.client.credentials(HTTP_AUTHORIZATION=self.admin_token_auth)
-
- mock_create_new_job.return_value = "work-id3"
-
- data = {"pipeline_id": "test_pipeline3"}
- response = self.client.post(
- "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
-
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertNotEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(PipelineSchedule.objects.count(), 1)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_create_schedule_with_staff_session_permitted(self, mock_create_new_job):
- mock_create_new_job.return_value = "work-id4"
- self.client.login(username="admin_with_session", password="adminpassword")
-
- data = {"pipeline_id": "test_pipeline3"}
- response = self.client.post(
- "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
- )
-
- self.assertNotEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(PipelineSchedule.objects.count(), 2)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_schedule_update_anon_user_not_permitted(self, mock_create_new_job):
- mock_create_new_job.return_value = "work-id5"
-
- data = {"run_interval": 2}
- response = self.client.patch(
- "/api/v2/pipelines/test_pipeline/",
- data,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.schedule1.refresh_from_db()
-
- self.assertNotEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertEqual(self.schedule1.run_interval, 1440)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_schedule_update_with_staff_token_not_permitted(self, mock_create_new_job):
- self.client = APIClient(enforce_csrf_checks=True)
- self.client.credentials(HTTP_AUTHORIZATION=self.admin_token_auth)
-
- mock_create_new_job.return_value = "work-id6"
-
- data = {"run_interval": 2}
- response = self.client.patch(
- "/api/v2/pipelines/test_pipeline/",
- data,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.schedule1.refresh_from_db()
-
- self.assertNotEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertEqual(self.schedule1.run_interval, 1440)
-
- @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
- def test_schedule_update_with_staff_session_permitted(self, mock_create_new_job):
- mock_create_new_job.return_value = "work-id7"
- self.client.login(username="admin_with_session", password="adminpassword")
-
- data = {"run_interval": 5}
- response = self.client.patch(
- "/api/v2/pipelines/test_pipeline/",
- data,
- format="json",
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- self.schedule1.refresh_from_db()
-
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertNotEqual(response.status_code, status.HTTP_403_FORBIDDEN)
- self.assertEqual(self.schedule1.run_interval, 5)
-
-
-class CodeFixV2APITest(APITestCase):
- def setUp(self):
- self.advisory = AdvisoryV2.objects.create(
- datasource_id="test_source",
- pipeline_id="test_source_v2",
- advisory_id="TEST-2025-001",
- avid="test_source/TEST-2025-001",
- unique_content_id="a" * 64,
- url="https://example.com/advisory",
- date_collected="2025-07-01T00:00:00Z",
- )
-
- self.affected_package = PackageV2.objects.from_purl(purl="pkg:pypi/affected_package@1.0.0")
- self.fixed_package = PackageV2.objects.from_purl(purl="pkg:pypi/fixed_package@1.0.1")
-
- self.codefix = CodeFixV2.objects.create(
- advisory=self.advisory,
- affected_package=self.affected_package,
- fixed_package=self.fixed_package,
- notes="Security patch",
- is_reviewed=True,
- )
- self.user = ApiUser.objects.create_api_user(username="e@mail.com")
- self.auth = f"Token {self.user.auth_token.key}"
- self.client = APIClient(enforce_csrf_checks=True)
- self.client.credentials(HTTP_AUTHORIZATION=self.auth)
-
- self.url = reverse("advisory-codefix-list")
-
- def test_list_all_codefixes(self):
- with self.assertNumQueries(10):
- response = self.client.get(self.url, HTTP_USER_AGENT="VCIO_API_AGENT")
- assert response.status_code == status.HTTP_200_OK
- assert response.data["count"] == 1
- assert response.data["results"][0]["affected_advisory_id"] == self.advisory.avid
-
- def test_filter_codefix_by_advisory_id_success(self):
- with self.assertNumQueries(10):
- response = self.client.get(
- self.url, {"advisory_id": self.advisory.avid}, HTTP_USER_AGENT="VCIO_API_AGENT"
- )
- assert response.status_code == status.HTTP_200_OK
- assert response.data["count"] == 1
- assert response.data["results"][0]["affected_advisory_id"] == self.advisory.avid
-
- def test_filter_codefix_by_advisory_id_not_found(self):
- with self.assertNumQueries(6):
- response = self.client.get(
- self.url,
- {"advisory_id": "nonexistent/ADVISORY-ID"},
- HTTP_USER_AGENT="VCIO_API_AGENT",
- )
- assert response.status_code == status.HTTP_200_OK
- assert response.data["count"] == 0
+# #
+# # Copyright (c) nexB Inc. and others. All rights reserved.
+# # VulnerableCode is a trademark of nexB Inc.
+# # SPDX-License-Identifier: Apache-2.0
+# # See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
+# # See https://github.com/aboutcode-org/vulnerablecode for support or download.
+# # See https://aboutcode.org for more information about nexB OSS projects.
+# #
+
+# from unittest.mock import patch
+
+# from django.contrib.auth.models import User
+# from django.core.cache import cache
+# from django.db.models import Prefetch
+# from django.urls import reverse
+# from rest_framework import status
+# from rest_framework.test import APIClient
+# from rest_framework.test import APITestCase
+
+# from vulnerabilities.api_v2 import PackageV2Serializer
+# from vulnerabilities.api_v2 import VulnerabilityListSerializer
+# from vulnerabilities.models import AdvisoryV2
+# from vulnerabilities.models import Alias
+# from vulnerabilities.models import ApiUser
+# from vulnerabilities.models import CodeFixV2
+# from vulnerabilities.models import Package
+# from vulnerabilities.models import PackageV2
+# from vulnerabilities.models import PipelineRun
+# from vulnerabilities.models import PipelineSchedule
+# from vulnerabilities.models import Vulnerability
+# from vulnerabilities.models import VulnerabilityReference
+# from vulnerabilities.models import Weakness
+
+
+# class VulnerabilityV2ViewSetTest(APITestCase):
+# def setUp(self):
+# # Create vulnerabilities
+# self.vuln1 = Vulnerability.objects.create(
+# vulnerability_id="VCID-1234", summary="Test vulnerability 1"
+# )
+# self.vuln2 = Vulnerability.objects.create(
+# vulnerability_id="VCID-5678", summary="Test vulnerability 2"
+# )
+
+# # Create aliases
+# Alias.objects.create(alias="CVE-2021-1234", vulnerability=self.vuln1)
+# Alias.objects.create(alias="CVE-2021-5678", vulnerability=self.vuln2)
+
+# # Create weaknesses
+# self.weakness1 = Weakness.objects.create(cwe_id=79)
+# self.weakness1.vulnerabilities.add(self.vuln1)
+
+# self.weakness2 = Weakness.objects.create(cwe_id=89)
+# self.weakness2.vulnerabilities.add(self.vuln2)
+
+# # Create references
+# self.reference1 = VulnerabilityReference.objects.create(
+# url="https://example.com/ref1", reference_type="advisory", reference_id="REF-1"
+# )
+# self.reference1.vulnerabilities.add(self.vuln1)
+
+# self.reference2 = VulnerabilityReference.objects.create(
+# url="https://example.com/ref2", reference_type="exploit", reference_id="REF-2"
+# )
+# self.reference2.vulnerabilities.add(self.vuln2)
+
+# cache.clear()
+# self.client = APIClient(enforce_csrf_checks=True)
+
+# def test_list_vulnerabilities(self):
+# """
+# Test listing vulnerabilities without filters.
+# Should return a paginated response with vulnerabilities dictionary.
+# """
+# url = reverse("vulnerability-v2-list")
+# response = self.client.get(url, format="json")
+# with self.assertNumQueries(4):
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("results", response.data)
+# self.assertIn("vulnerabilities", response.data["results"])
+# self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
+# self.assertIn("VCID-1234", response.data["results"]["vulnerabilities"])
+# self.assertIn("VCID-5678", response.data["results"]["vulnerabilities"])
+# self.assertTrue("url" in response.data["results"]["vulnerabilities"]["VCID-1234"])
+
+# def test_retrieve_vulnerability_detail(self):
+# """
+# Test retrieving vulnerability details by vulnerability_id.
+# """
+# url = reverse("vulnerability-v2-detail", kwargs={"vulnerability_id": "VCID-1234"})
+# with self.assertNumQueries(7):
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(response.data["vulnerability_id"], "VCID-1234")
+# self.assertEqual(response.data["summary"], "Test vulnerability 1")
+# self.assertEqual(response.data["aliases"], ["CVE-2021-1234"])
+# self.assertEqual(len(response.data["weaknesses"]), 1)
+# self.assertEqual(len(response.data["references"]), 1)
+
+# def test_filter_vulnerability_by_vulnerability_id(self):
+# """
+# Test filtering vulnerabilities by vulnerability_id.
+# """
+# url = reverse("vulnerability-v2-list")
+# with self.assertNumQueries(3):
+# response = self.client.get(
+# url,
+# {"vulnerability_id": "VCID-1234"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(response.data["vulnerability_id"], "VCID-1234")
+
+# def test_filter_vulnerability_by_alias(self):
+# """
+# Test filtering vulnerabilities by alias.
+# """
+# url = reverse("vulnerability-v2-list")
+# with self.assertNumQueries(4):
+# response = self.client.get(
+# url,
+# {"alias": "CVE-2021-5678"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("results", response.data)
+# self.assertIn("vulnerabilities", response.data["results"])
+# self.assertEqual(
+# response.data["results"]["vulnerabilities"]["VCID-5678"]["vulnerability_id"],
+# "VCID-5678",
+# )
+
+# def test_filter_vulnerabilities_multiple_ids(self):
+# """
+# Test filtering vulnerabilities by multiple vulnerability_ids.
+# """
+# url = reverse("vulnerability-v2-list")
+# with self.assertNumQueries(4):
+# response = self.client.get(
+# url,
+# {"vulnerability_id": ["VCID-1234", "VCID-5678"]},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
+
+# def test_filter_vulnerabilities_multiple_aliases(self):
+# """
+# Test filtering vulnerabilities by multiple aliases.
+# """
+# url = reverse("vulnerability-v2-list")
+# with self.assertNumQueries(4):
+# response = self.client.get(
+# url,
+# {"alias": ["CVE-2021-1234", "CVE-2021-5678"]},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["vulnerabilities"]), 2)
+
+# def test_invalid_vulnerability_id(self):
+# """
+# Test retrieving a vulnerability with an invalid vulnerability_id.
+# Should return 404 Not Found.
+# """
+# url = reverse("vulnerability-v2-detail", kwargs={"vulnerability_id": "VCID-9999"})
+# with self.assertNumQueries(4):
+# response = self.client.get(
+# url,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+# def test_get_url_in_serializer(self):
+# """
+# Test that the serializer correctly includes the URL field.
+# """
+# vulnerability = Vulnerability.objects.get(vulnerability_id="VCID-1234")
+# serializer = VulnerabilityListSerializer(vulnerability, context={"request": None})
+# self.assertIn("url", serializer.data)
+# self.assertEqual(serializer.data["vulnerability_id"], "VCID-1234")
+
+# def test_list_vulnerabilities_pagination(self):
+# """
+# Test listing vulnerabilities with pagination.
+# """
+# # Create additional vulnerabilities to trigger pagination
+# for i in range(3, 15):
+# Vulnerability.objects.create(
+# vulnerability_id=f"VCID-{i}", summary=f"Test vulnerability {i}"
+# )
+
+# url = reverse("vulnerability-v2-list")
+# response = self.client.get(
+# url,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("results", response.data)
+# self.assertIn("vulnerabilities", response.data["results"])
+# self.assertIn("next", response.data)
+# self.assertIn("previous", response.data)
+# # The 'vulnerabilities' dictionary should contain vulnerabilities up to the page limit
+# self.assertEqual(
+# len(response.data["results"]["vulnerabilities"]), 14
+# ) # Assuming default page size is 100
+
+
+# class PackageV2ViewSetTest(APITestCase):
+# def setUp(self):
+# # Create packages
+# self.package1 = Package.objects.create(
+# package_url="pkg:pypi/django@3.2", name="django", version="3.2", type="pypi"
+# )
+# self.package2 = Package.objects.create(
+# package_url="pkg:npm/lodash@4.17.20", name="lodash", version="4.17.20", type="npm"
+# )
+
+# # Create vulnerabilities
+# self.vuln1 = Vulnerability.objects.create(
+# vulnerability_id="VCID-1234", summary="Test vulnerability 1"
+# )
+# self.vuln2 = Vulnerability.objects.create(
+# vulnerability_id="VCID-5678", summary="Test vulnerability 2"
+# )
+
+# # Associate packages with vulnerabilities
+# self.package1.affected_by_vulnerabilities.add(self.vuln1)
+# self.package2.fixing_vulnerabilities.add(self.vuln2)
+
+# cache.clear()
+# self.client = APIClient(enforce_csrf_checks=True)
+
+# def test_list_packages(self):
+# """
+# Test listing packages without filters.
+# Should return a list of packages with their details and associated vulnerabilities.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(31):
+# response = self.client.get(
+# url,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("results", response.data)
+# self.assertIn("packages", response.data["results"])
+# self.assertIn("vulnerabilities", response.data["results"])
+# self.assertEqual(len(response.data["results"]["packages"]), 2)
+# # Verify that vulnerabilities are included
+# self.assertIsInstance(response.data["results"]["vulnerabilities"], dict)
+# package_vulns = set()
+# for package in response.data["results"]["packages"]:
+# package_vulns.update(package["affected_by_vulnerabilities"])
+# package_vulns.update(package["fixing_vulnerabilities"])
+# self.assertTrue(
+# all(vuln_id in response.data["results"]["vulnerabilities"] for vuln_id in package_vulns)
+# )
+
+# def test_filter_packages_by_purl(self):
+# """
+# Test filtering packages by one or more PURLs.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(19):
+# response = self.client.get(
+# url,
+# {"purl": "pkg:pypi/django@3.2"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["packages"]), 1)
+# self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:pypi/django@3.2")
+
+# def test_filter_packages_by_affected_vulnerability(self):
+# """
+# Test filtering packages by affected_by_vulnerability.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(19):
+# response = self.client.get(
+# url,
+# {"affected_by_vulnerability": "VCID-1234"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["packages"]), 1)
+# self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:pypi/django@3.2")
+
+# def test_filter_packages_by_fixing_vulnerability(self):
+# """
+# Test filtering packages by fixing_vulnerability.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(17):
+# response = self.client.get(
+# url,
+# {"fixing_vulnerability": "VCID-5678"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["packages"]), 1)
+# self.assertEqual(response.data["results"]["packages"][0]["purl"], "pkg:npm/lodash@4.17.20")
+
+# def test_package_serializer_fields(self):
+# """
+# Test that the PackageV2Serializer returns the correct fields and formats them correctly.
+# """
+# # Fetch the package
+# package = Package.objects.get(package_url="pkg:pypi/django@3.2")
+
+# # 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"),
+# to_attr="prefetched_affected_vulnerabilities",
+# )
+# )
+# .first()
+# )
+
+# # Serialize the package
+# serializer = PackageV2Serializer(package)
+# data = serializer.data
+
+# # Verify the presence of required fields
+# self.assertIn("purl", data)
+# self.assertIn("affected_by_vulnerabilities", data)
+# self.assertIn("fixing_vulnerabilities", data)
+# self.assertIn("next_non_vulnerable_version", data)
+# self.assertIn("latest_non_vulnerable_version", data)
+# self.assertIn("risk_score", data)
+
+# # Verify field values
+# self.assertEqual(data["purl"], "pkg:pypi/django@3.2")
+# self.assertEqual(data["next_non_vulnerable_version"], None)
+# self.assertEqual(data["latest_non_vulnerable_version"], None)
+# self.assertEqual(data["risk_score"], None)
+
+# # Verify affected_by_vulnerabilities structure
+# expected_affected_by_vulnerabilities = {
+# "VCID-1234": {
+# "code_fixes": [],
+# "vulnerability_id": "VCID-1234",
+# "fixed_by_packages": None,
+# }
+# }
+# self.assertEqual(data["affected_by_vulnerabilities"], expected_affected_by_vulnerabilities)
+
+# # Verify fixing_vulnerabilities structure
+# expected_fixing_vulnerabilities = []
+# self.assertEqual(data["fixing_vulnerabilities"], expected_fixing_vulnerabilities)
+
+# def test_list_packages_pagination(self):
+# """
+# Test listing packages with pagination.
+# """
+# # Create additional packages to trigger pagination
+# for i in range(3, 15):
+# Package.objects.create(
+# package_url=f"pkg:pypi/package{i}@1.0.{i}",
+# name=f"package{i}",
+# version=f"1.0.{i}",
+# type="pypi",
+# )
+
+# url = reverse("package-v2-list")
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("results", response.data)
+# self.assertIn("packages", response.data["results"])
+# self.assertIn("vulnerabilities", response.data["results"])
+# self.assertIn("next", response.data)
+# self.assertIn("previous", response.data)
+# self.assertEqual(
+# len(response.data["results"]["packages"]), 14
+# ) # Assuming default page size is 100
+
+# def test_invalid_vulnerability_filter(self):
+# """
+# Test filtering packages with an invalid vulnerability ID.
+# Should return an empty list.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(3):
+# response = self.client.get(
+# url,
+# {"affected_by_vulnerability": "VCID-9999"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["packages"]), 0)
+
+# def test_invalid_purl_filter(self):
+# """
+# Test filtering packages with an invalid PURL.
+# Should return an empty list.
+# """
+# url = reverse("package-v2-list")
+# with self.assertNumQueries(3):
+# response = self.client.get(
+# url,
+# {"purl": "pkg:nonexistent/package@1.0.0"},
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(len(response.data["results"]["packages"]), 0)
+
+# def test_get_affected_by_vulnerabilities(self):
+# """
+# Test the get_affected_by_vulnerabilities method in 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"),
+# to_attr="prefetched_affected_vulnerabilities",
+# )
+# )
+# .first()
+# )
+
+# serializer = PackageV2Serializer()
+# vulnerabilities = serializer.get_affected_by_vulnerabilities(package)
+# self.assertEqual(
+# vulnerabilities,
+# {
+# "VCID-1234": {
+# "code_fixes": [],
+# "vulnerability_id": "VCID-1234",
+# "fixed_by_packages": None,
+# }
+# },
+# )
+
+# def test_get_fixing_vulnerabilities(self):
+# """
+# Test the get_fixing_vulnerabilities method in the serializer.
+# """
+# package = Package.objects.get(package_url="pkg:npm/lodash@4.17.20")
+# serializer = PackageV2Serializer()
+# vulnerabilities = serializer.get_fixing_vulnerabilities(package)
+# self.assertEqual(vulnerabilities, ["VCID-5678"])
+
+# def test_bulk_lookup_with_valid_purls(self):
+# """
+# Test bulk lookup with valid PURLs.
+# Should return packages and their associated vulnerabilities.
+# """
+# url = reverse("package-v2-bulk-lookup")
+# data = {"purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"]}
+# with self.assertNumQueries(27):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("packages", response.data)
+# self.assertIn("vulnerabilities", response.data)
+# self.assertEqual(len(response.data["packages"]), 2)
+# # Verify that the returned data matches the packages
+# purls = [package["purl"] for package in response.data["packages"]]
+# self.assertIn("pkg:pypi/django@3.2", purls)
+# self.assertIn("pkg:npm/lodash@4.17.20", purls)
+# # Verify that vulnerabilities are included
+# package_vulns = set()
+# for package in response.data["packages"]:
+# package_vulns.update(package["affected_by_vulnerabilities"])
+# package_vulns.update(package["fixing_vulnerabilities"])
+# self.assertTrue(
+# all(vuln_id in response.data["vulnerabilities"] for vuln_id in package_vulns)
+# )
+
+# def test_bulk_lookup_with_invalid_purls(self):
+# """
+# Test bulk lookup with invalid PURLs.
+# """
+# url = reverse("package-v2-bulk-lookup")
+# data = {"purls": ["pkg:pypi/nonexistent@1.0.0", "pkg:npm/unknown@0.0.1"]}
+# with self.assertNumQueries(3):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # Since the packages don't exist, the response should be empty
+# self.assertEqual(len(response.data["packages"]), 0)
+# self.assertEqual(len(response.data["vulnerabilities"]), 0)
+
+# def test_bulk_lookup_with_empty_purls(self):
+# """
+# Test bulk lookup with empty purls list.
+# Should return 400 Bad Request.
+# """
+# url = reverse("package-v2-bulk-lookup")
+# data = {"purls": []}
+# with self.assertNumQueries(2):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+# self.assertIn("error", response.data)
+# self.assertIn("message", response.data)
+# self.assertEqual(response.data["message"], "A non-empty 'purls' list of PURLs is required.")
+
+# def test_bulk_search_with_valid_purls(self):
+# """
+# Test bulk search with valid PURLs.
+# Should return packages and their associated vulnerabilities.
+# """
+# url = reverse("package-v2-bulk-search")
+# data = {"purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"]}
+# with self.assertNumQueries(27):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("packages", response.data)
+# self.assertIn("vulnerabilities", response.data)
+# self.assertEqual(len(response.data["packages"]), 2)
+# purls = [package["purl"] for package in response.data["packages"]]
+# self.assertIn("pkg:pypi/django@3.2", purls)
+# self.assertIn("pkg:npm/lodash@4.17.20", purls)
+# # Verify that vulnerabilities are included
+# package_vulns = set()
+# for package in response.data["packages"]:
+# package_vulns.update(package["affected_by_vulnerabilities"])
+# package_vulns.update(package["fixing_vulnerabilities"])
+# self.assertTrue(
+# all(vuln_id in response.data["vulnerabilities"] for vuln_id in package_vulns)
+# )
+
+# def test_bulk_search_with_purl_only_true(self):
+# """
+# Test bulk search with purl_only set to True.
+# Should return only the PURLs of vulnerable packages.
+# """
+# url = reverse("package-v2-bulk-search")
+# data = {
+# "purls": ["pkg:pypi/django@3.2", "pkg:npm/lodash@4.17.20"],
+# "purl_only": True,
+# }
+# with self.assertNumQueries(16):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # Since purl_only=True, response should be a list of PURLs
+# self.assertIsInstance(response.data, list)
+# # Only vulnerable packages should be included
+# self.assertEqual(len(response.data), 1)
+# self.assertEqual(response.data, ["pkg:pypi/django@3.2"])
+
+# def test_bulk_search_with_plain_purl_true(self):
+# """
+# Test bulk search with plain_purl set to True.
+# Should return packages grouped by plain PURLs.
+# """
+# # Create another package with the same name and version but different qualifiers
+# Package.objects.create(
+# name="django",
+# version="3.2",
+# type="pypi",
+# qualifiers={"extension": "tar.gz"},
+# )
+
+# url = reverse("package-v2-bulk-search")
+# data = {
+# "purls": ["pkg:pypi/django@3.2", "pkg:pypi/django@3.2?extension=tar.gz"],
+# "plain_purl": True,
+# }
+# with self.assertNumQueries(15):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertIn("packages", response.data)
+# self.assertIn("vulnerabilities", response.data)
+# # Since plain_purl=True, packages with the same type, namespace, name, version are grouped
+# self.assertEqual(len(response.data["packages"]), 1)
+# purl = response.data["packages"][0]["purl"]
+# self.assertTrue(purl.startswith("pkg:pypi/django@3.2"))
+
+# def test_bulk_search_with_purl_only_and_plain_purl_true(self):
+# """
+# Test bulk search with purl_only and plain_purl both set to True.
+# Should return only the plain PURLs of vulnerable packages.
+# """
+# url = reverse("package-v2-bulk-search")
+# data = {
+# "purls": ["pkg:pypi/django@3.2", "pkg:pypi/django@3.1"],
+# "purl_only": True,
+# "plain_purl": True,
+# }
+# with self.assertNumQueries(10):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # Response should be a list of plain PURLs
+# self.assertIsInstance(response.data, list)
+# # Only one plain PURL should be returned for vulnerable packages
+# self.assertEqual(len(response.data), 1)
+# self.assertEqual(response.data, ["pkg:pypi/django@3.2"])
+
+# def test_bulk_search_with_invalid_purls(self):
+# """
+# Test bulk search with invalid PURLs.
+# Should return an empty response.
+# """
+# url = reverse("package-v2-bulk-search")
+# data = {"purls": ["pkg:pypi/nonexistent@1.0.0", "pkg:npm/unknown@0.0.1"]}
+# with self.assertNumQueries(3):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # Since the packages don't exist, the response should be empty
+# self.assertEqual(len(response.data["packages"]), 0)
+# self.assertEqual(len(response.data["vulnerabilities"]), 0)
+
+# def test_bulk_search_with_empty_purls(self):
+# """
+# Test bulk search with empty purls list.
+# Should return 400 Bad Request.
+# """
+# url = reverse("package-v2-bulk-search")
+# data = {"purls": []}
+# with self.assertNumQueries(2):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+# self.assertIn("error", response.data)
+# self.assertIn("message", response.data)
+# self.assertEqual(response.data["message"], "A non-empty 'purls' list of PURLs is required.")
+
+# def test_all_vulnerable_packages(self):
+# """
+# Test the 'all' endpoint that returns all vulnerable package URLs.
+# """
+# url = reverse("package-v2-all")
+# with self.assertNumQueries(3):
+# response = self.client.get(url, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # Since package1 is vulnerable, it should be returned
+# expected_purls = ["pkg:pypi/django@3.2"]
+# self.assertEqual(sorted(response.data), sorted(expected_purls))
+
+# def test_lookup_with_valid_purl(self):
+# """
+# Test the 'lookup' endpoint with a valid PURL.
+# Should return the package and its associated vulnerabilities.
+# """
+# url = reverse("package-v2-lookup")
+# data = {"purl": "pkg:pypi/django@3.2"}
+# with self.assertNumQueries(12):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(1, len(response.data))
+# self.assertIn("purl", response.data[0])
+# self.assertIn("affected_by_vulnerabilities", response.data[0])
+# self.assertIn("fixing_vulnerabilities", response.data[0])
+# self.assertIn("next_non_vulnerable_version", response.data[0])
+# self.assertIn("latest_non_vulnerable_version", response.data[0])
+# self.assertEqual(response.data[0]["purl"], "pkg:pypi/django@3.2")
+# self.assertEqual(
+# response.data[0]["affected_by_vulnerabilities"],
+# {
+# "VCID-1234": {
+# "code_fixes": [],
+# "vulnerability_id": "VCID-1234",
+# "fixed_by_packages": None,
+# }
+# },
+# )
+# self.assertEqual(response.data[0]["fixing_vulnerabilities"], [])
+
+# def test_lookup_with_invalid_purl(self):
+# """
+# Test the 'lookup' endpoint with a PURL that does not exist.
+# Should return empty packages and vulnerabilities.
+# """
+# url = reverse("package-v2-lookup")
+# data = {"purl": "pkg:pypi/nonexistent@1.0.0"}
+# with self.assertNumQueries(3):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # No packages or vulnerabilities should be returned
+# self.assertEqual(len(response.data), 0)
+
+# def test_lookup_with_missing_purl(self):
+# """
+# Test the 'lookup' endpoint without providing a 'purl'.
+# Should return 400 Bad Request.
+# """
+# url = reverse("package-v2-lookup")
+# data = {}
+# with self.assertNumQueries(2):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+# self.assertIn("error", response.data)
+# self.assertIn("message", response.data)
+# self.assertEqual(response.data["message"], "A 'purl' is required.")
+
+# def test_lookup_with_invalid_purl_format(self):
+# """
+# Test the 'lookup' endpoint with an invalid PURL format.
+# Should return empty packages and vulnerabilities.
+# """
+# url = reverse("package-v2-lookup")
+# data = {"purl": "invalid_purl_format"}
+# with self.assertNumQueries(3):
+# response = self.client.post(url, data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# # No packages or vulnerabilities should be returned
+# self.assertEqual(len(response.data), 0)
+
+
+# class PipelineScheduleV2ViewSetTest(APITestCase):
+# def setUp(self):
+# # Reset the api throttling for anon user to properly test the
+# # access on schedule endpoint.
+# # DRF stores throttling state in cache, clear cache to reset throttling.
+# # See https://www.django-rest-framework.org/api-guide/throttling/#setting-up-the-cache
+# cache.clear()
+
+# patcher = patch.object(PipelineSchedule, "create_new_job")
+# self.mock_create_new_job = patcher.start()
+# self.addCleanup(patcher.stop)
+
+# self.mock_create_new_job.return_value = "work-id"
+
+# self.schedule1 = PipelineSchedule.objects.create(
+# pipeline_id="test_pipeline",
+# )
+# self.run1 = PipelineRun.objects.create(
+# pipeline=self.schedule1,
+# )
+
+# self.admin_user = User.objects.create_superuser(
+# username="admin_with_session",
+# password="adminpassword",
+# email="admin@test.com",
+# )
+
+# self.admin_token_only_user = ApiUser.objects.create_api_user(
+# username="staff_with_token",
+# is_staff=True,
+# )
+# self.admin_token_auth = f"Token {self.admin_token_only_user.auth_token.key}"
+
+# def test_schedule_list_anon_user_permitted(self):
+# response = self.client.get("/api/v2/pipelines/", HTTP_USER_AGENT="VCIO_API_AGENT")
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+# def test_schedule_retrieve_anon_user_permitted(self):
+# response = self.client.get(
+# "/api/v2/pipelines/test_pipeline/", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_create_schedule_anon_user_not_permitted(self, mock_create_new_job):
+# mock_create_new_job.return_value = "work-id2"
+
+# data = {"pipeline_id": "test_pipeline2"}
+# response = self.client.post(
+# "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+
+# self.assertNotEqual(response.status_code, status.HTTP_201_CREATED)
+# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertEqual(PipelineSchedule.objects.count(), 1)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_create_schedule_with_staff_token_not_permitted(self, mock_create_new_job):
+# self.client = APIClient(enforce_csrf_checks=True)
+# self.client.credentials(HTTP_AUTHORIZATION=self.admin_token_auth)
+
+# mock_create_new_job.return_value = "work-id3"
+
+# data = {"pipeline_id": "test_pipeline3"}
+# response = self.client.post(
+# "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+
+# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertNotEqual(response.status_code, status.HTTP_201_CREATED)
+# self.assertEqual(PipelineSchedule.objects.count(), 1)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_create_schedule_with_staff_session_permitted(self, mock_create_new_job):
+# mock_create_new_job.return_value = "work-id4"
+# self.client.login(username="admin_with_session", password="adminpassword")
+
+# data = {"pipeline_id": "test_pipeline3"}
+# response = self.client.post(
+# "/api/v2/pipelines/", data, format="json", HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+
+# self.assertNotEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+# self.assertEqual(PipelineSchedule.objects.count(), 2)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_schedule_update_anon_user_not_permitted(self, mock_create_new_job):
+# mock_create_new_job.return_value = "work-id5"
+
+# data = {"run_interval": 2}
+# response = self.client.patch(
+# "/api/v2/pipelines/test_pipeline/",
+# data,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.schedule1.refresh_from_db()
+
+# self.assertNotEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertEqual(self.schedule1.run_interval, 1440)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_schedule_update_with_staff_token_not_permitted(self, mock_create_new_job):
+# self.client = APIClient(enforce_csrf_checks=True)
+# self.client.credentials(HTTP_AUTHORIZATION=self.admin_token_auth)
+
+# mock_create_new_job.return_value = "work-id6"
+
+# data = {"run_interval": 2}
+# response = self.client.patch(
+# "/api/v2/pipelines/test_pipeline/",
+# data,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.schedule1.refresh_from_db()
+
+# self.assertNotEqual(response.status_code, status.HTTP_200_OK)
+# self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertEqual(self.schedule1.run_interval, 1440)
+
+# @patch("vulnerabilities.models.PipelineSchedule.create_new_job")
+# def test_schedule_update_with_staff_session_permitted(self, mock_create_new_job):
+# mock_create_new_job.return_value = "work-id7"
+# self.client.login(username="admin_with_session", password="adminpassword")
+
+# data = {"run_interval": 5}
+# response = self.client.patch(
+# "/api/v2/pipelines/test_pipeline/",
+# data,
+# format="json",
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# self.schedule1.refresh_from_db()
+
+# self.assertEqual(response.status_code, status.HTTP_200_OK)
+# self.assertNotEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+# self.assertEqual(self.schedule1.run_interval, 5)
+
+
+# class CodeFixV2APITest(APITestCase):
+# def setUp(self):
+# self.advisory = AdvisoryV2.objects.create(
+# datasource_id="test_source",
+# pipeline_id="test_source_v2",
+# advisory_id="TEST-2025-001",
+# avid="test_source/TEST-2025-001",
+# unique_content_id="a" * 64,
+# url="https://example.com/advisory",
+# date_collected="2025-07-01T00:00:00Z",
+# )
+
+# self.affected_package = PackageV2.objects.from_purl(purl="pkg:pypi/affected_package@1.0.0")
+# self.fixed_package = PackageV2.objects.from_purl(purl="pkg:pypi/fixed_package@1.0.1")
+
+# self.codefix = CodeFixV2.objects.create(
+# advisory=self.advisory,
+# affected_package=self.affected_package,
+# fixed_package=self.fixed_package,
+# notes="Security patch",
+# is_reviewed=True,
+# )
+# self.user = ApiUser.objects.create_api_user(username="e@mail.com")
+# self.auth = f"Token {self.user.auth_token.key}"
+# self.client = APIClient(enforce_csrf_checks=True)
+# self.client.credentials(HTTP_AUTHORIZATION=self.auth)
+
+# self.url = reverse("advisory-codefix-list")
+
+# def test_list_all_codefixes(self):
+# with self.assertNumQueries(10):
+# response = self.client.get(self.url, HTTP_USER_AGENT="VCIO_API_AGENT")
+# assert response.status_code == status.HTTP_200_OK
+# assert response.data["count"] == 1
+# assert response.data["results"][0]["affected_advisory_id"] == self.advisory.avid
+
+# def test_filter_codefix_by_advisory_id_success(self):
+# with self.assertNumQueries(10):
+# response = self.client.get(
+# self.url, {"advisory_id": self.advisory.avid}, HTTP_USER_AGENT="VCIO_API_AGENT"
+# )
+# assert response.status_code == status.HTTP_200_OK
+# assert response.data["count"] == 1
+# assert response.data["results"][0]["affected_advisory_id"] == self.advisory.avid
+
+# def test_filter_codefix_by_advisory_id_not_found(self):
+# with self.assertNumQueries(6):
+# response = self.client.get(
+# self.url,
+# {"advisory_id": "nonexistent/ADVISORY-ID"},
+# HTTP_USER_AGENT="VCIO_API_AGENT",
+# )
+# assert response.status_code == status.HTTP_200_OK
+# assert response.data["count"] == 0
diff --git a/vulnerabilities/tests/test_forms.py b/vulnerabilities/tests/test_forms.py
index 430ca8b01..b5ee73424 100644
--- a/vulnerabilities/tests/test_forms.py
+++ b/vulnerabilities/tests/test_forms.py
@@ -15,28 +15,27 @@
from vulnerabilities.forms import VulnerabilitySearchForm
from vulnerabilities.models import Vulnerability
-
-class TestVulnerabilitySearchForm(TestCase):
- def setUp(self) -> None:
- self.client = Client()
- session = self.client.session
- session["altcha_verified_at"] = time.time()
- session.save()
- self.vulnerability = Vulnerability.objects.create(
- vulnerability_id="VCID-1234",
- summary="test-vuln1",
- )
-
- def test_VulnerabilitySearchForm__is_valid_with_simple_input(self):
- form = VulnerabilitySearchForm(data={"search": "vcid-1234"})
- assert form.is_valid()
-
- def test_vulnerabilities_search_view_can_lookup_by_vcid(self):
- vcid = self.vulnerability.vulnerability_id
- response = self.client.get(f"/vulnerabilities/{vcid}?search=vcid-1234")
- self.assertContains(response, "test-vuln1", status_code=200)
-
- def test_vulnerabilities_search_view_does_not_work_by_pk(self):
- pk = self.vulnerability.pk
- response = self.client.get(f"/vulnerabilities/{pk}")
- self.assertEqual(response.status_code, 404)
+# class TestVulnerabilitySearchForm(TestCase):
+# def setUp(self) -> None:
+# self.client = Client()
+# session = self.client.session
+# session["altcha_verified_at"] = time.time()
+# session.save()
+# self.vulnerability = Vulnerability.objects.create(
+# vulnerability_id="VCID-1234",
+# summary="test-vuln1",
+# )
+
+# def test_VulnerabilitySearchForm__is_valid_with_simple_input(self):
+# form = VulnerabilitySearchForm(data={"search": "vcid-1234"})
+# assert form.is_valid()
+
+# def test_vulnerabilities_search_view_can_lookup_by_vcid(self):
+# vcid = self.vulnerability.vulnerability_id
+# response = self.client.get(f"/vulnerabilities/{vcid}?search=vcid-1234")
+# self.assertContains(response, "test-vuln1", status_code=200)
+
+# def test_vulnerabilities_search_view_does_not_work_by_pk(self):
+# pk = self.vulnerability.pk
+# response = self.client.get(f"/vulnerabilities/{pk}")
+# self.assertEqual(response.status_code, 404)
diff --git a/vulnerabilities/tests/test_models.py b/vulnerabilities/tests/test_models.py
index b72eef02f..6e327ed6e 100644
--- a/vulnerabilities/tests/test_models.py
+++ b/vulnerabilities/tests/test_models.py
@@ -218,88 +218,89 @@ def setUp(self):
vulnerability_id="VCID-wxyz-0000-0000",
)
- def test_fixed_package_details(self):
- searched_for_package = self.package_pypi_redis_4_1_1
-
- assert searched_for_package.package_url == "pkg:pypi/redis@4.1.1"
- assert searched_for_package.plain_package_url == "pkg:pypi/redis@4.1.1"
- assert searched_for_package.get_absolute_url() == "/packages/pkg:pypi/redis@4.1.1"
- assert searched_for_package.purl == "pkg:pypi/redis@4.1.1"
-
- assert len(searched_for_package.affected_by) == 2
-
- assert self.vuln_VCID_g2fu_45jw_aaan in searched_for_package.affected_by
- assert (
- self.package_pypi_redis_4_3_6 in self.vuln_VCID_g2fu_45jw_aaan.fixed_by_packages.all()
- )
-
- assert self.vuln_VCID_rqe1_dkmg_aaad in searched_for_package.affected_by
- assert (
- self.package_pypi_redis_5_0_0b1 in self.vuln_VCID_rqe1_dkmg_aaad.fixed_by_packages.all()
- )
-
- searched_for_package_details = searched_for_package.fixed_package_details
-
- package_details = {
- "purl": PackageURL(
- type="pypi",
- name="redis",
- version="4.1.1",
- ),
- "next_non_vulnerable": self.package_pypi_redis_5_0_0b1,
- "latest_non_vulnerable": self.package_pypi_redis_5_0_0b1,
- "vulnerabilities": [
- {
- "vulnerability": self.vuln_VCID_g2fu_45jw_aaan,
- "fixed_by_package_details": [
- {
- "fixed_by_purl": PackageURL(
- type="pypi",
- namespace=None,
- name="redis",
- version="4.3.6",
- qualifiers={},
- subpath=None,
- ),
- "fixed_by_purl_vulnerabilities": [self.vuln_VCID_rqe1_dkmg_aaad],
- }
- ],
- "fixed_by_purl": [],
- "fixed_by_purl_vulnerabilities": [],
- },
- {
- "vulnerability": self.vuln_VCID_rqe1_dkmg_aaad,
- "fixed_by_package_details": [
- {
- "fixed_by_purl": PackageURL(
- type="pypi",
- namespace=None,
- name="redis",
- version="5.0.0b1",
- qualifiers={},
- subpath=None,
- ),
- "fixed_by_purl_vulnerabilities": [],
- }
- ],
- "fixed_by_purl": [],
- "fixed_by_purl_vulnerabilities": [],
- },
- ],
- }
-
- assert searched_for_package_details == package_details
-
- assert (
- searched_for_package_details.get("latest_non_vulnerable")
- == self.package_pypi_redis_5_0_0b1
- )
-
- searched_for_package_fixing = searched_for_package.fixing
- assert type(searched_for_package_fixing) == models.VulnerabilityQuerySet
- assert searched_for_package_fixing.count() == 0
- assert len(searched_for_package_fixing) == 0
- assert list(searched_for_package_fixing) == []
+ # FIXME: Remove this test after removing V1 models
+ # def test_fixed_package_details(self):
+ # searched_for_package = self.package_pypi_redis_4_1_1
+
+ # assert searched_for_package.package_url == "pkg:pypi/redis@4.1.1"
+ # assert searched_for_package.plain_package_url == "pkg:pypi/redis@4.1.1"
+ # assert searched_for_package.get_absolute_url() == "/packages/pkg:pypi/redis@4.1.1"
+ # assert searched_for_package.purl == "pkg:pypi/redis@4.1.1"
+
+ # assert len(searched_for_package.affected_by) == 2
+
+ # assert self.vuln_VCID_g2fu_45jw_aaan in searched_for_package.affected_by
+ # assert (
+ # self.package_pypi_redis_4_3_6 in self.vuln_VCID_g2fu_45jw_aaan.fixed_by_packages.all()
+ # )
+
+ # assert self.vuln_VCID_rqe1_dkmg_aaad in searched_for_package.affected_by
+ # assert (
+ # self.package_pypi_redis_5_0_0b1 in self.vuln_VCID_rqe1_dkmg_aaad.fixed_by_packages.all()
+ # )
+
+ # searched_for_package_details = searched_for_package.fixed_package_details
+
+ # package_details = {
+ # "purl": PackageURL(
+ # type="pypi",
+ # name="redis",
+ # version="4.1.1",
+ # ),
+ # "next_non_vulnerable": self.package_pypi_redis_5_0_0b1,
+ # "latest_non_vulnerable": self.package_pypi_redis_5_0_0b1,
+ # "vulnerabilities": [
+ # {
+ # "vulnerability": self.vuln_VCID_g2fu_45jw_aaan,
+ # "fixed_by_package_details": [
+ # {
+ # "fixed_by_purl": PackageURL(
+ # type="pypi",
+ # namespace=None,
+ # name="redis",
+ # version="4.3.6",
+ # qualifiers={},
+ # subpath=None,
+ # ),
+ # "fixed_by_purl_vulnerabilities": [self.vuln_VCID_rqe1_dkmg_aaad],
+ # }
+ # ],
+ # "fixed_by_purl": [],
+ # "fixed_by_purl_vulnerabilities": [],
+ # },
+ # {
+ # "vulnerability": self.vuln_VCID_rqe1_dkmg_aaad,
+ # "fixed_by_package_details": [
+ # {
+ # "fixed_by_purl": PackageURL(
+ # type="pypi",
+ # namespace=None,
+ # name="redis",
+ # version="5.0.0b1",
+ # qualifiers={},
+ # subpath=None,
+ # ),
+ # "fixed_by_purl_vulnerabilities": [],
+ # }
+ # ],
+ # "fixed_by_purl": [],
+ # "fixed_by_purl_vulnerabilities": [],
+ # },
+ # ],
+ # }
+
+ # assert searched_for_package_details == package_details
+
+ # assert (
+ # searched_for_package_details.get("latest_non_vulnerable")
+ # == self.package_pypi_redis_5_0_0b1
+ # )
+
+ # searched_for_package_fixing = searched_for_package.fixing
+ # assert type(searched_for_package_fixing) == models.VulnerabilityQuerySet
+ # assert searched_for_package_fixing.count() == 0
+ # assert len(searched_for_package_fixing) == 0
+ # assert list(searched_for_package_fixing) == []
def test_get_vulnerable_packages(self):
vuln_packages = Package.objects.vulnerable()
@@ -320,34 +321,34 @@ def test_get_vulnerable_packages(self):
assert len(second_vulnerable_package_matching_fixed_packages) == 2
assert first_fixed_by_package.purl == "pkg:pypi/redis@4.3.6"
- def test_string_to_package(self):
- purl_string = "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
- purl = PackageURL.from_string(purl_string)
- purl_to_dict = purl.to_dict()
-
- # For namespace, version, qualifiers and subpath, we need to add the or * to avoid an
- # IntegrityError, e.g., django.db.utils.IntegrityError: null value in column "subpath" violates
- # not-null constraint
- vulnerablecode_package = models.Package.objects.create(
- type=purl_to_dict.get("type"),
- namespace=purl_to_dict.get("namespace") or "",
- name=purl_to_dict.get("name"),
- version=purl_to_dict.get("version") or "",
- qualifiers=purl_to_dict.get("qualifiers") or {},
- subpath=purl_to_dict.get("subpath") or "",
- )
-
- assert type(vulnerablecode_package) == models.Package
- assert vulnerablecode_package.purl == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
- assert vulnerablecode_package.package_url == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
- assert (
- vulnerablecode_package.plain_package_url
- == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
- )
- assert (
- vulnerablecode_package.get_absolute_url()
- == "/packages/pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
- )
+ # def test_string_to_package(self):
+ # purl_string = "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
+ # purl = PackageURL.from_string(purl_string)
+ # purl_to_dict = purl.to_dict()
+
+ # # For namespace, version, qualifiers and subpath, we need to add the or * to avoid an
+ # # IntegrityError, e.g., django.db.utils.IntegrityError: null value in column "subpath" violates
+ # # not-null constraint
+ # vulnerablecode_package = models.Package.objects.create(
+ # type=purl_to_dict.get("type"),
+ # namespace=purl_to_dict.get("namespace") or "",
+ # name=purl_to_dict.get("name"),
+ # version=purl_to_dict.get("version") or "",
+ # qualifiers=purl_to_dict.get("qualifiers") or {},
+ # subpath=purl_to_dict.get("subpath") or "",
+ # )
+
+ # assert type(vulnerablecode_package) == models.Package
+ # assert vulnerablecode_package.purl == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
+ # assert vulnerablecode_package.package_url == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
+ # assert (
+ # vulnerablecode_package.plain_package_url
+ # == "pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
+ # )
+ # assert (
+ # vulnerablecode_package.get_absolute_url()
+ # == "/packages/pkg:maven/org.apache.tomcat/tomcat@10.0.0-M4"
+ # )
def test_univers_version_comparisons(self):
assert versions.PypiVersion("1.2.3") < versions.PypiVersion("1.2.4")
diff --git a/vulnerabilities/tests/test_throttling.py b/vulnerabilities/tests/test_throttling.py
index 343053bee..225169e44 100644
--- a/vulnerabilities/tests/test_throttling.py
+++ b/vulnerabilities/tests/test_throttling.py
@@ -23,7 +23,7 @@
def simulate_throttle_usage(url, client, mock_use_count):
throttle = PermissionBasedUserRateThrottle()
- request = client.get(url, HTTP_USER_AGENT="VCIO_API_AGENT").wsgi_request
+ request = client.post(url, HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}).wsgi_request
if cache_key := throttle.get_cache_key(request, view=None):
now = throttle.timer()
@@ -93,122 +93,127 @@ def setUp(self):
def test_user_with_low_perm_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.th_low_user_csrf_client,
mock_use_count=19,
)
- response = self.th_low_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_low_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# exhausted 20/minute allowed requests.
- response = self.th_low_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_low_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
def test_basic_user_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.basic_user_csrf_client,
mock_use_count=29,
)
- response = self.basic_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.basic_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# exhausted 30/minute allowed requests.
- response = self.basic_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.basic_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
def test_user_with_medium_perm_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.th_medium_user_csrf_client,
mock_use_count=29,
)
- response = self.th_medium_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_medium_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
+ print(response.json())
self.assertEqual(response.status_code, status.HTTP_200_OK)
# exhausted 30/minute allowed requests for user with 14400 perm.
- response = self.th_medium_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_medium_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
def test_user_with_high_perm_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.th_high_user_csrf_client,
mock_use_count=0,
)
- response = self.th_high_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_high_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# exhausted 18000/hr allowed requests for user with 18000 perm.
- response = self.th_high_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_high_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
def test_user_with_unrestricted_perm_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.th_unrestricted_user_csrf_client,
mock_use_count=20000,
)
# no throttling for user with unrestricted perm.
- response = self.th_unrestricted_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_unrestricted_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_user_in_group_with_unrestricted_perm_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.th_group_user_csrf_client,
mock_use_count=20000,
)
# no throttling for user in group with unrestricted perm.
- response = self.th_group_user_csrf_client.get(
- "/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.th_group_user_csrf_client.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", data={"purls": []}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_anon_throttling(self):
simulate_throttle_usage(
- url="/api/packages",
+ url="/api/v3/packages",
client=self.csrf_client_anon,
mock_use_count=9,
)
- response = self.csrf_client_anon.get("/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT")
+ response = self.csrf_client_anon.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", format="json", data={"purls": []}
+ )
self.assertEqual(response.status_code, status.HTTP_200_OK)
# exhausted 3600/hr allowed requests for anon.
- response = self.csrf_client_anon.get("/api/packages", HTTP_USER_AGENT="VCIO_API_AGENT")
+ response = self.csrf_client_anon.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", format="json", data={"purls": []}
+ )
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
self.assertEqual(
response.data.get("message"),
"Your request has been throttled. Please contact support@nexb.com",
)
- response = self.csrf_client_anon.get(
- "/api/vulnerabilities", HTTP_USER_AGENT="VCIO_API_AGENT"
+ response = self.csrf_client_anon.post(
+ "/api/v3/packages", HTTP_USER_AGENT="VCIO_API_AGENT", format="json", data={"purls": []}
)
# 429 - too many requests for anon user
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
@@ -220,7 +225,7 @@ def test_anon_throttling(self):
data = json.dumps({"purls": ["pkg:foo/bar"]})
response = self.csrf_client_anon.post(
- "/api/packages/bulk_search",
+ "/api/v3/packages",
data=data,
content_type="application/json",
HTTP_USER_AGENT="VCIO_API_AGENT",
diff --git a/vulnerabilities/tests/test_view.py b/vulnerabilities/tests/test_view.py
index 1774b1c22..bcee104ac 100644
--- a/vulnerabilities/tests/test_view.py
+++ b/vulnerabilities/tests/test_view.py
@@ -22,6 +22,7 @@
from vulnerabilities.models import Alias
from vulnerabilities.models import FixingPackageRelatedVulnerability
from vulnerabilities.models import Package
+from vulnerabilities.models import PackageV2
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilitySeverity
from vulnerabilities.templatetags.url_filters import url_quote_filter
@@ -33,162 +34,162 @@
TEST_DIR = os.path.join(BASE_DIR, "test_data/package_sort")
-class PackageSearchTestCase(TestCase):
- def setUp(self):
- self.client = Client()
- session = self.client.session
- session["altcha_verified_at"] = time.time()
- session.save()
- packages = [
- "pkg:nginx/nginx@0.6.18",
- "pkg:nginx/nginx@1.20.0",
- "pkg:nginx/nginx@1.21.0",
- "pkg:nginx/nginx@1.20.1",
- "pkg:nginx/nginx@1.9.5",
- "pkg:nginx/nginx@1.17.2",
- "pkg:nginx/nginx@1.17.3",
- "pkg:nginx/nginx@1.16.1",
- "pkg:nginx/nginx@1.15.5",
- "pkg:nginx/nginx@1.15.6",
- "pkg:nginx/nginx@1.14.1",
- "pkg:nginx/nginx@1.0.7",
- "pkg:nginx/nginx@1.0.15",
- "pkg:nginx/nginx@1.0.15?foo=bar",
- "pkg:pypi/foo@1",
- ]
- self.packages = packages
- for package in packages:
- purl = PackageURL.from_string(package)
- attrs = {k: v for k, v in purl.to_dict().items() if v}
- Package.objects.create(**attrs)
-
- def test_packages_search_view_paginator(self):
- response = self.client.get("/packages/search/?type=deb&name=&page=1")
- self.assertEqual(response.status_code, 200)
- response = self.client.get("/packages/search/?type=deb&name=&page=*")
- self.assertEqual(response.status_code, 404)
- response = self.client.get("/packages/search/?type=deb&name=&page=")
- self.assertEqual(response.status_code, 200)
- response = self.client.get("/packages/search/?type=&name=&page=")
- self.assertEqual(response.status_code, 200)
-
- def test_package_view(self):
- qs = PackageSearch().get_queryset(query="pkg:nginx/nginx@1.0.15?foo=bar")
- pkgs = list(qs)
- self.assertEqual(len(pkgs), 2)
- self.assertEqual(pkgs[0].purl, "pkg:nginx/nginx@1.0.15")
-
- def test_package_detail_view(self):
- package = PackageDetails(kwargs={"purl": "pkg:nginx/nginx@1.0.15"}).get_object()
- assert package.purl == "pkg:nginx/nginx@1.0.15"
-
- def test_package_view_with_purl_fragment(self):
- qs = PackageSearch().get_queryset(query="nginx@1.0.15")
- pkgs = list(qs)
- self.assertEqual(len(pkgs), 2)
- self.assertEqual(pkgs[0].purl, "pkg:nginx/nginx@1.0.15")
- self.assertEqual(pkgs[1].purl, "pkg:nginx/nginx@1.0.15?foo=bar")
-
- def test_package_view_with_purl_fragment_2(self):
- qs = PackageSearch().get_queryset(query="nginx/nginx")
- pkgs = list(qs)
- pkgs = [p.purl for p in pkgs]
- expected = [
- "pkg:nginx/nginx@0.6.18",
- "pkg:nginx/nginx@1.0.15",
- "pkg:nginx/nginx@1.0.15?foo=bar",
- "pkg:nginx/nginx@1.0.7",
- "pkg:nginx/nginx@1.14.1",
- "pkg:nginx/nginx@1.15.5",
- "pkg:nginx/nginx@1.15.6",
- "pkg:nginx/nginx@1.16.1",
- "pkg:nginx/nginx@1.17.2",
- "pkg:nginx/nginx@1.17.3",
- "pkg:nginx/nginx@1.20.0",
- "pkg:nginx/nginx@1.20.1",
- "pkg:nginx/nginx@1.21.0",
- "pkg:nginx/nginx@1.9.5",
- ]
- assert pkgs == expected
-
- def test_package_view_with_valid_purl_without_version(self):
- qs = PackageSearch().get_queryset(query="pkg:nginx/nginx")
- pkgs = list(qs)
- pkgs = [p.purl for p in pkgs]
- assert pkgs == [
- "pkg:nginx/nginx@0.6.18",
- "pkg:nginx/nginx@1.0.15",
- "pkg:nginx/nginx@1.0.15?foo=bar",
- "pkg:nginx/nginx@1.0.7",
- "pkg:nginx/nginx@1.14.1",
- "pkg:nginx/nginx@1.15.5",
- "pkg:nginx/nginx@1.15.6",
- "pkg:nginx/nginx@1.16.1",
- "pkg:nginx/nginx@1.17.2",
- "pkg:nginx/nginx@1.17.3",
- "pkg:nginx/nginx@1.20.0",
- "pkg:nginx/nginx@1.20.1",
- "pkg:nginx/nginx@1.21.0",
- "pkg:nginx/nginx@1.9.5",
- ]
-
- def test_package_view_with_valid_purl_and_incomplete_version(self):
- qs = PackageSearch().get_queryset(query="pkg:nginx/nginx@1")
- pkgs = list(qs)
- pkgs = [p.purl for p in pkgs]
- assert pkgs == [
- "pkg:nginx/nginx@1.0.15",
- "pkg:nginx/nginx@1.0.15?foo=bar",
- "pkg:nginx/nginx@1.0.7",
- "pkg:nginx/nginx@1.14.1",
- "pkg:nginx/nginx@1.15.5",
- "pkg:nginx/nginx@1.15.6",
- "pkg:nginx/nginx@1.16.1",
- "pkg:nginx/nginx@1.17.2",
- "pkg:nginx/nginx@1.17.3",
- "pkg:nginx/nginx@1.20.0",
- "pkg:nginx/nginx@1.20.1",
- "pkg:nginx/nginx@1.21.0",
- "pkg:nginx/nginx@1.9.5",
- ]
-
- def test_package_view_with_purl_type(self):
- qs = PackageSearch().get_queryset(query="pkg:pypi")
- pkgs = list(qs)
- pkgs = [p.purl for p in pkgs]
- assert pkgs == ["pkg:pypi/foo@1"]
-
- def test_package_view_with_type_as_input(self):
- qs = PackageSearch().get_queryset(query="pypi")
- pkgs = list(qs)
- pkgs = [p.purl for p in pkgs]
- assert pkgs == ["pkg:pypi/foo@1"]
-
-
-class VulnerabilitySearchTestCase(TestCase):
- def setUp(self):
- self.vulnerability = vulnerability = Vulnerability(summary="test")
- vulnerability.save()
- alias = Alias(alias="TEST-2022", vulnerability=vulnerability)
- alias.save()
- self.client = Client()
- session = self.client.session
- session["altcha_verified_at"] = time.time()
- session.save()
-
- def test_vulnerabilties_search_view_with_vcid_works_and_pk_does_not(self):
- response = self.client.get(f"/vulnerabilities/{self.vulnerability.pk}")
- self.assertEqual(response.status_code, 404)
- response = self.client.get(f"/vulnerabilities/{self.vulnerability.vulnerability_id}")
- self.assertEqual(response.status_code, 200)
-
- def test_vulnerabilties_search_view_with_empty(self):
- response = self.client.get(f"/vulnerabilities/search/")
- self.assertEqual(response.status_code, 200)
-
- def test_vulnerabilties_search_view_can_find_alias(self):
- response = self.client.get(f"/vulnerabilities/search/?search=TEST-2022")
- self.assertEqual(response.status_code, 200)
+# class PackageSearchTestCase(TestCase):
+# def setUp(self):
+# self.client = Client()
+# session = self.client.session
+# session["altcha_verified_at"] = time.time()
+# session.save()
+# packages = [
+# "pkg:nginx/nginx@0.6.18",
+# "pkg:nginx/nginx@1.20.0",
+# "pkg:nginx/nginx@1.21.0",
+# "pkg:nginx/nginx@1.20.1",
+# "pkg:nginx/nginx@1.9.5",
+# "pkg:nginx/nginx@1.17.2",
+# "pkg:nginx/nginx@1.17.3",
+# "pkg:nginx/nginx@1.16.1",
+# "pkg:nginx/nginx@1.15.5",
+# "pkg:nginx/nginx@1.15.6",
+# "pkg:nginx/nginx@1.14.1",
+# "pkg:nginx/nginx@1.0.7",
+# "pkg:nginx/nginx@1.0.15",
+# "pkg:nginx/nginx@1.0.15?foo=bar",
+# "pkg:pypi/foo@1",
+# ]
+# self.packages = packages
+# for package in packages:
+# purl = PackageURL.from_string(package)
+# attrs = {k: v for k, v in purl.to_dict().items() if v}
+# Package.objects.create(**attrs)
+
+# def test_packages_search_view_paginator(self):
+# response = self.client.get("/packages/search/?type=deb&name=&page=1")
+# self.assertEqual(response.status_code, 200)
+# response = self.client.get("/packages/search/?type=deb&name=&page=*")
+# self.assertEqual(response.status_code, 404)
+# response = self.client.get("/packages/search/?type=deb&name=&page=")
+# self.assertEqual(response.status_code, 200)
+# response = self.client.get("/packages/search/?type=&name=&page=")
+# self.assertEqual(response.status_code, 200)
+
+# def test_package_view(self):
+# qs = PackageSearch().get_queryset(query="pkg:nginx/nginx@1.0.15?foo=bar")
+# pkgs = list(qs)
+# self.assertEqual(len(pkgs), 2)
+# self.assertEqual(pkgs[0].purl, "pkg:nginx/nginx@1.0.15")
+
+# def test_package_detail_view(self):
+# package = PackageDetails(kwargs={"purl": "pkg:nginx/nginx@1.0.15"}).get_object()
+# assert package.purl == "pkg:nginx/nginx@1.0.15"
+
+# def test_package_view_with_purl_fragment(self):
+# qs = PackageSearch().get_queryset(query="nginx@1.0.15")
+# pkgs = list(qs)
+# self.assertEqual(len(pkgs), 2)
+# self.assertEqual(pkgs[0].purl, "pkg:nginx/nginx@1.0.15")
+# self.assertEqual(pkgs[1].purl, "pkg:nginx/nginx@1.0.15?foo=bar")
+
+# def test_package_view_with_purl_fragment_2(self):
+# qs = PackageSearch().get_queryset(query="nginx/nginx")
+# pkgs = list(qs)
+# pkgs = [p.purl for p in pkgs]
+# expected = [
+# "pkg:nginx/nginx@0.6.18",
+# "pkg:nginx/nginx@1.0.15",
+# "pkg:nginx/nginx@1.0.15?foo=bar",
+# "pkg:nginx/nginx@1.0.7",
+# "pkg:nginx/nginx@1.14.1",
+# "pkg:nginx/nginx@1.15.5",
+# "pkg:nginx/nginx@1.15.6",
+# "pkg:nginx/nginx@1.16.1",
+# "pkg:nginx/nginx@1.17.2",
+# "pkg:nginx/nginx@1.17.3",
+# "pkg:nginx/nginx@1.20.0",
+# "pkg:nginx/nginx@1.20.1",
+# "pkg:nginx/nginx@1.21.0",
+# "pkg:nginx/nginx@1.9.5",
+# ]
+# assert pkgs == expected
+
+# def test_package_view_with_valid_purl_without_version(self):
+# qs = PackageSearch().get_queryset(query="pkg:nginx/nginx")
+# pkgs = list(qs)
+# pkgs = [p.purl for p in pkgs]
+# assert pkgs == [
+# "pkg:nginx/nginx@0.6.18",
+# "pkg:nginx/nginx@1.0.15",
+# "pkg:nginx/nginx@1.0.15?foo=bar",
+# "pkg:nginx/nginx@1.0.7",
+# "pkg:nginx/nginx@1.14.1",
+# "pkg:nginx/nginx@1.15.5",
+# "pkg:nginx/nginx@1.15.6",
+# "pkg:nginx/nginx@1.16.1",
+# "pkg:nginx/nginx@1.17.2",
+# "pkg:nginx/nginx@1.17.3",
+# "pkg:nginx/nginx@1.20.0",
+# "pkg:nginx/nginx@1.20.1",
+# "pkg:nginx/nginx@1.21.0",
+# "pkg:nginx/nginx@1.9.5",
+# ]
+
+# def test_package_view_with_valid_purl_and_incomplete_version(self):
+# qs = PackageSearch().get_queryset(query="pkg:nginx/nginx@1")
+# pkgs = list(qs)
+# pkgs = [p.purl for p in pkgs]
+# assert pkgs == [
+# "pkg:nginx/nginx@1.0.15",
+# "pkg:nginx/nginx@1.0.15?foo=bar",
+# "pkg:nginx/nginx@1.0.7",
+# "pkg:nginx/nginx@1.14.1",
+# "pkg:nginx/nginx@1.15.5",
+# "pkg:nginx/nginx@1.15.6",
+# "pkg:nginx/nginx@1.16.1",
+# "pkg:nginx/nginx@1.17.2",
+# "pkg:nginx/nginx@1.17.3",
+# "pkg:nginx/nginx@1.20.0",
+# "pkg:nginx/nginx@1.20.1",
+# "pkg:nginx/nginx@1.21.0",
+# "pkg:nginx/nginx@1.9.5",
+# ]
+
+# def test_package_view_with_purl_type(self):
+# qs = PackageSearch().get_queryset(query="pkg:pypi")
+# pkgs = list(qs)
+# pkgs = [p.purl for p in pkgs]
+# assert pkgs == ["pkg:pypi/foo@1"]
+
+# def test_package_view_with_type_as_input(self):
+# qs = PackageSearch().get_queryset(query="pypi")
+# pkgs = list(qs)
+# pkgs = [p.purl for p in pkgs]
+# assert pkgs == ["pkg:pypi/foo@1"]
+
+
+# class VulnerabilitySearchTestCase(TestCase):
+# def setUp(self):
+# self.vulnerability = vulnerability = Vulnerability(summary="test")
+# vulnerability.save()
+# alias = Alias(alias="TEST-2022", vulnerability=vulnerability)
+# alias.save()
+# self.client = Client()
+# session = self.client.session
+# session["altcha_verified_at"] = time.time()
+# session.save()
+
+# def test_vulnerabilties_search_view_with_vcid_works_and_pk_does_not(self):
+# response = self.client.get(f"/vulnerabilities/{self.vulnerability.pk}")
+# self.assertEqual(response.status_code, 404)
+# response = self.client.get(f"/vulnerabilities/{self.vulnerability.vulnerability_id}")
+# self.assertEqual(response.status_code, 200)
+
+# def test_vulnerabilties_search_view_with_empty(self):
+# response = self.client.get(f"/vulnerabilities/search/")
+# self.assertEqual(response.status_code, 200)
+
+# def test_vulnerabilties_search_view_can_find_alias(self):
+# response = self.client.get(f"/vulnerabilities/search/?search=TEST-2022")
+# self.assertEqual(response.status_code, 200)
class CheckRobotsTxtTestCase(TestCase):
@@ -299,6 +300,8 @@ def setUp(self):
self.package2 = Package.objects.create(type="pypi", name="django", version="2.0.0")
self.package3 = Package.objects.create(type="pypi", name="django", version="3.0.0")
+ self.package4 = PackageV2.objects.create(type="pypi", name="django", version="1.0.0")
+
AffectedByPackageRelatedVulnerability.objects.create(
package=self.package1, vulnerability=self.vuln1
)
@@ -336,9 +339,9 @@ def setUp(self):
self.vuln1.save()
def test_aggregate_fixed_and_affected_packages(self):
- with self.assertNumQueries(12):
+ with self.assertNumQueries(11):
start_time = time.time()
- response = self.client.get(f"/vulnerabilities/{self.vuln1.vulnerability_id}")
+ response = self.client.get(f"/packages/v2/{self.package4.package_url}")
end_time = time.time()
# Increase time for ALTCHA verification
assert end_time - start_time < 0.06
@@ -369,7 +372,7 @@ def test_throttle_after_15_requests(self):
assert responses[15] == 429
- url = reverse("package_search")
+ url = reverse("package_search_v2")
response = self.client.get(
url,
diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py
index abebb4f1c..484608ac7 100644
--- a/vulnerabilities/views.py
+++ b/vulnerabilities/views.py
@@ -769,7 +769,7 @@ def get(self, request):
class HomePageV2(VulnerableCodeView):
- template_name = "index_v2.html"
+ template_name = "index.html"
def get(self, request):
request_query = request.GET
diff --git a/vulnerablecode/urls.py b/vulnerablecode/urls.py
index f8c97f8e2..07fffbae1 100644
--- a/vulnerablecode/urls.py
+++ b/vulnerablecode/urls.py
@@ -16,15 +16,6 @@
from drf_spectacular.views import SpectacularSwaggerView
from rest_framework.routers import DefaultRouter
-from vulnerabilities.api import AliasViewSet
-from vulnerabilities.api import CPEViewSet
-from vulnerabilities.api import PackageViewSet
-from vulnerabilities.api import VulnerabilityViewSet
-from vulnerabilities.api_v2 import CodeFixV2ViewSet
-from vulnerabilities.api_v2 import CodeFixViewSet
-from vulnerabilities.api_v2 import PackageV2ViewSet
-from vulnerabilities.api_v2 import PipelineScheduleV2ViewSet
-from vulnerabilities.api_v2 import VulnerabilityV2ViewSet
from vulnerabilities.api_v3 import AdvisoryV3ViewSet
from vulnerabilities.api_v3 import AffectedByAdvisoriesViewSet
from vulnerabilities.api_v3 import FixingAdvisoriesViewSet
@@ -40,18 +31,12 @@
from vulnerabilities.views import AltchaView
from vulnerabilities.views import ApiUserCreateView
from vulnerabilities.views import FixingAdvisoriesListView
-from vulnerabilities.views import HomePage
from vulnerabilities.views import HomePageV2
-from vulnerabilities.views import PackageDetails
-from vulnerabilities.views import PackageSearch
from vulnerabilities.views import PackageSearchV2
from vulnerabilities.views import PackageV2Details
from vulnerabilities.views import PipelineRunDetailView
from vulnerabilities.views import PipelineRunListView
from vulnerabilities.views import PipelineScheduleListView
-from vulnerabilities.views import VulnerabilityDetails
-from vulnerabilities.views import VulnerabilityPackagesDetails
-from vulnerabilities.views import VulnerabilitySearch
from vulnerablecode.settings import DEBUG
from vulnerablecode.settings import DEBUG_TOOLBAR
@@ -63,20 +48,6 @@ def __init__(self, *args, **kwargs):
self.trailing_slash = "/?"
-api_router = OptionalSlashRouter()
-api_router.register("packages", PackageViewSet)
-# `DefaultRouter` requires `basename` when registering viewsets that don't define a queryset.
-api_router.register("vulnerabilities", VulnerabilityViewSet, basename="vulnerability")
-api_router.register("cpes", CPEViewSet, basename="cpe")
-api_router.register("aliases", AliasViewSet, basename="alias")
-
-api_v2_router = OptionalSlashRouter()
-api_v2_router.register("packages", PackageV2ViewSet, basename="package-v2")
-api_v2_router.register("vulnerabilities", VulnerabilityV2ViewSet, basename="vulnerability-v2")
-api_v2_router.register("codefixes", CodeFixViewSet, basename="codefix")
-api_v2_router.register("pipelines", PipelineScheduleV2ViewSet, basename="pipelines")
-api_v2_router.register("advisory-codefixes", CodeFixV2ViewSet, basename="advisory-codefix")
-
api_v3_router = OptionalSlashRouter()
api_v3_router.register("packages", PackageV3ViewSet, basename="package-v3")
@@ -89,7 +60,6 @@ def __init__(self, *args, **kwargs):
urlpatterns = [
path("admin/login/", AdminLoginView.as_view(), name="admin-login"),
- path("api/v2/", include(api_v2_router.urls)),
path("api/v3/", include(api_v3_router.urls)),
path(
"robots.txt",
@@ -97,7 +67,7 @@ def __init__(self, *args, **kwargs):
),
path(
"",
- HomePage.as_view(),
+ HomePageV2.as_view(),
name="home",
),
path(
@@ -125,11 +95,6 @@ def __init__(self, *args, **kwargs):
PipelineRunDetailView.as_view(),
name="run-details",
),
- path(
- "v2",
- HomePageV2.as_view(),
- name="home",
- ),
path(
"advisories/packages/",
AdvisoryPackagesDetails.as_view(),
@@ -146,21 +111,11 @@ def __init__(self, *args, **kwargs):
name="advisory_details",
),
path("altcha/", AltchaView.as_view(), name="altcha"),
- path(
- "packages/search/",
- PackageSearch.as_view(),
- name="package_search",
- ),
path(
"packages/v2/search/",
PackageSearchV2.as_view(),
name="package_search_v2",
),
- re_path(
- r"^packages/(?Ppkg:.+)$",
- PackageDetails.as_view(),
- name="package_details",
- ),
re_path(
r"^packages/v2/(?Ppkg:.+)$",
PackageV2Details.as_view(),
@@ -176,26 +131,6 @@ def __init__(self, *args, **kwargs):
AffectedByAdvisoriesListView.as_view(),
name="affected_by_advisories_v2",
),
- path(
- "vulnerabilities/search/",
- VulnerabilitySearch.as_view(),
- name="vulnerability_search",
- ),
- path(
- "vulnerabilities/",
- VulnerabilityDetails.as_view(),
- name="vulnerability_details",
- ),
- path(
- "vulnerabilities//packages",
- VulnerabilityPackagesDetails.as_view(),
- name="vulnerability_package_details",
- ),
- path(
- "api/",
- include(api_router.urls),
- name="api",
- ),
path(
"api/schema/",
SpectacularAPIView.as_view(),