diff options
-rw-r--r-- | meta/classes/cve-check.bbclass | 10 | ||||
-rw-r--r-- | meta/lib/oe/cve_check.py | 58 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/cases/cve_check.py | 27 |
3 files changed, 90 insertions, 5 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index d843e7c4ac..646cc879dd 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass | |||
@@ -206,7 +206,7 @@ def check_cves(d, patched_cves): | |||
206 | """ | 206 | """ |
207 | Connect to the NVD database and find unpatched cves. | 207 | Connect to the NVD database and find unpatched cves. |
208 | """ | 208 | """ |
209 | from distutils.version import LooseVersion | 209 | from oe.cve_check import Version |
210 | 210 | ||
211 | pn = d.getVar("PN") | 211 | pn = d.getVar("PN") |
212 | real_pv = d.getVar("PV") | 212 | real_pv = d.getVar("PV") |
@@ -263,8 +263,8 @@ def check_cves(d, patched_cves): | |||
263 | else: | 263 | else: |
264 | if operator_start: | 264 | if operator_start: |
265 | try: | 265 | try: |
266 | vulnerable_start = (operator_start == '>=' and LooseVersion(pv) >= LooseVersion(version_start)) | 266 | vulnerable_start = (operator_start == '>=' and Version(pv) >= Version(version_start)) |
267 | vulnerable_start |= (operator_start == '>' and LooseVersion(pv) > LooseVersion(version_start)) | 267 | vulnerable_start |= (operator_start == '>' and Version(pv) > Version(version_start)) |
268 | except: | 268 | except: |
269 | bb.warn("%s: Failed to compare %s %s %s for %s" % | 269 | bb.warn("%s: Failed to compare %s %s %s for %s" % |
270 | (product, pv, operator_start, version_start, cve)) | 270 | (product, pv, operator_start, version_start, cve)) |
@@ -274,8 +274,8 @@ def check_cves(d, patched_cves): | |||
274 | 274 | ||
275 | if operator_end: | 275 | if operator_end: |
276 | try: | 276 | try: |
277 | vulnerable_end = (operator_end == '<=' and LooseVersion(pv) <= LooseVersion(version_end)) | 277 | vulnerable_end = (operator_end == '<=' and Version(pv) <= Version(version_end) ) |
278 | vulnerable_end |= (operator_end == '<' and LooseVersion(pv) < LooseVersion(version_end)) | 278 | vulnerable_end |= (operator_end == '<' and Version(pv) < Version(version_end) ) |
279 | except: | 279 | except: |
280 | bb.warn("%s: Failed to compare %s %s %s for %s" % | 280 | bb.warn("%s: Failed to compare %s %s %s for %s" % |
281 | (product, pv, operator_end, version_end, cve)) | 281 | (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 @@ | |||
1 | import collections | ||
2 | import re | ||
3 | import itertools | ||
4 | |||
5 | _Version = collections.namedtuple( | ||
6 | "_Version", ["release", "pre_l", "pre_v"] | ||
7 | ) | ||
8 | |||
9 | class Version(): | ||
10 | _version_pattern = r"""v?(?:(?P<release>[0-9]+(?:[-\.][0-9]+)*)(?P<pre>[-_\.]?(?P<pre_l>(rc|alpha|beta|pre|preview|dev))[-_\.]?(?P<pre_v>[0-9]+)?)?)(.*)?""" | ||
11 | _regex = re.compile(r"^\s*" + _version_pattern + r"\s*$", re.VERBOSE | re.IGNORECASE) | ||
12 | def __init__(self, version): | ||
13 | match = self._regex.search(version) | ||
14 | if not match: | ||
15 | raise Exception("Invalid version: '{0}'".format(version)) | ||
16 | |||
17 | self._version = _Version( | ||
18 | release=tuple(int(i) for i in match.group("release").replace("-",".").split(".")), | ||
19 | pre_l=match.group("pre_l"), | ||
20 | pre_v=match.group("pre_v") | ||
21 | ) | ||
22 | |||
23 | self._key = _cmpkey( | ||
24 | self._version.release, | ||
25 | self._version.pre_l, | ||
26 | self._version.pre_v | ||
27 | ) | ||
28 | |||
29 | def __le__(self, other): | ||
30 | if not isinstance(other, Version): | ||
31 | return NotImplemented | ||
32 | return self._key <= other._key | ||
33 | |||
34 | def __lt__(self, other): | ||
35 | if not isinstance(other, Version): | ||
36 | return NotImplemented | ||
37 | return self._key < other._key | ||
38 | |||
39 | def __ge__(self, other): | ||
40 | if not isinstance(other, Version): | ||
41 | return NotImplemented | ||
42 | return self._key >= other._key | ||
43 | |||
44 | def __gt__(self, other): | ||
45 | if not isinstance(other, Version): | ||
46 | return NotImplemented | ||
47 | return self._key > other._key | ||
48 | |||
49 | def _cmpkey(release, pre_l, pre_v): | ||
50 | # remove leading 0 | ||
51 | _release = tuple( | ||
52 | reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) | ||
53 | ) | ||
54 | if pre_l is None and pre_v is None: | ||
55 | _pre = float('inf') | ||
56 | else: | ||
57 | _pre = float(pre_v) if pre_v else float('-inf') | ||
58 | 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 @@ | |||
1 | from oe.cve_check import Version | ||
2 | from oeqa.selftest.case import OESelftestTestCase | ||
3 | |||
4 | class CVECheck(OESelftestTestCase): | ||
5 | |||
6 | def test_version_compare(self): | ||
7 | result = Version("100") > Version("99") | ||
8 | self.assertTrue( result, msg="Failed to compare version '100' > '99'") | ||
9 | result = Version("2.3.1") > Version("2.2.3") | ||
10 | self.assertTrue( result, msg="Failed to compare version '2.3.1' > '2.2.3'") | ||
11 | result = Version("2021-01-21") > Version("2020-12-25") | ||
12 | self.assertTrue( result, msg="Failed to compare version '2021-01-21' > '2020-12-25'") | ||
13 | result = Version("1.2-20200910") < Version("1.2-20200920") | ||
14 | self.assertTrue( result, msg="Failed to compare version '1.2-20200910' < '1.2-20200920'") | ||
15 | |||
16 | result = Version("1.0") >= Version("1.0beta") | ||
17 | self.assertTrue( result, msg="Failed to compare version '1.0' >= '1.0beta'") | ||
18 | result = Version("1.0-rc2") > Version("1.0-rc1") | ||
19 | self.assertTrue( result, msg="Failed to compare version '1.0-rc2' > '1.0-rc1'") | ||
20 | result = Version("1.0.alpha1") < Version("1.0") | ||
21 | self.assertTrue( result, msg="Failed to compare version '1.0.alpha1' < '1.0'") | ||
22 | result = Version("1.0_dev") <= Version("1.0") | ||
23 | self.assertTrue( result, msg="Failed to compare version '1.0_dev' <= '1.0'") | ||
24 | |||
25 | # ignore "p1" and "p2", so these should be equal | ||
26 | result = Version("1.0p2") <= Version("1.0p1") and Version("1.0p2") >= Version("1.0p1") | ||
27 | self.assertTrue( result ,msg="Failed to compare version '1.0p2' to '1.0p1'") | ||