diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index 669da6c8e9..93af667544 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass @@ -203,7 +203,7 @@ def check_cves(d, patched_cves): """ Connect to the NVD database and find unpatched cves. """ - from distutils.version import LooseVersion + from oe.cve_check import Version pn = d.getVar("PN") real_pv = d.getVar("PV") @@ -260,8 +260,8 @@ def check_cves(d, patched_cves): else: if operator_start: try: - vulnerable_start = (operator_start == '>=' and LooseVersion(pv) >= LooseVersion(version_start)) - vulnerable_start |= (operator_start == '>' and LooseVersion(pv) > LooseVersion(version_start)) + vulnerable_start = (operator_start == '>=' and Version(pv) >= Version(version_start)) + vulnerable_start |= (operator_start == '>' and Version(pv) > Version(version_start)) except: bb.warn("%s: Failed to compare %s %s %s for %s" % (product, pv, operator_start, version_start, cve)) @@ -271,8 +271,8 @@ def check_cves(d, patched_cves): if operator_end: try: - vulnerable_end = (operator_end == '<=' and LooseVersion(pv) <= LooseVersion(version_end)) - vulnerable_end |= (operator_end == '<' and LooseVersion(pv) < LooseVersion(version_end)) + vulnerable_end = (operator_end == '<=' and Version(pv) <= Version(version_end) ) + vulnerable_end |= (operator_end == '<' and Version(pv) < Version(version_end) ) except: bb.warn("%s: Failed to compare %s %s %s for %s" % (product, pv, operator_end, version_end, cve)) diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py new file mode 100644 index 0000000000..ec48a3f829 --- /dev/null +++ b/meta/lib/oe/cve_check.py @@ -0,0 +1,58 @@ +import collections +import re +import itertools + +_Version = collections.namedtuple( + "_Version", ["release", "pre_l", "pre_v"] +) + +class Version(): + _version_pattern = r"""v?(?:(?P[0-9]+(?:[-\.][0-9]+)*)(?P
[-_\.]?(?P(rc|alpha|beta|pre|preview|dev))[-_\.]?(?P[0-9]+)?)?)(.*)?"""
+    _regex = re.compile(r"^\s*" + _version_pattern + r"\s*$", re.VERBOSE | re.IGNORECASE)
+    def __init__(self, version):
+        match = self._regex.search(version)
+        if not match:
+            raise Exception("Invalid version: '{0}'".format(version))
+
+        self._version = _Version(
+            release=tuple(int(i) for i in match.group("release").replace("-",".").split(".")),
+            pre_l=match.group("pre_l"),
+            pre_v=match.group("pre_v")
+        )
+
+        self._key = _cmpkey(
+            self._version.release,
+            self._version.pre_l,
+            self._version.pre_v
+        )
+
+    def __le__(self, other):
+        if not isinstance(other, Version):
+            return NotImplemented
+        return self._key <= other._key
+
+    def __lt__(self, other):
+        if not isinstance(other, Version):
+            return NotImplemented
+        return self._key < other._key
+
+    def __ge__(self, other):
+        if not isinstance(other, Version):
+            return NotImplemented
+        return self._key >= other._key
+
+    def __gt__(self, other):
+        if not isinstance(other, Version):
+            return NotImplemented
+        return self._key > other._key
+
+def _cmpkey(release, pre_l, pre_v):
+    # remove leading 0
+    _release = tuple(
+        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
+    )
+    if pre_l is None and pre_v is None:
+        _pre = float('inf')
+    else:
+        _pre = float(pre_v) if pre_v else float('-inf')
+    return _release, _pre
diff --git a/meta/lib/oeqa/selftest/cases/cve_check.py b/meta/lib/oeqa/selftest/cases/cve_check.py
new file mode 100644
index 0000000000..35e2b29a9a
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/cve_check.py
@@ -0,0 +1,27 @@
+from oe.cve_check import Version
+from oeqa.selftest.case import OESelftestTestCase
+
+class CVECheck(OESelftestTestCase):
+
+    def test_version_compare(self):
+        result = Version("100") > Version("99")
+        self.assertTrue( result, msg="Failed to compare version '100' > '99'")
+        result = Version("2.3.1") > Version("2.2.3")
+        self.assertTrue( result, msg="Failed to compare version '2.3.1' > '2.2.3'")
+        result = Version("2021-01-21") > Version("2020-12-25")
+        self.assertTrue( result, msg="Failed to compare version '2021-01-21' > '2020-12-25'")
+        result = Version("1.2-20200910") < Version("1.2-20200920")
+        self.assertTrue( result, msg="Failed to compare version '1.2-20200910' < '1.2-20200920'")
+
+        result = Version("1.0") >= Version("1.0beta")
+        self.assertTrue( result, msg="Failed to compare version '1.0' >= '1.0beta'")
+        result = Version("1.0-rc2") > Version("1.0-rc1")
+        self.assertTrue( result, msg="Failed to compare version '1.0-rc2' > '1.0-rc1'")
+        result = Version("1.0.alpha1") < Version("1.0")
+        self.assertTrue( result, msg="Failed to compare version '1.0.alpha1' < '1.0'")
+        result = Version("1.0_dev") <= Version("1.0")
+        self.assertTrue( result, msg="Failed to compare version '1.0_dev' <= '1.0'")
+
+        # ignore "p1" and "p2", so these should be equal
+        result = Version("1.0p2") <= Version("1.0p1") and Version("1.0p2") >= Version("1.0p1")
+        self.assertTrue( result ,msg="Failed to compare version '1.0p2' to '1.0p1'")