Skip to content

Commit

Permalink
Add openssl support in univers
Browse files Browse the repository at this point in the history
- closes aboutcode-org#36
- For `OpenSSL-FIPS Module` see aboutcode-org#41

Signed-off-by: keshav-space <[email protected]>
  • Loading branch information
keshav-space committed Feb 21, 2022
1 parent 7206c1f commit 97c4b1f
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/univers/version_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,22 @@ def from_string(cls, string, version_class):
version = None
else:
version = version_class(version)

"""
Can't always be certain that a particular class would create an object
of its own kind it may return an object of a different class.
Example:
OpensslVersion when instantiated will return either an object of
LegacyOpensslVersion or SemverVersion depending on version.
OpensslVersion("0.9.8a") -> LegacyOpensslVersion(string="0.9.8a")
OpensslVersion("3.0.1") -> SemverVersion(string="3.0.1")
Hence the need for updating `version_class` after creation of version object
"""
if version != None:
version_class = version.__class__

return cls(comparator=comparator, version=version, version_class=version_class)

@staticmethod
Expand Down
20 changes: 20 additions & 0 deletions src/univers/version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,25 @@ def from_native(cls, string):
return cls(constraints=constraints)


class OpensslVersionRange(VersionRange):
scheme = "openssl"
version_class = versions.OpensslVersion
# openssl doesn't use <,>,<=,>=
# https://www.openssl.org/news/vulnerabilities.xml
vers_by_native_comparators = {"=": "="}

@classmethod
def from_native(cls, string):
cleaned = remove_spaces(string).lower()
constraints = set()
# plain single version
for clause in cleaned.split(","):
version = cls.version_class(clause)
constraint = VersionConstraint(comparator="=", version=version)
constraints.add(constraint)
return cls(constraints=list(constraints))


def is_even(s):
"""
Return True if the string "s" is an even number and False if this is an odd
Expand Down Expand Up @@ -902,4 +921,5 @@ def is_even(s):
"ebuild": EbuildVersionRange,
"archlinux": ArchLinuxVersionRange,
"nginx": NginxVersionRange,
"openssl": OpensslVersionRange,
}
80 changes: 80 additions & 0 deletions src/univers/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

from functools import total_ordering
import re

import attr
import semantic_version
Expand Down Expand Up @@ -343,3 +344,82 @@ def __gt__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return gentoo.vercmp(self.value, other.value) > 0


@attr.s(frozen=True, order=False, eq=False, hash=True)
class LegacyOpensslVersion(Version):
@classmethod
def is_valid(cls, string):
return OpensslVersion.is_valid_legacy(string)

# OpenSSL doesn't seems to use >,<,>=,=<
# See: https://www.openssl.org/news/vulnerabilities.xml
def __eq__(self, other):
if isinstance(other, SemverVersion):
return False
if not isinstance(other, self.__class__):
return NotImplemented
return self.value == other.value

def __lt__(self, other):
# All legacy scheme is behind those using semver
if isinstance(other, SemverVersion):
return True
if not isinstance(other, self.__class__):
return NotImplemented
re_vers1 = re.findall(r"^(\d+\.\d+\.\d+)(.*)$", self.value)[0]
re_vers2 = re.findall(r"^(\d+\.\d+\.\d+)(.*)$", other.value)[0]
if int(re_vers1[0].replace(".", "")) < int(re_vers2[0].replace(".", "")):
return True
return re_vers1[0] == re_vers2[0] and re_vers1[1] < re_vers2[1]

def __gt__(self, other):
# All openssl version using semver is ahead of legacy scheme
if isinstance(other, SemverVersion):
return False
if not isinstance(other, self.__class__):
return NotImplemented
re_vers1 = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", self.value)[0]
re_vers2 = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", other.value)[0]
if int(re_vers1[0].replace(".", "")) > int(re_vers2[0].replace(".", "")):
return True
return re_vers1[0] == re_vers2[0] and re_vers1[1] > re_vers2[1]

def __le__(self, other):
return self.__lt__(other) or self.__eq__(other)

def __ge__(self, other):
return self.__gt__(other) or self.__eq__(other)


@attr.s(frozen=True, order=False, eq=False, hash=True)
class OpensslVersion:
"""
Handles the object creation for openssl depending on
whether version is legacy or semver
"""

def __new__(cls, string):
if OpensslVersion.is_valid_legacy(string):
return LegacyOpensslVersion(string)
elif OpensslVersion.is_valid_new_openssl(string):
return SemverVersion(string)
raise InvalidVersion(f"{string!r} is not a valid/supported OpensslVersion")

@classmethod
def is_valid_legacy(cls, string):
# all legacy base version of openssl that ever exited/exists
all_legacy_base = ["0.9.6", "0.9.7", "0.9.8", "1.0.0", "1.0.1", "1.0.2", "1.1.0", "1.1.1"]
if string in all_legacy_base:
return True
regexed_string = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", string)
if not regexed_string or regexed_string[0][0] not in all_legacy_base:
return False
return True

@classmethod
def is_valid_new_openssl(cls, string):
regexed_string = re.findall(r"^(\d+)(\.)(.*)$", string)
if not regexed_string or int(regexed_string[0][0]) < 3:
return False
return SemverVersion.is_valid(string)
32 changes: 32 additions & 0 deletions tests/test_version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
from univers.version_range import VersionRange
from univers.version_range import RANGE_CLASS_BY_SCHEMES
from univers.version_range import NpmVersionRange
from univers.version_range import OpensslVersionRange
from univers.versions import PypiVersion
from univers.versions import RubygemsVersion
from univers.versions import SemverVersion
from univers.versions import LegacyOpensslVersion


class TestVersionRange(TestCase):
Expand Down Expand Up @@ -233,10 +235,40 @@ def test_NpmVersionRange_from_native_with_approximately_equal_to_operator(self):
version_range = NpmVersionRange.from_native(npm_range)
assert version_range == expected

def test_OpensslVersionRange_from_native_single_legacy(self):
openssl_range = "0.9.8j"
expected = OpensslVersionRange(
constraints=(
VersionConstraint(comparator="=", version=LegacyOpensslVersion(string="0.9.8j")),
)
)
version_range = OpensslVersionRange.from_native(openssl_range)
assert version_range == expected

def test_OpensslVersionRange_from_native_single_new_semver(self):
openssl_range = "3.0.1"
expected = OpensslVersionRange(
constraints=(VersionConstraint(comparator="=", version=SemverVersion(string="3.0.1")),)
)
version_range = OpensslVersionRange.from_native(openssl_range)
assert version_range == expected

def test_OpensslVersionRange_from_native_mixed(self):
openssl_range = "3.0.0, 1.0.1b"
expected = OpensslVersionRange(
constraints=(
VersionConstraint(comparator="=", version=LegacyOpensslVersion(string="1.0.1b")),
VersionConstraint(comparator="=", version=SemverVersion(string="3.0.0")),
)
)
version_range = OpensslVersionRange.from_native(openssl_range)
assert version_range == expected


VERSION_RANGE_TESTS_BY_SCHEME = {
"nginx": ["0.8.40+", "0.7.52-0.8.39", "0.9.10", "1.5.0+, 1.4.1+"],
"npm": ["^1.2.9", "~3.8.2", "5.0.0 - 7.2.3", "2.1 || 2.6", "1.1.2 1.2.2", "<=2.1 >=1.1"],
"openssl": ["1.1.1ak", "1.1.0", "3.0.2", "3.0.1, 0.9.7a", "1.0.2ck, 3.1.2"],
}


Expand Down

0 comments on commit 97c4b1f

Please sign in to comment.