Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle invalid version input in ecosystem sort_key #2515

Merged
merged 2 commits into from
Aug 23, 2024
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
4 changes: 4 additions & 0 deletions osv/ecosystems/alpine.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ def get_branch_name(self) -> str:
return self.alpine_release_ver.lstrip('v') + self._BRANCH_SUFFIX

def sort_key(self, version):
if not AlpineLinuxVersion.is_valid(version):
# If version is not valid, it is most likely an invalid input
# version then sort it to the last/largest element
return AlpineLinuxVersion('999999')
return AlpineLinuxVersion(version)

@staticmethod
Expand Down
4 changes: 4 additions & 0 deletions osv/ecosystems/alpine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,8 @@ def test_alpine(self, ensure_updated_checkout_mock: mock.MagicMock):
self.assertGreater(
ecosystem.sort_key('1.13.2-r0'), ecosystem.sort_key('1.13.2_alpha'))

# Check invalid version handle
self.assertGreater(
ecosystem.sort_key('1-0-0'), ecosystem.sort_key('1.13.2-r0'))

ecosystems.config.set_cache(None)
4 changes: 4 additions & 0 deletions osv/ecosystems/bioconductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def get_bioc_versions(self):

def sort_key(self, version):
"""Sort key."""
if not semver_index.is_valid(version):
# If version is not valid, it is most likely an invalid input
# version then sort it to the last/largest element
return semver_index.parse('999999')
return semver_index.parse(version)

