From 200e41e69521b7f940ce6f61adab638581147d80 Mon Sep 17 00:00:00 2001 From: Alex Cameron Date: Fri, 26 Nov 2021 10:20:05 +1100 Subject: [PATCH 1/3] _dependency_source/pip: Log warning when attempting to audit a package with an invalid version --- pip_audit/_dependency_source/pip.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pip_audit/_dependency_source/pip.py b/pip_audit/_dependency_source/pip.py index 9acd388c..9a749f00 100644 --- a/pip_audit/_dependency_source/pip.py +++ b/pip_audit/_dependency_source/pip.py @@ -7,7 +7,7 @@ from typing import Iterator, Optional import pip_api -from packaging.version import Version +from packaging.version import InvalidVersion, Version from pip_audit._dependency_source import DependencySource, DependencySourceError from pip_audit._service import Dependency @@ -62,12 +62,18 @@ def collect(self) -> Iterator[Dependency]: # We collect them all into a single well-defined error. try: for (_, dist) in pip_api.installed_distributions(local=self._local).items(): - dep = Dependency(name=dist.name, version=Version(str(dist.version))) - if self.state is not None: - self.state.update_state( - f"Collecting {dep.name} ({dep.version})" - ) # pragma: no cover - yield dep + try: + dep = Dependency(name=dist.name, version=Version(str(dist.version))) + if self.state is not None: + self.state.update_state( + f"Collecting {dep.name} ({dep.version})" + ) # pragma: no cover + yield dep + except InvalidVersion: + logger.warning( + "Warning: Package has invalid version and could not be audited: " + f"{dist.name} ({dist.version})" + ) except Exception as e: raise PipSourceError("failed to list installed distributions") from e From 7727caad99afe8689d150fac61ca0cc9614e3849 Mon Sep 17 00:00:00 2001 From: Alex Cameron Date: Sun, 28 Nov 2021 22:55:07 +1100 Subject: [PATCH 2/3] test: Add unit test for versions that don't conform to PEP 440 --- test/dependency_source/test_pip.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/dependency_source/test_pip.py b/test/dependency_source/test_pip.py index f2f08b5f..ac0f067e 100644 --- a/test/dependency_source/test_pip.py +++ b/test/dependency_source/test_pip.py @@ -1,7 +1,10 @@ +from typing import Dict + import pip_api import pretend import pytest from packaging.version import Version +from pip_api._installed_distributions import Distribution import pip_audit from pip_audit._dependency_source import pip @@ -43,3 +46,25 @@ def explode(): with pytest.raises(pip.PipSourceError): list(source.collect()) + + +def test_pip_source_invalid_version(monkeypatch): + logger = pretend.stub(warning=pretend.call_recorder(lambda s: None)) + monkeypatch.setattr(pip, "logger", logger) + + source = pip.PipSource() + + def mock_installed_distributions(local: bool) -> Dict[str, Distribution]: + return { + "pytest": Distribution("pytest", "0.1"), + "pip-audit": Distribution("pip-audit", "1.0-ubuntu0.21.04.1"), + "pip-api": Distribution("pip-api", "1.0"), + } + + monkeypatch.setattr(pip_api, "installed_distributions", mock_installed_distributions) + + specs = list(source.collect()) + assert len(logger.warning.calls) == 1 + assert len(specs) == 2 + assert Dependency(name="pytest", version=Version("0.1")) in specs + assert Dependency(name="pip-api", version=Version("1.0")) in specs From 6034cbe43901326c7e113e3038d4ccdfab61113a Mon Sep 17 00:00:00 2001 From: Alex Cameron Date: Sun, 28 Nov 2021 23:17:23 +1100 Subject: [PATCH 3/3] test: Avoid constructing `LegacyVersion` objects --- test/dependency_source/test_pip.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/dependency_source/test_pip.py b/test/dependency_source/test_pip.py index ac0f067e..c6f1adcd 100644 --- a/test/dependency_source/test_pip.py +++ b/test/dependency_source/test_pip.py @@ -1,10 +1,10 @@ +from dataclasses import dataclass from typing import Dict import pip_api import pretend import pytest from packaging.version import Version -from pip_api._installed_distributions import Distribution import pip_audit from pip_audit._dependency_source import pip @@ -54,11 +54,18 @@ def test_pip_source_invalid_version(monkeypatch): source = pip.PipSource() - def mock_installed_distributions(local: bool) -> Dict[str, Distribution]: + @dataclass(frozen=True) + class MockDistribution: + name: str + version: str + + # Return a distribution with a version that doesn't conform to PEP 440. + # We should log a warning and skip it. + def mock_installed_distributions(local: bool) -> Dict[str, MockDistribution]: return { - "pytest": Distribution("pytest", "0.1"), - "pip-audit": Distribution("pip-audit", "1.0-ubuntu0.21.04.1"), - "pip-api": Distribution("pip-api", "1.0"), + "pytest": MockDistribution("pytest", "0.1"), + "pip-audit": MockDistribution("pip-audit", "1.0-ubuntu0.21.04.1"), + "pip-api": MockDistribution("pip-api", "1.0"), } monkeypatch.setattr(pip_api, "installed_distributions", mock_installed_distributions)