Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from dateutil import parser
from packaging import version
from packaging.version import InvalidVersion

from guarddog.analyzer.metadata.potentially_compromised_email_domain import (
PotentiallyCompromisedEmailDomainDetector,
Expand All @@ -16,6 +17,19 @@
from .utils import get_email_addresses


def _safe_version_key(v: str):
"""Sort key that tolerates legacy version strings (e.g. ``2004d``).

``packaging`` 22.0 removed ``LegacyVersion``, so ``version.parse`` raises
``InvalidVersion`` on non-PEP-440 strings. Return a minimum sentinel so
legacy versions sort as oldest rather than crashing the whole rule (#389).
"""
try:
return version.parse(v)
except InvalidVersion:
return version.parse("0")


class PypiPotentiallyCompromisedEmailDomainDetector(
PotentiallyCompromisedEmailDomainDetector
):
Expand All @@ -36,9 +50,7 @@ def get_project_latest_release_date(self, package_info) -> Optional[datetime]:
datetime: creation date of the most recent in releases
"""
releases = package_info["releases"]
sorted_versions = sorted(
releases.keys(), key=lambda r: version.parse(r), reverse=True
)
sorted_versions = sorted(releases.keys(), key=_safe_version_key, reverse=True)
earlier_versions = (
sorted_versions[:-1] if len(sorted_versions) > 1 else sorted_versions
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

from tests.analyzer.metadata.utils import MockWhoIs


with open(
os.path.join(pathlib.Path(__file__).parent.resolve(), "resources", "npm_data.json"),
"r",
Expand Down Expand Up @@ -110,3 +109,37 @@ def test_single_package_version(self):
pass # we expect no exception to be thrown
except Exception as e:
pytest.fail(f"Unexpected exception thrown: {e}")

def test_legacy_version_string(self):
"""
Regression test for https://github.com/DataDog/guarddog/issues/389

packaging >= 22.0 raises InvalidVersion on non-PEP-440 strings
(e.g. pytz's "2004d"), which used to crash the whole rule.
"""

def mock_whois(domain):
return MockWhoIs(datetime(1990, 1, 31))

MonkeyPatch().setattr("whois.whois", mock_whois)

current_info = deepcopy(PYPI_PACKAGE_INFO)
current_info["releases"] = {
"2004d": [
{
"upload_time": "2004-04-01T00:41:25",
"upload_time_iso_8601": "2004-04-01T00:41:25.953817Z",
}
],
"2023.3": [
{
"upload_time": "2023-03-06T00:41:25",
"upload_time_iso_8601": "2023-03-06T00:41:25.953817Z",
}
],
}
try:
pypi_detector.detect(current_info)
pass # we expect no exception to be thrown
except Exception as e:
pytest.fail(f"Unexpected exception thrown: {e}")
Loading