def _enumerate_versions(self,
Expand Down
1 change: 1 addition & 0 deletions osv/ecosystems/bioconductor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ def test_next_version(self):
ecosystem = ecosystems.get('Bioconductor')
self.assertEqual('1.18.0', ecosystem.next_version('a4', '1.16.0'))
self.assertEqual('1.20.0', ecosystem.next_version('a4', '1.18.0'))
self.assertGreater(ecosystem.sort_key('1-0'), ecosystem.sort_key('1.2.0'))
with self.assertRaises(ecosystems.EnumerateError):
ecosystem.next_version('doesnotexist123456', '1')
1 change: 1 addition & 0 deletions osv/ecosystems/cran.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def sort_key(self, version):
# The packaging.version appears to work for the typical X.Y.Z and
# X.Y-Z cases
version = version.replace("-", ".")
# version.parse() handles invalid versions by returning LegacyVersion()
return packaging.version.parse(version)

def _enumerate_versions(self,
Expand Down
7 changes: 6 additions & 1 deletion osv/ecosystems/haskell.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ def sort_key(self, version):
https://hackage.haskell.org/package/Cabal-syntax/docs/Distribution-Types-Version.html

"""
return [int(x) for x in version.split('.')]
# If version is not valid, it is most likely an invalid input version
# then sort it to the last/largest element
try:
return [int(x) for x in version.split('.')]
except ValueError:
return [999999]

def enumerate_versions(self,
package,
Expand Down
6 changes: 6 additions & 0 deletions osv/ecosystems/haskell_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def test_next_version(self):
with self.assertRaises(ecosystems.EnumerateError):
ecosystem.next_version('doesnotexist123456', '1')

def test_sort_key(self):
"""Test sort_key."""
ecosystem = ecosystems.get('Hackage')
self.assertGreater(
ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0'))


class GHCEcosystemTest(unittest.TestCase):
"""GHC ecosystem helper tests."""
Expand Down
37 changes: 8 additions & 29 deletions osv/ecosystems/maven_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,42 +80,21 @@ def test_sort(self):
"""Basic sort tests."""
# These tests are taken from the spec.
unsorted = [
'1',
'1.1',
'1-snapshot',
'1',
'1-sp',
'1-foo2',
'1-foo10',
'1.foo',
'1-foo',
'1-1',
'1.1',
'1.ga',
'1-ga',
'1-0',
'1.0',
'1',
'1-sp',
'1-ga',
'1-sp.1',
'1-ga.1',
'1-sp-1',
'1-ga-1',
'1-1',
'1-a1',
'1-alpha-1',
'2',
'1', '1.1', '1-snapshot', '1', '1-sp', '1-foo2', '1-foo10', '1.foo',
'1-foo', '1-1', '1.1', '1.ga', '1-ga', '1-0', '1.0', '1', '1-sp',
'1-ga', '1-sp.1', '1-ga.1', '1-sp-1', '1-ga-1', '1-1', '1-a1',
'1-alpha-1', '2', 'invalid', '0'
]

sorted_versions = [
str(v) for v in sorted(maven.Version.from_string(v) for v in unsorted)
]

self.assertListEqual([
'1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1', '1', '1', '1',
'1', '1', '1.foo', '1-.1', '1-sp', '1-sp', '1-sp-1', '1-sp.1', '1-foo',
'1-foo-2', '1-foo-10', '1-1', '1-1', '1-1', '1.1', '1.1', '2'
'invalid', '0', '1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1',
'1', '1', '1', '1', '1', '1.foo', '1-.1', '1-sp', '1-sp', '1-sp-1',
'1-sp.1', '1-foo', '1-foo-2', '1-foo-10', '1-1', '1-1', '1-1', '1.1',
'1.1', '2'
], sorted_versions)

def test_versions_qualifiers(self):
Expand Down
4 changes: 4 additions & 0 deletions osv/ecosystems/nuget.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def __lt__(self, other):
def from_string(cls, str_version):
str_version = semver_index.coerce(str_version)
str_version, revision = _extract_revision(str_version)
if not semver_index.is_valid(str_version):
# If version is not valid, it is most likely an invalid input
# version then sort it to the last/largest element
return Version(semver_index.parse('999999'), 999999)
return Version(semver_index.parse(str_version), revision)


Expand Down
6 changes: 6 additions & 0 deletions osv/ecosystems/nuget_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ def test_next_version(self):
with self.assertRaises(ecosystems.EnumerateError):
ecosystem.next_version('doesnotexist123456', '1')

def test_sort_key(self):
ecosystem = ecosystems.get('NuGet')
# Tests invalid input versions
self.assertGreater(
ecosystem.sort_key('1.4.0rc3'), ecosystem.sort_key('3.0.0.4001'))


if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions osv/ecosystems/packagist_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class PackagistEcosystemTest(unittest.TestCase):
def test_packagist(self):
"""Test Packagist."""
ecosystem = ecosystems.get('Packagist')
# Any invalid versions will be handled.
self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0'))
self.assertLess(
ecosystem.sort_key('4.3-2RC1'), ecosystem.sort_key('4.3-2RC2'))
self.assertGreater(
Expand Down
7 changes: 6 additions & 1 deletion osv/ecosystems/pub.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ def __eq__(self, other):

@classmethod
def from_string(cls, str_version):
return Version(semver_index.parse(str_version))
# If version is not valid, it is most likely an invalid input
# version then sort it to the last/largest element
try:
return Version(semver_index.parse(str_version))
except ValueError:
return Version(semver_index.parse('999999'))


class Pub(Ecosystem):
Expand Down
2 changes: 2 additions & 0 deletions osv/ecosystems/pub_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def test_parse(self):
pub.Version.from_string('1.2.3+build.1')
pub.Version.from_string('1.2.3+x.7.z-92')
pub.Version.from_string('1.0.0-rc-1+build-1')
# Tests invalid versions
pub.Version.from_string('3.4.0rc3-invalid')

def test_empty_identifier(self):
"""Test parsing versions with empty identifiers.
Expand Down
1 change: 1 addition & 0 deletions osv/ecosystems/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class PyPI(Ecosystem):

def sort_key(self, version):
"""Sort key."""
# version.parse() handles invalid versions by returning LegacyVersion()
return packaging.version.parse(version)

def enumerate_versions(self,
Expand Down
6 changes: 6 additions & 0 deletions osv/ecosystems/pypi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ def test_next_version(self):
self.assertEqual('0.3.0', ecosystem.next_version('grpcio', '0'))
with self.assertRaises(ecosystems.EnumerateError):
ecosystem.next_version('doesnotexist123456', '1')

def test_sort_key(self):
"""Test sort_key"""
ecosystem = ecosystems.get('PyPI')
self.assertGreater(ecosystem.sort_key('2.0.0'), ecosystem.sort_key('1.0.0'))
self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0'))
16 changes: 9 additions & 7 deletions osv/ecosystems/rocky_linux_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ class RockyLinuxEcosystemTest(unittest.TestCase):
"""Rocky Linux ecosystem helper tests."""

def test_rocky_linux(self):
"""Test sort_key"""
ecosystem = ecosystems.get('Rocky Linux')
self.assertEqual("Rocky Linux", ecosystem.name)
self.assertEqual('Rocky Linux', ecosystem.name)
self.assertGreater(
ecosystem.sort_key("0:0.2.6-20.module+el8.9.0+1420+91577025"),
ecosystem.sort_key("0:0.0.99.4-5.module+el8.9.0+1445+07728297"))
ecosystem.sort_key('0:0.2.6-20.module+el8.9.0+1420+91577025'),
ecosystem.sort_key('0:0.0.99.4-5.module+el8.9.0+1445+07728297'))
self.assertGreater(
ecosystem.sort_key("0:0.2.6-20.module+el8.9.0+1420+91577025"),
ecosystem.sort_key("0"))
ecosystem.sort_key('0:0.2.6-20.module+el8.9.0+1420+91577025'),
ecosystem.sort_key('0'))
self.assertGreater(
ecosystem.sort_key("2:1.14.3-2.module+el8.10.0+1815+5fe7415e"),
ecosystem.sort_key("2:1.10.3-1.module+el8.10.0+1815+5fe7415e"))
ecosystem.sort_key('2:1.14.3-2.module+el8.10.0+1815+5fe7415e'),
ecosystem.sort_key('2:1.10.3-1.module+el8.10.0+1815+5fe7415e'))
self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0'))
9 changes: 7 additions & 2 deletions osv/ecosystems/rubygems.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import requests

