diff --git a/vulnerabilities/importer.py b/vulnerabilities/importer.py index 005534512..682901e83 100644 --- a/vulnerabilities/importer.py +++ b/vulnerabilities/importer.py @@ -223,18 +223,30 @@ def from_dict(cls, affected_pkg: dict): """ package = PackageURL(**affected_pkg["package"]) affected_version_range = None - if ( - affected_pkg["affected_version_range"] - and affected_pkg["affected_version_range"] != "None" - ): - affected_version_range = VersionRange.from_string( - affected_pkg["affected_version_range"] - ) + affected_range = affected_pkg["affected_version_range"] + + # TODO: "None" is a likely bug + if affected_range and affected_range != "None": + try: + affected_version_range = VersionRange.from_string(affected_range) + except: + tb = traceback.format_exc() + logger.error( + f"Cannot create AffectedPackage with invalid or unknown range: {affected_pkg!r} with error: {tb!r}" + ) + return + fixed_version = affected_pkg["fixed_version"] if fixed_version and affected_version_range: # TODO: revisit after https://github.com/nexB/univers/issues/10 fixed_version = affected_version_range.version_class(fixed_version) + if not fixed_version and not affected_version_range: + logger.error( + f"Cannot create AffectedPackage without fixed version or affected range: {affected_pkg!r}" + ) + return + return cls( package=package, affected_version_range=affected_version_range, @@ -295,7 +307,9 @@ def from_dict(cls, advisory_data): "aliases": advisory_data["aliases"], "summary": advisory_data["summary"], "affected_packages": [ - AffectedPackage.from_dict(pkg) for pkg in advisory_data["affected_packages"] + AffectedPackage.from_dict(pkg) + for pkg in advisory_data["affected_packages"] + if pkg is not None ], "references": [Reference.from_dict(ref) for ref in advisory_data["references"]], "date_published": datetime.datetime.fromisoformat(date_published) diff --git a/vulnerabilities/improvers/default.py b/vulnerabilities/improvers/default.py index 720a143e8..a2e49e9f2 100644 --- a/vulnerabilities/improvers/default.py +++ b/vulnerabilities/improvers/default.py @@ -105,6 +105,8 @@ def get_exact_purls(affected_package: AffectedPackage) -> Tuple[List[PackageURL] ... ) >>> assert expected == got """ + if not affected_package: + return [], [] try: vr = affected_package.affected_version_range diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index fc5eb5e3c..903443fa1 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -1137,7 +1137,9 @@ def to_advisory_data(self) -> "AdvisoryData": return AdvisoryData( aliases=self.aliases, summary=self.summary, - affected_packages=[AffectedPackage.from_dict(pkg) for pkg in self.affected_packages], + affected_packages=[ + AffectedPackage.from_dict(pkg) for pkg in self.affected_packages if pkg + ], references=[Reference.from_dict(ref) for ref in self.references], date_published=self.date_published, weaknesses=self.weaknesses, diff --git a/vulnerabilities/tests/test_default_improver.py b/vulnerabilities/tests/test_default_improver.py index 703e985a4..005a2d12c 100644 --- a/vulnerabilities/tests/test_default_improver.py +++ b/vulnerabilities/tests/test_default_improver.py @@ -17,6 +17,7 @@ from vulnerabilities.importer import Reference from vulnerabilities.improver import Inference from vulnerabilities.improvers.default import DefaultImprover +from vulnerabilities.improvers.default import get_exact_purls from vulnerabilities.tests import util_tests BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -136,3 +137,29 @@ def test_default_improver_with_nvd(): for data in list(default_improver.get_inferences(AdvisoryData.from_dict(advisory_data))) ] util_tests.check_results_against_json(result, expected_file) + + +def test_AffectedPackage_from_dict_should_not_crash_with_invalid_version_range(): + package = PackageURL( + type="rpm", + namespace="rpms", + name="python", + qualifiers={}, + subpath=None, + ) + + test_ranges = [ + # foo is a non-existing range + "vers:foo/1.2.3", + # apache was not supported and returned from vulnerabilities.importers.apache_httpd.ApacheHTTPDImporter + "vers:apache/", + None, + ] + for tr in test_ranges: + pkg = { + "package": package.to_dict(), + "affected_version_range": tr, + "fixed_version": None, + } + + assert AffectedPackage.from_dict(pkg) is None