From a0359f8d3e4602f70ab107de40dcac8b82610ca0 Mon Sep 17 00:00:00 2001 From: Sampurna Pyne Date: Mon, 9 Feb 2026 00:23:33 +0530 Subject: [PATCH 1/2] Add OSSA Importer V2 Signed-off-by: Sampurna Pyne --- vulnerabilities/importers/__init__.py | 2 + .../v2_importers/ossa_importer_v2.py | 226 ++++ .../v2_importers/test_ossa_importer_v2.py | 43 + .../tests/test_data/ossa/expected.json | 967 ++++++++++++++++++ .../test_data/ossa/ossa/OSSA-2011-001.yaml | 58 ++ .../test_data/ossa/ossa/OSSA-2017-005.yaml | 54 + .../test_data/ossa/ossa/OSSA-2019-003.yaml | 60 ++ .../test_data/ossa/ossa/OSSA-2020-006.yaml | 63 ++ .../test_data/ossa/ossa/OSSA-2021-002.yaml | 77 ++ .../test_data/ossa/ossa/OSSA-2023-003.yaml | 217 ++++ .../test_data/ossa/ossa/OSSA-2024-001.yaml | 119 +++ .../test_data/ossa/ossa/OSSA-2025-002.yaml | 81 ++ .../test_data/ossa/ossa/OSSA-2026-001.yaml | 67 ++ 13 files changed, 2034 insertions(+) create mode 100644 vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py create mode 100644 vulnerabilities/tests/pipelines/v2_importers/test_ossa_importer_v2.py create mode 100644 vulnerabilities/tests/test_data/ossa/expected.json create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2011-001.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2017-005.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2019-003.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2020-006.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2021-002.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2023-003.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2024-001.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2025-002.yaml create mode 100644 vulnerabilities/tests/test_data/ossa/ossa/OSSA-2026-001.yaml diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index a0ef5fb44..b0f822163 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -65,6 +65,7 @@ from vulnerabilities.pipelines.v2_importers import nvd_importer as nvd_importer_v2 from vulnerabilities.pipelines.v2_importers import openssl_importer as openssl_importer_v2 from vulnerabilities.pipelines.v2_importers import oss_fuzz as oss_fuzz_v2 +from vulnerabilities.pipelines.v2_importers import ossa_importer_v2 from vulnerabilities.pipelines.v2_importers import postgresql_importer as postgresql_importer_v2 from vulnerabilities.pipelines.v2_importers import ( project_kb_msr2019_importer as project_kb_msr2019_importer_v2, @@ -110,6 +111,7 @@ nginx_importer_v2.NginxImporterPipeline, debian_importer_v2.DebianImporterPipeline, mattermost_importer_v2.MattermostImporterPipeline, + ossa_importer_v2.OSSAImporterPipeline, apache_tomcat_v2.ApacheTomcatImporterPipeline, retiredotnet_importer_v2.RetireDotnetImporterPipeline, ubuntu_osv_importer_v2.UbuntuOSVImporterPipeline, diff --git a/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py b/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py new file mode 100644 index 000000000..e84d1cba0 --- /dev/null +++ b/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py @@ -0,0 +1,226 @@ +# +# 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 re +from datetime import datetime +from pathlib import Path +from typing import Iterable +from typing import Tuple + +from dateutil import parser as dateparser +from fetchcode.vcs import fetch_via_vcs +from packageurl import PackageURL +from pytz import UTC +from univers.version_constraint import VersionConstraint +from univers.version_range import PypiVersionRange +from univers.versions import PypiVersion + +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackageV2 +from vulnerabilities.importer import ReferenceV2 +from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2 +from vulnerabilities.utils import load_yaml + + +class OSSAImporterPipeline(VulnerableCodeBaseImporterPipelineV2): + """OpenStack Security Advisory (OSSA) Importer Pipeline V2""" + + pipeline_id = "ossa_importer_v2" + spdx_license_expression = "CC-BY-3.0" + license_url = "https://github.com/openstack/ossa/blob/master/LICENSE" + repo_url = "git+https://github.com/openstack/ossa" + cutoff_years = 10 + + @classmethod + def steps(cls): + return ( + cls.clone, + cls.fetch, + cls.collect_and_store_advisories, + cls.clean_downloads, + ) + + def clone(self): + self.log(f"Cloning `{self.repo_url}`") + self.vcs_response = fetch_via_vcs(self.repo_url) + + def _get_cutoff_date(self): + current_date = datetime.now(UTC) + cutoff_year = current_date.year - self.cutoff_years + return current_date.replace(year=cutoff_year) + + def fetch(self): + ossa_dir = Path(self.vcs_response.dest_dir) / "ossa" + cutoff = self._get_cutoff_date() + self.processable_advisories = [] + skipped_old = 0 + + for file_path in sorted(ossa_dir.glob("OSSA-*.yaml")): + data = load_yaml(str(file_path)) + date_str = data.get("date") + + if date_str: + date_published = dateparser.parse(str(date_str)) + date_published = date_published.replace(tzinfo=UTC) + + if date_published < cutoff: + skipped_old += 1 + continue + + self.processable_advisories.append(file_path) + + if skipped_old > 0: + self.log(f"Skipped {skipped_old} advisories older than {self.cutoff_years} years") + self.log(f"Fetched {len(self.processable_advisories)} processable advisories") + + def advisories_count(self) -> int: + return len(self.processable_advisories) + + def collect_advisories(self) -> Iterable[AdvisoryData]: + for file_path in self.processable_advisories: + advisory = self.process_file(file_path) + yield advisory + + def process_file(self, file_path): + data = load_yaml(str(file_path)) + ossa_id = data.get("id") + + date_published = None + date_str = data.get("date") + date_published = dateparser.parse(str(date_str)) + date_published = date_published.replace(tzinfo=UTC) + + aliases = [] + for vulnerability in data.get("vulnerabilities"): + cve = vulnerability.get("cve-id", "") + aliases.append(cve) + + affected_packages = [] + for entry in data.get("affected-products"): + product = entry.get("product", "") + version = entry.get("version", "") + + if not product: + self.log(f"Missing affected-product: {ossa_id}") + continue + + for package_name, version_str in self.expand_products(product, version): + purl = PackageURL(type="pypi", name=package_name.lower()) + version_range = self.parse_version_range(version_str) + if purl and version_range: + affected_packages.append( + AffectedPackageV2(package=purl, affected_version_range=version_range) + ) + + references = [] + for link in (data.get("issues")).get("links"): + references.append(ReferenceV2(url=str(link))) + reviews = data.get("reviews") + for branch, links in reviews.items(): + # Skip metadata fields like 'type: gerrit'(https://github.com/openstack/ossa/blob/4461806fbad5fbc111b4993b2ab4d6b718ba85c8/ossa/OSSA-2019-004.yaml#L46) + if branch == "type": + continue + for link in links: + references.append(ReferenceV2(url=link)) + + title = data.get("title", "") + description = data.get("description", "") + summary = f"{title}\n\n{description}" + url = f"https://security.openstack.org/ossa/{ossa_id}.html" + return AdvisoryData( + advisory_id=ossa_id, + aliases=aliases, + summary=summary, + affected_packages=affected_packages, + references_v2=references, + date_published=date_published, + url=url, + ) + + def expand_products(self, product_str, version_str) -> Iterable[Tuple[str, str]]: + """ + OSSA advisories specifies affected products in different formats: + Format 1: + version="Cinder <1.0; Glance <2.0" + Format 2: + product="Cinder, Glance" + version="<1.0" + """ + # Format 1: "Cinder <1.0; Glance <2.0" + if ";" in version_str: + for segment in version_str.split(";"): + parts = segment.split(None, 1) + if len(parts) == 2: + yield parts[0], parts[1] + return + + # Format 2: product="Cinder, Glance" version="<1.0" + if "," in product_str: + for product in product_str.split(","): + if product: + yield product, version_str + return + + yield product_str, version_str + + def parse_version_range(self, version_str: str): + original_version_str = version_str + + if version_str.lower() == "all versions": + self.log(f"Skipping 'all versions' - cannot parse to specific range") + return None + + # Normalize "and" to comma + # "<=5.0.3, >=6.0.0 <=6.1.0 and ==7.0.0" -> "<=5.0.3, >=6.0.0 <=6.1.0, ==7.0.0" + version_str = version_str.lower().replace(" and ", ",") + + # Remove spaces around operators + # "<=5.0.3, >=6.0.0 <=6.1.0, ==7.0.0" -> "<=5.0.3,>=6.0.0<=6.1.0,==7.0.0" + version_str = re.sub(r"\s+([<>=!]+)", r"\1", version_str) + version_str = re.sub(r"([<>=!]+)\s+", r"\1", version_str) + + # Insert comma between consecutive constraints + # "<=5.0.3,>=6.0.0<=6.1.0,==7.0.0" -> "<=5.0.3,>=6.0.0,<=6.1.0,==7.0.0" + version_str = re.sub(r"(\d)([<>=!])", r"\1,\2", version_str) + + constraints = [] + for part in version_str.split(","): + comparator = None + version = part + + for op in ["==", "!=", "<=", ">=", "<", ">", "="]: + if part.startswith(op): + comparator = op + version = part[len(op) :].strip() + break + + # Default to "=" if no comparator is found + # "1.16.0" -> "=1.16.0" + if comparator is None: + comparator = "=" + # "==27.0.0" -> "=27.0.0" + if comparator == "==": + comparator = "=" + try: + constraints.append( + VersionConstraint(comparator=comparator, version=PypiVersion(version)) + ) + except ValueError as e: + self.log(f"Failed to parse version '{version}' from '{original_version_str}' : {e}") + continue + + return PypiVersionRange(constraints=constraints) if constraints else None + + def clean_downloads(self): + if self.vcs_response: + self.log("Removing cloned repository") + self.vcs_response.delete() + + def on_failure(self): + self.clean_downloads() diff --git a/vulnerabilities/tests/pipelines/v2_importers/test_ossa_importer_v2.py b/vulnerabilities/tests/pipelines/v2_importers/test_ossa_importer_v2.py new file mode 100644 index 000000000..6273f31f5 --- /dev/null +++ b/vulnerabilities/tests/pipelines/v2_importers/test_ossa_importer_v2.py @@ -0,0 +1,43 @@ +# +# 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 pathlib import Path +from unittest.mock import MagicMock +from unittest.mock import patch + +import pytest + +from vulnerabilities.pipelines.v2_importers.ossa_importer_v2 import OSSAImporterPipeline +from vulnerabilities.tests import util_tests + +TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "ossa" + + +@pytest.fixture +def mock_vcs_response(): + mock = MagicMock() + mock.dest_dir = str(TEST_DATA) + mock.delete = MagicMock() + return mock + + +@pytest.fixture +def mock_fetch_via_vcs(mock_vcs_response): + with patch("vulnerabilities.pipelines.v2_importers.ossa_importer_v2.fetch_via_vcs") as mock: + mock.return_value = mock_vcs_response + yield mock + + +def test_collect_advisories(mock_fetch_via_vcs): + pipeline = OSSAImporterPipeline() + pipeline.clone() + pipeline.fetch() + advisories = [adv.to_dict() for adv in pipeline.collect_advisories()] + expected_file = TEST_DATA / "expected.json" + util_tests.check_results_against_json(advisories, expected_file) diff --git a/vulnerabilities/tests/test_data/ossa/expected.json b/vulnerabilities/tests/test_data/ossa/expected.json new file mode 100644 index 000000000..256086a2f --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/expected.json @@ -0,0 +1,967 @@ +[ + { + "advisory_id": "OSSA-2017-005", + "aliases": [ + "CVE-2017-16239" + ], + "summary": "Nova Filter Scheduler bypass through rebuild action\n\nGeorge Shuklin from servers.com reported a vulnerability in Nova. By rebuilding an instance, an authenticated user may be able to circumvent the Filter Scheduler bypassing imposed filters (for example, the ImagePropertiesFilter or the IsolatedHostsFilter). All setups using Nova Filter Scheduler are affected.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<=14.0.10|>=15.0.0|<=15.0.8|>=16.0.0|<=16.0.3", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/1664931" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/519662" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/521186 (errata)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/519672" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/523212 (errata)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/519681" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/523427 (errata)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/519684" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/523434 (errata)" + } + ], + "patches": [], + "severities": [], + "date_published": "2017-11-14T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2017-005.html" + }, + { + "advisory_id": "OSSA-2019-003", + "aliases": [ + "CVE-2019-14433" + ], + "summary": "Nova Server Resource Faults Leak External Exception Details\n\nDonny Davis with Intel reported a vulnerability in Nova Compute resource fault handling. If an API request from an authenticated user ends in a fault condition due to an external exception, details of the underlying environment may be leaked in the response and could include sensitive configuration or other data.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<17.0.12|>=18.0.0|<18.2.2|>=19.0.0|<19.0.2", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/1837877" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674821" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674828" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674848" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674859" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674877" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.openstack.org/674908" + } + ], + "patches": [], + "severities": [], + "date_published": "2019-08-06T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2019-003.html" + }, + { + "advisory_id": "OSSA-2020-006", + "aliases": [ + "CVE-2020-17376" + ], + "summary": "Live migration fails to update persistent domain XML\n\nTadayoshi Hosoya (NEC) and Lee Yarwood (Red Hat) reported a vulnerability in Nova live migration. By performing a soft reboot of an instance which has previously undergone live migration, a user may gain access to destination host devices that share the same paths as host devices previously referenced by the virtual machine on the source. This can include block devices that map to different Cinder volumes on the destination than the source. The risk is increased significantly in non-default configurations allowing untrusted users to initiate live migrations, so administrators may consider temporarily disabling this in policy if they cannot upgrade immediately. This only impacts deployments where users are allowed to perform soft reboots of server instances; it is recommended to disable soft reboots in policy (only allowing hard reboots) until the fix can be applied.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<19.3.1|>=20.0.0|<20.3.1|21.0.0", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/1890501" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747969" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747972" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747973" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747974" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747975" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747976" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/747978" + } + ], + "patches": [], + "severities": [], + "date_published": "2020-08-25T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2020-006.html" + }, + { + "advisory_id": "OSSA-2021-002", + "aliases": [ + "CVE-2021-3654" + ], + "summary": "Open Redirect in noVNC proxy\n\nSwe Aung, Shahaan Ayyub, and Salman Khan with the Monash University Cyber Security team reported a vulnerability affecting Nova's noVNC proxying implementation which exposed access to a well-known redirect behavior in the Python standard library's http.server.SimpleHTTPRequestHandler and thus noVNC's WebSockifyRequestHandler which uses it. By convincing a user to follow a specially-crafted novncproxy URL, the user could be redirected to an unrelated site under control of the attacker in an attempt to convince them to divulge credentials or other sensitive data. All Nova deployments with novncproxy enabled are affected.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<21.2.3|>=22.0.0|<22.2.3|>=23.0.0|<23.0.3", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/1927677" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://bugs.python.org/issue32084" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/791297" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/805654 (errata 1)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/791577" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/805818 (errata 1)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/791805" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/806626 (errata 1)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/791806" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/806628 (errata 1)" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/791807" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/806629 (errata 1)" + } + ], + "patches": [], + "severities": [], + "date_published": "2021-07-29T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2021-002.html" + }, + { + "advisory_id": "OSSA-2023-003", + "aliases": [ + "CVE-2023-2088" + ], + "summary": "Unauthorized volume access through deleted volume attachments\n\nAn unauthorized access to a volume could occur when an iSCSI or FC\nconnection from a host is severed due to a volume being unmapped on\nthe storage system and the device is later reused for another volume\non the same host.\n\n**Scope:** Only deployments with iSCSI or FC volumes are affected.\nHowever, the fix for this issue includes a configuration change in\nNova and Cinder that may impact you on your next upgrade regardless\nof what backend storage technology you use. See the *Configuration\nchange* section below, and item 4(B) in the *Patches and Associated\nDeployment Changes* for details.\n\nThis data leak can be triggered by two different situations.\n\n**Accidental case:** If there is a problem with network connectivity\nduring a normal detach operation, OpenStack may fail to clean the\nsituation up properly. Instead of force-detaching the compute node\ndevice, Nova ignores the error, assuming the instance has already\nbeen deleted. Due to this incomplete operation OpenStack may end up\nselecting the wrong multipath device when connecting another volume\nto an instance.\n\n**Intentional case:** A regular user can create an instance with a\nvolume, and then delete the volume attachment directly in Cinder,\nwhich neglects to notify Nova. The compute node SCSI plumbing (over\niSCSI/FC) will continue trying to connect to the original\nhost/port/LUN, not knowing the attachment has been deleted. If a\nsubsequent volume attachment re-uses the host/port/LUN for a\ndifferent instance and volume, the original instance will gain\naccess to it once the SCSI plumbing reconnects.\n\nConfiguration Change\n--------------------\nTo prevent the intentional case, the Block Storage API provided by\nCinder must only accept attachment delete requests from Nova for\ninstance-attached volumes. A complicating factor is that Nova\ndeletes an attachment by making a call to the Block Storage API on\nbehalf of the user (that is, by passing the user's token), which\nmakes the request indistinguishable from the user making this\nrequest directly. The solution is to have Nova include a service\ntoken along with the user's token so that Cinder can determine that\nthe detach request is coming from Nova. The ability for Nova to pass\na service token has been supported since Ocata, but has not been\nrequired until now. Thus, deployments that are not currently sending\nservice user credentials from Nova will need to apply the relevant\ncode changes and also make configuration changes to solve the\nproblem.\n\nPatches and Associated Deployment Changes\n-----------------------------------------\nGiven the above analysis, a thorough fix must include the following\nelements:\n\n1. The os-brick library must implement the ``force`` option for\n fibre channel, which which has only been available for iSCSI\n until now (covered by the linked patches).\n\n2. Nova must call os-brick with the ``force`` option when\n disconnecting volumes from deleted instances (covered by the\n linked patches).\n\n3. In deployments where Glance uses the cinder glance_store driver,\n glance must call os-brick with the ``force`` option when\n disconnecting volumes (covered by the linked patches).\n\n4. Cinder must distinguish between safe and unsafe attachment delete\n requests and reject the unsafe ones. This part of the fix has two\n components:\n\n a. The Block Storage API will return a 409 (Conflict) for a\n request to delete an attachment if there is an instance\n currently using the attachment, **unless** the request is\n being made by a service (for example, Nova) on behalf of a\n user (covered by the linked patches).\n\n b. In order to recognize that a request is being made by a\n service on behalf of a user, Nova must be configured to send a\n service token along with the user token. If this configuration\n change is not made, the cinder change will reject **any**\n request to delete an attachment associated with a volume that\n is attached to an instance. Nova must be configured to send a\n service token to Cinder, and Cinder must be configured to\n accept service tokens. This is described in the following\n document and **IS NOT AUTOMATICALLY APPLIED BY THE LINKED\n PATCHES:** (Using service tokens to prevent long-running job\n failures)\n https://docs.openstack.org/cinder/latest/configuration/block-storage/service-token.html\n The Nova patch mentioned in step 2 includes a similar document\n more focused on Nova:\n doc/source/admin/configuration/service-user-token.rst\n\n5. The cinder glance_store driver does not attach volumes to\n instances; instead, it attaches volumes directly to the Glance\n node. Thus, the Cinder change in step 4 will recognize an\n attachment-delete request coming from Glance as safe and allow\n it. (Of course, we expect that you will have applied the patches\n in steps 1 and 3 to your Glance nodes.)", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "cinder", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<20.2.1|>=21.0.0|<21.2.1|22.0.0", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "pypi", + "namespace": "", + "name": "glance-store", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<3.0.1|>=4.0.0|<4.1.1|>=4.2.0|<4.3.1", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<25.1.2|>=26.0.0|<26.1.2|27.0.0", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "pypi", + "namespace": "", + "name": "os-brick", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<5.2.3|>=6.0.0|<6.1.1|>=6.2.0|<6.2.2", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/2004555" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882835" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882834" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882847" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882852" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882840" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882876" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882836" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882851" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882858" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882859" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882843" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882837" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882853" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882860" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882861" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882844" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882838" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882854" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882863" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882864" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882846" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882839" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882855" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882867" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882868" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882848" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882869" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/882870" + } + ], + "patches": [], + "severities": [], + "date_published": "2023-05-10T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2023-003.html" + }, + { + "advisory_id": "OSSA-2024-001", + "aliases": [ + "CVE-2024-32498" + ], + "summary": "Arbitrary file access through custom QCOW2 external data\n\nMartin Kaesberger reported a vulnerability in QCOW2 image processing for Cinder, Glance and Nova. By supplying a specially created QCOW2 image which references a specific data file path, an authenticated user may convince systems to return a copy of that file's contents from the server resulting in unauthorized access to potentially sensitive data. All Cinder deployments are affected; only Glance deployments with image conversion enabled are affected; all Nova deployments are affected.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "cinder", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<22.1.3|>=23.0.0|<23.1.1|24.0.0", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "pypi", + "namespace": "", + "name": "glance", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<26.0.1|27.0.0|>=28.0.0|<28.0.2", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "pypi", + "namespace": "", + "name": "nova", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<27.3.1|>=28.0.0|<28.1.1|>=29.0.0|<29.0.3", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/2059809" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923244" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923245" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923246" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923247" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923248" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923249" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923250" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923251" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923252" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923253" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923254" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923259" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923260" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923261" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923262" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923263" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923264" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923265" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923266" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923267" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923268" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923269" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923270" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923271" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923272" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923277" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923278" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923279" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923280" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923281" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923282" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923283" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923255" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923256" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923257" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923258" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923273" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923274" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923275" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923276" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923284" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923285" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923286" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923287" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923288" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923289" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923290" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/923281" + } + ], + "patches": [], + "severities": [], + "date_published": "2024-07-02T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2024-001.html" + }, + { + "advisory_id": "OSSA-2025-002", + "aliases": [ + "CVE-2025-65073" + ], + "summary": "Unauthenticated access to EC2/S3 token endpoints can grant Keystone authorization\n\nkay reported a vulnerability in Keystone\u2019s ec2tokens and s3tokens APIs. By sending those endpoints a valid AWS Signature (e.g., from a presigned S3 URL), an unauthenticated attacker may obtain Keystone authorization for the user associated with the signature (ec2tokens can yield a fully scoped token; s3tokens can reveal scope accepted by some services), resulting in unauthorized access and privilege escalation. Deployments where /v3/ec2tokens or /v3/s3tokens are reachable by unauthenticated clients (e.g., exposed on a public API) are affected.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "keystone", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/<26.0.1|27.0.0|28.0.0", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/2119646" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966069" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966070" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966071" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966073" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966871" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966062" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966063" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966064" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966067" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/966068" + } + ], + "patches": [], + "severities": [], + "date_published": "2025-11-04T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2025-002.html" + }, + { + "advisory_id": "OSSA-2026-001", + "aliases": [ + "CVE-2026-22797" + ], + "summary": "Privilege Escalation via Identity Headers in External OAuth2 Tokens\n\nGrzegorz Grasza with Red Hat reported a vulnerability in the external_oauth2_token middleware for keystonemiddleware. This middleware fails to sanitize incoming authentication headers before processing OAuth 2.0 tokens. By sending forged identity headers such as X-Is-Admin-Project, X-Roles, or X-User-Id, an authenticated attacker may escalate privileges or impersonate other users. All deployments using the external_oauth2_token middleware are affected.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "keystonemiddleware", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:pypi/>=10.5.0|<10.7.2|>=10.8.0|<10.9.1|>=10.10.0|<10.12.1", + "fixed_version_range": null, + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + } + ], + "references_v2": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://launchpad.net/bugs/2129018" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/973494" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/973495" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/973496" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/973497" + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://review.opendev.org/973499" + } + ], + "patches": [], + "severities": [], + "date_published": "2026-01-15T00:00:00+00:00", + "weaknesses": [], + "url": "https://security.openstack.org/ossa/OSSA-2026-001.html" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2011-001.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2011-001.yaml new file mode 100644 index 000000000..29df81bce --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2011-001.yaml @@ -0,0 +1,58 @@ +date: 2011-12-13 + +id: OSSA-2011-001 + +title: 'Path traversal issues registering malicious images using EC2 API' + +description: 'David Black reported two issues in OpenStack Nova''s support for EC2 + RegisterImage action. By registering images from malicious tarballs or manifests, + an attacker could potentially traverse directories and overwrite files with the + rights of the user Nova runs under. Only setups allowing the EC2 API and the S3/RegisterImage + method for registering images are affected.' + +reference: https://lists.launchpad.net/openstack/msg06105.html + +affected-products: + + - product: nova + version: All versions + +vulnerabilities: + + - cve-id: CVE-2011-4596 + impact-assessment: + source: 'Red Hat Product Security' + rating: moderate + assessment: + type: CVSS2 + score: 5.8 + detail: AV:N/AC:M/Au:N/C:P/I:P/A:N + classification: + source: 'Red Hat Product Security' + type: CWE + detail: CWE-20->CWE-22 + +reporters: + + - name: 'David Black' + affiliation: UNKNOWN + reported: + - CVE-2011-4596 + +issues: + + links: + - https://launchpad.net/bugs/885167 + - https://launchpad.net/bugs/894755 + + type: launchpad + +reviews: + + essex: + - https://review.openstack.org/#/c/2283 + + diablo: + - https://review.openstack.org/#/c/2284 + + type: gerrit \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2017-005.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2017-005.yaml new file mode 100644 index 000000000..778d2be00 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2017-005.yaml @@ -0,0 +1,54 @@ +date: 2017-11-14 + +id: OSSA-2017-005 + +title: Nova Filter Scheduler bypass through rebuild action + +description: > + George Shuklin from servers.com reported a vulnerability in Nova. By + rebuilding an instance, an authenticated user may be able to circumvent the + Filter Scheduler bypassing imposed filters (for example, the + ImagePropertiesFilter or the IsolatedHostsFilter). + All setups using Nova Filter Scheduler are affected. + +errata: > + The former fix introduced regressions in the rebuild functionality. + Rebuild may fail depending on configured scheduler filters and environment, + for example, when the compute host is running at capacity or when the host + is disabled. + This update provides an additional set of fixes for these regressions. + +affected-products: + - product: nova + version: "<=14.0.10, >=15.0.0 <=15.0.8, >=16.0.0 <=16.0.3" + +vulnerabilities: + - cve-id: CVE-2017-16239 + +reporters: + - name: George Shuklin + affiliation: Servers.com + reported: + - CVE-2017-16239 + +issues: + links: + - https://launchpad.net/bugs/1664931 + +reviews: + queens: + - https://review.openstack.org/519662 + - https://review.openstack.org/521186 (errata) + pike: + - https://review.openstack.org/519672 + - https://review.openstack.org/523212 (errata) + ocata: + - https://review.openstack.org/519681 + - https://review.openstack.org/523427 (errata) + newton: + - https://review.openstack.org/519684 + - https://review.openstack.org/523434 (errata) + +errata_history: + - 2017-12-05 - Errata 1 + - 2017-11-14 - Original Version \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2019-003.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2019-003.yaml new file mode 100644 index 000000000..9000ef660 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2019-003.yaml @@ -0,0 +1,60 @@ +date: 2019-08-06 + +id: OSSA-2019-003 + +title: Nova Server Resource Faults Leak External Exception Details + +description: > + Donny Davis with Intel reported a vulnerability in Nova Compute + resource fault handling. If an API request from an authenticated + user ends in a fault condition due to an external exception, details + of the underlying environment may be leaked in the response and + could include sensitive configuration or other data. + +affected-products: + + - product: Nova + version: '<17.0.12, >=18.0.0 <18.2.2, >=19.0.0 <19.0.2' + +vulnerabilities: + + - cve-id: CVE-2019-14433 + +reporters: + + - name: Donny Davis + affiliation: Intel + reported: + - CVE-2019-14433 + +issues: + + links: + - https://launchpad.net/bugs/1837877 + +reviews: + + train: + - https://review.openstack.org/674821 + + stein: + - https://review.openstack.org/674828 + + rocky: + - https://review.openstack.org/674848 + + queens: + - https://review.openstack.org/674859 + + pike: + - https://review.openstack.org/674877 + + ocata: + - https://review.openstack.org/674908 + + type: gerrit + +notes: + - The stable/ocata and stable/pike branches are under extended + maintenance and will receive no new point releases, but patches + for them are provided as a courtesy. \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2020-006.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2020-006.yaml new file mode 100644 index 000000000..b69fd67c7 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2020-006.yaml @@ -0,0 +1,63 @@ +date: 2020-08-25 + +id: OSSA-2020-006 + +title: Live migration fails to update persistent domain XML + +description: > + Tadayoshi Hosoya (NEC) and Lee Yarwood (Red Hat) reported a + vulnerability in Nova live migration. By performing a soft reboot of + an instance which has previously undergone live migration, a user + may gain access to destination host devices that share the same + paths as host devices previously referenced by the virtual machine + on the source. This can include block devices that map to different + Cinder volumes on the destination than the source. The risk is + increased significantly in non-default configurations allowing + untrusted users to initiate live migrations, so administrators may + consider temporarily disabling this in policy if they cannot upgrade + immediately. This only impacts deployments where users are allowed + to perform soft reboots of server instances; it is recommended to + disable soft reboots in policy (only allowing hard reboots) until + the fix can be applied. + +affected-products: + - product: Nova + version: '<19.3.1, >=20.0.0 <20.3.1, ==21.0.0' + +vulnerabilities: + - cve-id: CVE-2020-17376 + +reporters: + - name: Tadayoshi Hosoya + affiliation: NEC + reported: + - CVE-2020-17376 + - name: Lee Yarwood + affiliation: Red Hat + reported: + - CVE-2020-17376 + +issues: + links: + - https://launchpad.net/bugs/1890501 + +reviews: + victoria: + - https://review.opendev.org/747969 + ussuri: + - https://review.opendev.org/747972 + train: + - https://review.opendev.org/747973 + stein: + - https://review.opendev.org/747974 + rocky: + - https://review.opendev.org/747975 + queens: + - https://review.opendev.org/747976 + pike: + - https://review.opendev.org/747978 + +notes: + - The stable/rocky, stable/queens, and stable/pike branches are under + extended maintenance and will receive no new point releases, but patches + for them are provided as a courtesy. \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2021-002.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2021-002.yaml new file mode 100644 index 000000000..0013f3081 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2021-002.yaml @@ -0,0 +1,77 @@ +date: 2021-07-29 + +id: OSSA-2021-002 + +title: Open Redirect in noVNC proxy + +description: > + Swe Aung, Shahaan Ayyub, and Salman Khan with the Monash University Cyber + Security team reported a vulnerability affecting Nova's noVNC proxying + implementation which exposed access to a well-known redirect behavior in the + Python standard library's http.server.SimpleHTTPRequestHandler and thus + noVNC's WebSockifyRequestHandler which uses it. By convincing a user to + follow a specially-crafted novncproxy URL, the user could be redirected to an + unrelated site under control of the attacker in an attempt to convince them + to divulge credentials or other sensitive data. All Nova deployments with + novncproxy enabled are affected. + +errata: > + The initial fix did not take into account the possibility of bypass using + exactly three slashes. This update provides a more thorough revised fix for + the issue. The affected versions list has been updated to indicate versions + expected to include the newer solution. + +affected-products: + - product: Nova + version: '<21.2.3, >=22.0.0 <22.2.3, >=23.0.0 <23.0.3' + +vulnerabilities: + - cve-id: CVE-2021-3654 + +reporters: + - name: Swe Aung + affiliation: Monash University Cyber Security team + reported: + - CVE-2021-3654 + - name: Shahaan Ayyub + affiliation: Monash University Cyber Security team + reported: + - CVE-2021-3654 + - name: Salman Khan + affiliation: Monash University Cyber Security team + reported: + - CVE-2021-3654 + +issues: + links: + - https://launchpad.net/bugs/1927677 + - https://bugs.python.org/issue32084 + +reviews: + xena: + - https://review.opendev.org/791297 + - https://review.opendev.org/805654 (errata 1) + + wallaby: + - https://review.opendev.org/791577 + - https://review.opendev.org/805818 (errata 1) + + victoria: + - https://review.opendev.org/791805 + - https://review.opendev.org/806626 (errata 1) + + ussuri: + - https://review.opendev.org/791806 + - https://review.opendev.org/806628 (errata 1) + + train: + - https://review.opendev.org/791807 + - https://review.opendev.org/806629 (errata 1) + +notes: + - The stable/train branch is under extended maintenance and will receive no + new point releases, but a patch for it is provided as a courtesy. + +errata_history: + - 2021-09-27 - Errata 1 + - 2021-07-29 - Original Version \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2023-003.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2023-003.yaml new file mode 100644 index 000000000..93a47b96d --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2023-003.yaml @@ -0,0 +1,217 @@ +date: 2023-05-10 + +id: OSSA-2023-003 + +title: Unauthorized volume access through deleted volume attachments + +description: |+ + An unauthorized access to a volume could occur when an iSCSI or FC + connection from a host is severed due to a volume being unmapped on + the storage system and the device is later reused for another volume + on the same host. + + **Scope:** Only deployments with iSCSI or FC volumes are affected. + However, the fix for this issue includes a configuration change in + Nova and Cinder that may impact you on your next upgrade regardless + of what backend storage technology you use. See the *Configuration + change* section below, and item 4(B) in the *Patches and Associated + Deployment Changes* for details. + + This data leak can be triggered by two different situations. + + **Accidental case:** If there is a problem with network connectivity + during a normal detach operation, OpenStack may fail to clean the + situation up properly. Instead of force-detaching the compute node + device, Nova ignores the error, assuming the instance has already + been deleted. Due to this incomplete operation OpenStack may end up + selecting the wrong multipath device when connecting another volume + to an instance. + + **Intentional case:** A regular user can create an instance with a + volume, and then delete the volume attachment directly in Cinder, + which neglects to notify Nova. The compute node SCSI plumbing (over + iSCSI/FC) will continue trying to connect to the original + host/port/LUN, not knowing the attachment has been deleted. If a + subsequent volume attachment re-uses the host/port/LUN for a + different instance and volume, the original instance will gain + access to it once the SCSI plumbing reconnects. + + Configuration Change + -------------------- + To prevent the intentional case, the Block Storage API provided by + Cinder must only accept attachment delete requests from Nova for + instance-attached volumes. A complicating factor is that Nova + deletes an attachment by making a call to the Block Storage API on + behalf of the user (that is, by passing the user's token), which + makes the request indistinguishable from the user making this + request directly. The solution is to have Nova include a service + token along with the user's token so that Cinder can determine that + the detach request is coming from Nova. The ability for Nova to pass + a service token has been supported since Ocata, but has not been + required until now. Thus, deployments that are not currently sending + service user credentials from Nova will need to apply the relevant + code changes and also make configuration changes to solve the + problem. + + Patches and Associated Deployment Changes + ----------------------------------------- + Given the above analysis, a thorough fix must include the following + elements: + + 1. The os-brick library must implement the ``force`` option for + fibre channel, which which has only been available for iSCSI + until now (covered by the linked patches). + + 2. Nova must call os-brick with the ``force`` option when + disconnecting volumes from deleted instances (covered by the + linked patches). + + 3. In deployments where Glance uses the cinder glance_store driver, + glance must call os-brick with the ``force`` option when + disconnecting volumes (covered by the linked patches). + + 4. Cinder must distinguish between safe and unsafe attachment delete + requests and reject the unsafe ones. This part of the fix has two + components: + + a. The Block Storage API will return a 409 (Conflict) for a + request to delete an attachment if there is an instance + currently using the attachment, **unless** the request is + being made by a service (for example, Nova) on behalf of a + user (covered by the linked patches). + + b. In order to recognize that a request is being made by a + service on behalf of a user, Nova must be configured to send a + service token along with the user token. If this configuration + change is not made, the cinder change will reject **any** + request to delete an attachment associated with a volume that + is attached to an instance. Nova must be configured to send a + service token to Cinder, and Cinder must be configured to + accept service tokens. This is described in the following + document and **IS NOT AUTOMATICALLY APPLIED BY THE LINKED + PATCHES:** (Using service tokens to prevent long-running job + failures) + https://docs.openstack.org/cinder/latest/configuration/block-storage/service-token.html + The Nova patch mentioned in step 2 includes a similar document + more focused on Nova: + doc/source/admin/configuration/service-user-token.rst + + 5. The cinder glance_store driver does not attach volumes to + instances; instead, it attaches volumes directly to the Glance + node. Thus, the Cinder change in step 4 will recognize an + attachment-delete request coming from Glance as safe and allow + it. (Of course, we expect that you will have applied the patches + in steps 1 and 3 to your Glance nodes.) + +errata: > + An additional nova patch is required to fix a minor regression in + periodic tasks and some nova-manage actions (errata 1). Also a patch + to tempest is needed to account for behavior changes with fixes in + place (errata 2). The stable/wallaby branch fix for nova introduced + a regression which was addressed through subsequent adjustment of + its patches after the advisory was published (errata 3). + +affected-products: + - product: cinder + version: '<20.2.1, >=21.0.0 <21.2.1, ==22.0.0' + - product: glance_store + version: '<3.0.1, >=4.0.0 <4.1.1, >=4.2.0 <4.3.1' + - product: nova + version: '<25.1.2, >=26.0.0 <26.1.2, ==27.0.0' + - product: os-brick + version: '<5.2.3, >=6.0.0 <6.1.1, >=6.2.0 <6.2.2' + +vulnerabilities: + - cve-id: CVE-2023-2088 + +reporters: + - name: Jan Wasilewski + affiliation: Atman + reported: + - CVE-2023-2088 + - name: Gorka Eguileor + affiliation: Red Hat + reported: + - CVE-2023-2088 + +issues: + links: + - https://launchpad.net/bugs/2004555 + +reviews: + 2023.2/bobcat cinder: + - https://review.opendev.org/882835 + 2023.2/bobcat glance_store: + - https://review.opendev.org/882834 + 2023.2/bobcat nova: + - https://review.opendev.org/882847 + 2023.2/bobcat nova errata 1: + - https://review.opendev.org/882852 + 2023.2/bobcat os-brick: + - https://review.opendev.org/882840 + 2023.2/bobcat tempest errata 2: + - https://review.opendev.org/882876 + 2023.1/antelope cinder: + - https://review.opendev.org/882836 + 2023.1/antelope glance_store: + - https://review.opendev.org/882851 + 2023.1/antelope nova: + - https://review.opendev.org/882858 + 2023.1/antelope nova errata 1: + - https://review.opendev.org/882859 + 2023.1/antelope os-brick: + - https://review.opendev.org/882843 + zed cinder: + - https://review.opendev.org/882837 + zed glance_store: + - https://review.opendev.org/882853 + zed nova: + - https://review.opendev.org/882860 + zed nova errata 1: + - https://review.opendev.org/882861 + zed os-brick: + - https://review.opendev.org/882844 + yoga cinder: + - https://review.opendev.org/882838 + yoga glance_store: + - https://review.opendev.org/882854 + yoga nova: + - https://review.opendev.org/882863 + yoga nova errata 1: + - https://review.opendev.org/882864 + yoga os-brick: + - https://review.opendev.org/882846 + xena cinder: + - https://review.opendev.org/882839 + xena glance_store: + - https://review.opendev.org/882855 + xena nova: + - https://review.opendev.org/882867 + xena nova errata 1: + - https://review.opendev.org/882868 + xena os-brick: + - https://review.opendev.org/882848 + wallaby nova: + - https://review.opendev.org/882869 + wallaby nova errata 1: + - https://review.opendev.org/882870 + +notes: + - Limited Protection Against Accidents... If you are only concerned with + protecting against the accidental case described earlier in this document, + steps 1-3 above should be sufficient. Note, however, that only applying + steps 1-3 leaves your cloud wide open to the intentional exploitation of + this vulnerability. Therefore, we recommend that the full fix be applied to + all deployments. + - Using Configuration as a Short-Term Mitigation... An alternative approach + to mitigation can be found in OSSN-0092 + https://wiki.openstack.org/wiki/OSSN/OSSN-0092 + - The stable/xena and stable/wallaby branches are under extended maintenance + and will receive no new point releases, but patches for them are provided + as a courtesy where available. + +errata_history: + - 2023-05-15 - Errata 3 + - 2023-05-10 - Errata 2 + - 2023-05-10 - Errata 1 + - 2023-05-10 - Original Version \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2024-001.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2024-001.yaml new file mode 100644 index 000000000..b83dc4275 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2024-001.yaml @@ -0,0 +1,119 @@ +date: 2024-07-02 + +id: OSSA-2024-001 + +title: Arbitrary file access through custom QCOW2 external data + +description: > + Martin Kaesberger reported a vulnerability in QCOW2 image processing for + Cinder, Glance and Nova. By supplying a specially created QCOW2 image which + references a specific data file path, an authenticated user may convince + systems to return a copy of that file's contents from the server resulting in + unauthorized access to potentially sensitive data. All Cinder deployments are + affected; only Glance deployments with image conversion enabled are affected; + all Nova deployments are affected. + +affected-products: + - product: Cinder + version: '<22.1.3, >=23.0.0 <23.1.1, ==24.0.0' + - product: Glance + version: '<26.0.1, ==27.0.0, >=28.0.0 <28.0.2' + - product: Nova + version: '<27.3.1, >=28.0.0 <28.1.1, >=29.0.0 <29.0.3' + +vulnerabilities: + - cve-id: CVE-2024-32498 + +reporters: + - name: Martin Kaesberger + affiliation: none + reported: + - CVE-2024-32498 + +issues: + links: + - https://launchpad.net/bugs/2059809 + +reviews: + 2024.2/dalmatian(cinder): + - https://review.opendev.org/923244 + + 2024.1/caracal(cinder): + - https://review.opendev.org/923245 + + 2023.2/bobcat(cinder): + - https://review.opendev.org/923246 + + 2023.1/antelope(cinder): + - https://review.opendev.org/923247 + + 2024.2/dalmatian(glance): + - https://review.opendev.org/923248 + - https://review.opendev.org/923249 + - https://review.opendev.org/923250 + - https://review.opendev.org/923251 + - https://review.opendev.org/923252 + - https://review.opendev.org/923253 + - https://review.opendev.org/923254 + + 2024.1/caracal(glance): + - https://review.opendev.org/923259 + - https://review.opendev.org/923260 + - https://review.opendev.org/923261 + - https://review.opendev.org/923262 + - https://review.opendev.org/923263 + - https://review.opendev.org/923264 + - https://review.opendev.org/923265 + + 2023.2/bobcat(glance): + - https://review.opendev.org/923266 + - https://review.opendev.org/923267 + - https://review.opendev.org/923268 + - https://review.opendev.org/923269 + - https://review.opendev.org/923270 + - https://review.opendev.org/923271 + - https://review.opendev.org/923272 + + 2023.1/antelope(glance): + - https://review.opendev.org/923277 + - https://review.opendev.org/923278 + - https://review.opendev.org/923279 + - https://review.opendev.org/923280 + - https://review.opendev.org/923281 + - https://review.opendev.org/923282 + - https://review.opendev.org/923283 + + 2024.2/dalmatian(nova): + - https://review.opendev.org/923255 + - https://review.opendev.org/923256 + - https://review.opendev.org/923257 + - https://review.opendev.org/923258 + + 2024.1/caracal(nova): + - https://review.opendev.org/923273 + - https://review.opendev.org/923274 + - https://review.opendev.org/923275 + - https://review.opendev.org/923276 + + 2023.2/bobcat(nova): + - https://review.opendev.org/923284 + - https://review.opendev.org/923285 + - https://review.opendev.org/923286 + - https://review.opendev.org/923287 + + 2023.1/antelope(nova): + - https://review.opendev.org/923288 + - https://review.opendev.org/923289 + - https://review.opendev.org/923290 + - https://review.opendev.org/923281 + +notes: + - Due to the scope of the problem and complexity of the resulting fixes, + regressions and additional bypasses were reported in the original bug by + downstream stakeholders during the coordinated disclosure period. As a + result, our initially chosen publication date was rescheduled, which put + the advisory four days past our promised ninety day maximum embargo length. + Additional revised patches and regression fixes were supplied to + stakeholders as soon as possible, but we understand the unfortunate timing + of these last-minute changes resulted in a lot of additional work for + everyone involved. \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2025-002.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2025-002.yaml new file mode 100644 index 000000000..0c6dfffb9 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2025-002.yaml @@ -0,0 +1,81 @@ +date: 2025-11-04 + +id: OSSA-2025-002 + +title: Unauthenticated access to EC2/S3 token endpoints can grant Keystone authorization + +description: > + kay reported a vulnerability in Keystone’s ec2tokens and s3tokens APIs. By + sending those endpoints a valid AWS Signature (e.g., from a presigned S3 + URL), an unauthenticated attacker may obtain Keystone authorization + for the user associated with the signature + (ec2tokens can yield a fully scoped token; s3tokens can reveal scope accepted + by some services), resulting in unauthorized access and privilege escalation. + Deployments where /v3/ec2tokens or /v3/s3tokens are reachable by + unauthenticated clients (e.g., exposed on a public API) are affected. + +errata: > + CVE-2025-65073 was assigned by MITRE after publication based on a request + submitted 2025-09-24 (months prior); if any other CNA has assigned a CVE + themselves in the meantime, please reject it so that we don't end up with + duplicates. Further, the description has been extended to clarify token + ownership. Backported fixes for the unmaintained/2024.1 branches are now + included. + +affected-products: + - product: Keystone + version: '<26.0.1, ==27.0.0, ==28.0.0' + +vulnerabilities: + - cve-id: CVE-2025-65073 + +reporters: + - name: kay + reported: + - CVE-2025-65073 + +issues: + links: + - https://launchpad.net/bugs/2119646 + +reviews: + 2026.1/gazpacho(keystone): + - https://review.opendev.org/966069 + + 2025.2/flamingo(keystone): + - https://review.opendev.org/966070 + + 2025.1/epoxy(keystone): + - https://review.opendev.org/966071 + + 2024.2/dalmatian(keystone): + - https://review.opendev.org/966073 + + 2024.1/caracal(keystone): + - https://review.opendev.org/966871 + + 2026.1/gazpacho(swift): + - https://review.opendev.org/966062 + + 2025.2/flamingo(swift): + - https://review.opendev.org/966063 + + 2025.1/epoxy(swift): + - https://review.opendev.org/966064 + + 2024.2/dalmatian(swift): + - https://review.opendev.org/966067 + + 2024.1/caracal(swift): + - https://review.opendev.org/966068 + +notes: + - While the indicated Keystone patches are sufficient to mitigate this + vulnerability, corresponding changes for Swift are included which keep its + optional S3-like API working. + - The unmaintained/2024.1 branches will receive no new point releases, but + patches for them are provided as a courtesy. + +errata_history: + - 2025-11-17 - Errata 1 + - 2025-11-04 - Original Version \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2026-001.yaml b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2026-001.yaml new file mode 100644 index 000000000..db1a14ab4 --- /dev/null +++ b/vulnerabilities/tests/test_data/ossa/ossa/OSSA-2026-001.yaml @@ -0,0 +1,67 @@ +date: 2026-01-15 + +id: OSSA-2026-001 + +title: Privilege Escalation via Identity Headers in External OAuth2 Tokens + +description: > + Grzegorz Grasza with Red Hat reported a vulnerability in the + external_oauth2_token middleware for keystonemiddleware. This middleware + fails to sanitize incoming authentication headers before processing OAuth 2.0 + tokens. By sending forged identity headers such as X-Is-Admin-Project, + X-Roles, or X-User-Id, an authenticated attacker may escalate privileges or + impersonate other users. All deployments using the external_oauth2_token + middleware are affected. + +errata: > + The original advisory listed versions >=10.0.0 as affected based on incorrect + data, the code in question was not added until 10.5.0. + +affected-products: + - product: keystonemiddleware + version: '>=10.5.0 <10.7.2, >=10.8.0 <10.9.1, >=10.10.0 <10.12.1' + +vulnerabilities: + - cve-id: CVE-2026-22797 + +reporters: + - name: Grzegorz Grasza + affiliation: Red Hat + reported: + - CVE-2026-22797 + +issues: + links: + - https://launchpad.net/bugs/2129018 + +reviews: + 2026.1/gazpacho: + - https://review.opendev.org/973494 + + 2025.2/flamingo: + - https://review.opendev.org/973495 + + 2025.1/epoxy: + - https://review.opendev.org/973496 + + 2024.2/dalmatian: + - https://review.opendev.org/973497 + + 2024.1/caracal: + - https://review.opendev.org/973499 + +notes: + - The unmaintained/2024.1 branches will receive no new point releases, but + patches for them are provided as a courtesy. + - This bug was possible because the middleware only conditionally set certain + headers (e.g., X-Is-Admin-Project was only set when the token had admin + privileges), leaving spoofed values intact when conditions were not met. + - The fix adds a call to remove_auth_headers() at the start of request + processing to sanitize all incoming identity headers, matching the behavior + of the main auth_token middleware. + - The affected code was introduced in keystonemiddleware 10.5.0 during the + OpenStack 2024.1 (Caracal) development cycle. + +errata_history: + - 2026-01-16 - Errata 1 + - 2026-01-15 - Original Version \ No newline at end of file From 4111f98138b42fde288c37411978441977ac7928 Mon Sep 17 00:00:00 2001 From: Sampurna Pyne Date: Thu, 12 Feb 2026 23:19:19 +0530 Subject: [PATCH 2/2] Refactor cutoff_year and improve documentation Signed-off-by: Sampurna Pyne --- .../v2_importers/ossa_importer_v2.py | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py b/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py index e84d1cba0..bd75be084 100644 --- a/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py +++ b/vulnerabilities/pipelines/v2_importers/ossa_importer_v2.py @@ -8,7 +8,6 @@ # import re -from datetime import datetime from pathlib import Path from typing import Iterable from typing import Tuple @@ -35,7 +34,9 @@ class OSSAImporterPipeline(VulnerableCodeBaseImporterPipelineV2): spdx_license_expression = "CC-BY-3.0" license_url = "https://github.com/openstack/ossa/blob/master/LICENSE" repo_url = "git+https://github.com/openstack/ossa" - cutoff_years = 10 + + # Advisories published before this year are not consumed due to inconsistent schema and irrelevance + cutoff_year = 2016 @classmethod def steps(cls): @@ -50,33 +51,24 @@ def clone(self): self.log(f"Cloning `{self.repo_url}`") self.vcs_response = fetch_via_vcs(self.repo_url) - def _get_cutoff_date(self): - current_date = datetime.now(UTC) - cutoff_year = current_date.year - self.cutoff_years - return current_date.replace(year=cutoff_year) - def fetch(self): ossa_dir = Path(self.vcs_response.dest_dir) / "ossa" - cutoff = self._get_cutoff_date() self.processable_advisories = [] skipped_old = 0 for file_path in sorted(ossa_dir.glob("OSSA-*.yaml")): data = load_yaml(str(file_path)) - date_str = data.get("date") - if date_str: - date_published = dateparser.parse(str(date_str)) - date_published = date_published.replace(tzinfo=UTC) - - if date_published < cutoff: - skipped_old += 1 - continue + date_str = data.get("date") + date_published = dateparser.parse(str(date_str)).replace(tzinfo=UTC) + if date_published.year < self.cutoff_year: + skipped_old += 1 + continue self.processable_advisories.append(file_path) if skipped_old > 0: - self.log(f"Skipped {skipped_old} advisories older than {self.cutoff_years} years") + self.log(f"Skipped {skipped_old} advisories older than {self.cutoff_year}") self.log(f"Fetched {len(self.processable_advisories)} processable advisories") def advisories_count(self) -> int: @@ -87,31 +79,26 @@ def collect_advisories(self) -> Iterable[AdvisoryData]: advisory = self.process_file(file_path) yield advisory - def process_file(self, file_path): + def process_file(self, file_path) -> AdvisoryData: + """Parse a single OSSA YAML file and extract advisory data""" data = load_yaml(str(file_path)) ossa_id = data.get("id") - date_published = None date_str = data.get("date") - date_published = dateparser.parse(str(date_str)) - date_published = date_published.replace(tzinfo=UTC) + date_published = dateparser.parse(str(date_str)).replace(tzinfo=UTC) aliases = [] for vulnerability in data.get("vulnerabilities"): - cve = vulnerability.get("cve-id", "") + cve = vulnerability.get("cve-id") aliases.append(cve) affected_packages = [] for entry in data.get("affected-products"): - product = entry.get("product", "") - version = entry.get("version", "") - - if not product: - self.log(f"Missing affected-product: {ossa_id}") - continue + product = entry.get("product") + version = entry.get("version") for package_name, version_str in self.expand_products(product, version): - purl = PackageURL(type="pypi", name=package_name.lower()) + purl = PackageURL(type="pypi", name=package_name) version_range = self.parse_version_range(version_str) if purl and version_range: affected_packages.append( @@ -129,8 +116,8 @@ def process_file(self, file_path): for link in links: references.append(ReferenceV2(url=link)) - title = data.get("title", "") - description = data.get("description", "") + title = data.get("title") + description = data.get("description") summary = f"{title}\n\n{description}" url = f"https://security.openstack.org/ossa/{ossa_id}.html" return AdvisoryData( @@ -151,6 +138,7 @@ def expand_products(self, product_str, version_str) -> Iterable[Tuple[str, str]] Format 2: product="Cinder, Glance" version="<1.0" + This function handles both formats and yields tuples of (product, version) for each affected product. """ # Format 1: "Cinder <1.0; Glance <2.0" if ";" in version_str: @@ -169,7 +157,10 @@ def expand_products(self, product_str, version_str) -> Iterable[Tuple[str, str]] yield product_str, version_str - def parse_version_range(self, version_str: str): + def parse_version_range(self, version_str: str) -> PypiVersionRange: + """ + Normalizes the version string and extracts individual constraints to create a PypiVersionRange object. + """ original_version_str = version_str if version_str.lower() == "all versions":