from ..third_party.univers.gem import GemVersion
from ..third_party.univers.gem import GemVersion, InvalidVersionError

from . import config
from .helper_base import Ecosystem, EnumerateError
Expand All @@ -28,7 +28,12 @@ class RubyGems(Ecosystem):

def sort_key(self, version):
"""Sort key."""
return GemVersion(version)
# If version is not valid, it is most likely an invalid input
# version then sort it to the last/largest element
try:
return GemVersion(version)
except InvalidVersionError:
return GemVersion('999999')

def enumerate_versions(self,
package,
Expand Down
8 changes: 8 additions & 0 deletions osv/ecosystems/rubygems_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ def test_next_version(self):
ecosystem.next_version('rails', '5.0.0.beta4'))
with self.assertRaises(ecosystems.EnumerateError):
ecosystem.next_version('doesnotexist123456', '1')

def test_sort_key(self):
"""Test sort_key with invalid versions"""
ecosystem = ecosystems.get('RubyGems')
self.assertGreater(
ecosystem.sort_key('invalid'), ecosystem.sort_key('4.0.0.rc1'))
self.assertGreater(
ecosystem.sort_key('v3.1.1'), ecosystem.sort_key('4.0.0.rc1'))
15 changes: 9 additions & 6 deletions osv/ecosystems/ubuntu_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ class UbuntuEcosystemTest(unittest.TestCase):
"""Ubuntu ecosystem helper tests."""

def test_ubuntu(self):
"""Test sort_key"""
ecosystem = ecosystems.get('Ubuntu')
self.assertGreater(
ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.3"),
ecosystem.sort_key("2.40.0+dfsg-3ubuntu0.5"))
ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.3'),
ecosystem.sort_key('2.40.0+dfsg-3ubuntu0.5'))
self.assertGreater(
ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.3"),
ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.2"))
self.assertGreater(ecosystem.sort_key("5.4.13-1"), ecosystem.sort_key("0"))
ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.3'),
ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.2'))
self.assertGreater(ecosystem.sort_key('5.4.13-1'), ecosystem.sort_key('0'))
self.assertGreater(
ecosystem.sort_key("5.4.13-1"), ecosystem.sort_key("3.2.30-1"))
ecosystem.sort_key('5.4.13-1'), ecosystem.sort_key('3.2.30-1'))
self.assertGreater(
ecosystem.sort_key('invalid'), ecosystem.sort_key('3.2.30-1'))
Loading