|
5 | 5 | # Visit https://aboutcode.org and https://github.com/nexB/univers for support and download. |
6 | 6 |
|
7 | 7 | from functools import total_ordering |
| 8 | +import re |
8 | 9 |
|
9 | 10 | import attr |
10 | 11 | import semantic_version |
@@ -343,3 +344,82 @@ def __gt__(self, other): |
343 | 344 | if not isinstance(other, self.__class__): |
344 | 345 | return NotImplemented |
345 | 346 | return gentoo.vercmp(self.value, other.value) > 0 |
| 347 | + |
| 348 | + |
| 349 | +@attr.s(frozen=True, order=False, eq=False, hash=True) |
| 350 | +class LegacyOpensslVersion(Version): |
| 351 | + @classmethod |
| 352 | + def is_valid(cls, string): |
| 353 | + return OpensslVersion.is_valid_legacy(string) |
| 354 | + |
| 355 | + # OpenSSL doesn't seems to use >,<,>=,=< |
| 356 | + # See: https://www.openssl.org/news/vulnerabilities.xml |
| 357 | + def __eq__(self, other): |
| 358 | + if isinstance(other, SemverVersion): |
| 359 | + return False |
| 360 | + if not isinstance(other, self.__class__): |
| 361 | + return NotImplemented |
| 362 | + return self.value == other.value |
| 363 | + |
| 364 | + def __lt__(self, other): |
| 365 | + # All legacy scheme is behind those using semver |
| 366 | + if isinstance(other, SemverVersion): |
| 367 | + return True |
| 368 | + if not isinstance(other, self.__class__): |
| 369 | + return NotImplemented |
| 370 | + re_vers1 = re.findall(r"^(\d+\.\d+\.\d+)(.*)$", self.value)[0] |
| 371 | + re_vers2 = re.findall(r"^(\d+\.\d+\.\d+)(.*)$", other.value)[0] |
| 372 | + if int(re_vers1[0].replace(".", "")) < int(re_vers2[0].replace(".", "")): |
| 373 | + return True |
| 374 | + return re_vers1[0] == re_vers2[0] and re_vers1[1] < re_vers2[1] |
| 375 | + |
| 376 | + def __gt__(self, other): |
| 377 | + # All openssl version using semver is ahead of legacy scheme |
| 378 | + if isinstance(other, SemverVersion): |
| 379 | + return False |
| 380 | + if not isinstance(other, self.__class__): |
| 381 | + return NotImplemented |
| 382 | + re_vers1 = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", self.value)[0] |
| 383 | + re_vers2 = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", other.value)[0] |
| 384 | + if int(re_vers1[0].replace(".", "")) > int(re_vers2[0].replace(".", "")): |
| 385 | + return True |
| 386 | + return re_vers1[0] == re_vers2[0] and re_vers1[1] > re_vers2[1] |
| 387 | + |
| 388 | + def __le__(self, other): |
| 389 | + return self.__lt__(other) or self.__eq__(other) |
| 390 | + |
| 391 | + def __ge__(self, other): |
| 392 | + return self.__gt__(other) or self.__eq__(other) |
| 393 | + |
| 394 | + |
| 395 | +@attr.s(frozen=True, order=False, eq=False, hash=True) |
| 396 | +class OpensslVersion: |
| 397 | + """ |
| 398 | + Handles the object creation for openssl depending on |
| 399 | + whether version is legacy or semver |
| 400 | + """ |
| 401 | + |
| 402 | + def __new__(cls, string): |
| 403 | + if OpensslVersion.is_valid_legacy(string): |
| 404 | + return LegacyOpensslVersion(string) |
| 405 | + elif OpensslVersion.is_valid_new_openssl(string): |
| 406 | + return SemverVersion(string) |
| 407 | + raise InvalidVersion(f"{string!r} is not a valid/supported OpensslVersion") |
| 408 | + |
| 409 | + @classmethod |
| 410 | + def is_valid_legacy(cls, string): |
| 411 | + # all legacy base version of openssl that ever exited/exists |
| 412 | + 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"] |
| 413 | + if string in all_legacy_base: |
| 414 | + return True |
| 415 | + regexed_string = re.findall(r"^(\d+\.\d+\.\d+)([a-z-]+)$", string) |
| 416 | + if not regexed_string or regexed_string[0][0] not in all_legacy_base: |
| 417 | + return False |
| 418 | + return True |
| 419 | + |
| 420 | + @classmethod |
| 421 | + def is_valid_new_openssl(cls, string): |
| 422 | + regexed_string = re.findall(r"^(\d+)(\.)(.*)$", string) |
| 423 | + if not regexed_string or int(regexed_string[0][0]) < 3: |
| 424 | + return False |
| 425 | + return SemverVersion.is_valid(string) |
0 commit comments