diff options
author | Lee Chee Yang <chee.yang.lee@intel.com> | 2021-01-29 11:51:15 +0800 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-01-30 10:41:04 +0000 |
commit | 86b42289bda5bc2a4eff221ab476f170dd3d3794 (patch) | |
tree | e936bb438cb97ea1a07db5517bc29524e2af501f | |
parent | ef208aaf0f05c478a3dbade3d2bfd3744f91d87e (diff) | |
download | poky-86b42289bda5bc2a4eff221ab476f170dd3d3794.tar.gz |
cve_check: add CVE_VERSION_SUFFIX to indicate suffix in versioning
add CVE_VERSION_SUFFIX to indicate the version suffix type, currently
works in two value, "alphabetical" if the version string uses single
alphabetical character suffix as incremental release, blank to not
consider the unidentified suffixes. This can be expand when more suffix
pattern identified.
refactor cve_check.Version class to use functools and add parameter to
handle suffix condition.
Also update testcases to cover new changes.
(From OE-Core rev: 5dfd5ad5144708b474ef31eaa89a846c57be8ac0)
Signed-off-by: Lee Chee Yang <chee.yang.lee@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | meta/classes/cve-check.bbclass | 12 | ||||
-rw-r--r-- | meta/lib/oe/cve_check.py | 40 | ||||
-rw-r--r-- | meta/lib/oeqa/selftest/cases/cve_check.py | 11 |
3 files changed, 39 insertions, 24 deletions
diff --git a/meta/classes/cve-check.bbclass b/meta/classes/cve-check.bbclass index 646cc879dd..ed86403b6b 100644 --- a/meta/classes/cve-check.bbclass +++ b/meta/classes/cve-check.bbclass | |||
@@ -53,6 +53,9 @@ CVE_CHECK_PN_WHITELIST ?= "" | |||
53 | # | 53 | # |
54 | CVE_CHECK_WHITELIST ?= "" | 54 | CVE_CHECK_WHITELIST ?= "" |
55 | 55 | ||
56 | # set to "alphabetical" for version using single alphabetical character as increament release | ||
57 | CVE_VERSION_SUFFIX ??= "" | ||
58 | |||
56 | python cve_save_summary_handler () { | 59 | python cve_save_summary_handler () { |
57 | import shutil | 60 | import shutil |
58 | import datetime | 61 | import datetime |
@@ -210,6 +213,7 @@ def check_cves(d, patched_cves): | |||
210 | 213 | ||
211 | pn = d.getVar("PN") | 214 | pn = d.getVar("PN") |
212 | real_pv = d.getVar("PV") | 215 | real_pv = d.getVar("PV") |
216 | suffix = d.getVar("CVE_VERSION_SUFFIX") | ||
213 | 217 | ||
214 | cves_unpatched = [] | 218 | cves_unpatched = [] |
215 | # CVE_PRODUCT can contain more than one product (eg. curl/libcurl) | 219 | # CVE_PRODUCT can contain more than one product (eg. curl/libcurl) |
@@ -263,8 +267,8 @@ def check_cves(d, patched_cves): | |||
263 | else: | 267 | else: |
264 | if operator_start: | 268 | if operator_start: |
265 | try: | 269 | try: |
266 | vulnerable_start = (operator_start == '>=' and Version(pv) >= Version(version_start)) | 270 | vulnerable_start = (operator_start == '>=' and Version(pv,suffix) >= Version(version_start,suffix)) |
267 | vulnerable_start |= (operator_start == '>' and Version(pv) > Version(version_start)) | 271 | vulnerable_start |= (operator_start == '>' and Version(pv,suffix) > Version(version_start,suffix)) |
268 | except: | 272 | except: |
269 | bb.warn("%s: Failed to compare %s %s %s for %s" % | 273 | bb.warn("%s: Failed to compare %s %s %s for %s" % |
270 | (product, pv, operator_start, version_start, cve)) | 274 | (product, pv, operator_start, version_start, cve)) |
@@ -274,8 +278,8 @@ def check_cves(d, patched_cves): | |||
274 | 278 | ||
275 | if operator_end: | 279 | if operator_end: |
276 | try: | 280 | try: |
277 | vulnerable_end = (operator_end == '<=' and Version(pv) <= Version(version_end) ) | 281 | vulnerable_end = (operator_end == '<=' and Version(pv,suffix) <= Version(version_end,suffix) ) |
278 | vulnerable_end |= (operator_end == '<' and Version(pv) < Version(version_end) ) | 282 | vulnerable_end |= (operator_end == '<' and Version(pv,suffix) < Version(version_end,suffix) ) |
279 | except: | 283 | except: |
280 | bb.warn("%s: Failed to compare %s %s %s for %s" % | 284 | bb.warn("%s: Failed to compare %s %s %s for %s" % |
281 | (product, pv, operator_end, version_end, cve)) | 285 | (product, pv, operator_end, version_end, cve)) |
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py index ec48a3f829..ce755f940a 100644 --- a/meta/lib/oe/cve_check.py +++ b/meta/lib/oe/cve_check.py | |||
@@ -1,58 +1,60 @@ | |||
1 | import collections | 1 | import collections |
2 | import re | 2 | import re |
3 | import itertools | 3 | import itertools |
4 | import functools | ||
4 | 5 | ||
5 | _Version = collections.namedtuple( | 6 | _Version = collections.namedtuple( |
6 | "_Version", ["release", "pre_l", "pre_v"] | 7 | "_Version", ["release", "patch_l", "pre_l", "pre_v"] |
7 | ) | 8 | ) |
8 | 9 | ||
10 | @functools.total_ordering | ||
9 | class Version(): | 11 | 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]+)?)?)(.*)?""" | 12 | |
11 | _regex = re.compile(r"^\s*" + _version_pattern + r"\s*$", re.VERBOSE | re.IGNORECASE) | 13 | def __init__(self, version, suffix=None): |
12 | def __init__(self, version): | 14 | if str(suffix) == "alphabetical": |
13 | match = self._regex.search(version) | 15 | version_pattern = r"""r?v?(?:(?P<release>[0-9]+(?:[-\.][0-9]+)*)(?P<patch>[-_\.]?(?P<patch_l>[a-z]))?(?P<pre>[-_\.]?(?P<pre_l>(rc|alpha|beta|pre|preview|dev))[-_\.]?(?P<pre_v>[0-9]+)?)?)(.*)?""" |
16 | else: | ||
17 | version_pattern = r"""r?v?(?:(?P<release>[0-9]+(?:[-\.][0-9]+)*)(?P<pre>[-_\.]?(?P<pre_l>(rc|alpha|beta|pre|preview|dev))[-_\.]?(?P<pre_v>[0-9]+)?)?)(.*)?""" | ||
18 | regex = re.compile(r"^\s*" + version_pattern + r"\s*$", re.VERBOSE | re.IGNORECASE) | ||
19 | |||
20 | match = regex.search(version) | ||
14 | if not match: | 21 | if not match: |
15 | raise Exception("Invalid version: '{0}'".format(version)) | 22 | raise Exception("Invalid version: '{0}'".format(version)) |
16 | 23 | ||
17 | self._version = _Version( | 24 | self._version = _Version( |
18 | release=tuple(int(i) for i in match.group("release").replace("-",".").split(".")), | 25 | release=tuple(int(i) for i in match.group("release").replace("-",".").split(".")), |
26 | patch_l=match.group("patch_l") if str(suffix) == "alphabetical" and match.group("patch_l") else "", | ||
19 | pre_l=match.group("pre_l"), | 27 | pre_l=match.group("pre_l"), |
20 | pre_v=match.group("pre_v") | 28 | pre_v=match.group("pre_v") |
21 | ) | 29 | ) |
22 | 30 | ||
23 | self._key = _cmpkey( | 31 | self._key = _cmpkey( |
24 | self._version.release, | 32 | self._version.release, |
33 | self._version.patch_l, | ||
25 | self._version.pre_l, | 34 | self._version.pre_l, |
26 | self._version.pre_v | 35 | self._version.pre_v |
27 | ) | 36 | ) |
28 | 37 | ||
29 | def __le__(self, other): | 38 | def __eq__(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): | 39 | if not isinstance(other, Version): |
36 | return NotImplemented | 40 | return NotImplemented |
37 | return self._key < other._key | 41 | 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 | 42 | ||
44 | def __gt__(self, other): | 43 | def __gt__(self, other): |
45 | if not isinstance(other, Version): | 44 | if not isinstance(other, Version): |
46 | return NotImplemented | 45 | return NotImplemented |
47 | return self._key > other._key | 46 | return self._key > other._key |
48 | 47 | ||
49 | def _cmpkey(release, pre_l, pre_v): | 48 | def _cmpkey(release, patch_l, pre_l, pre_v): |
50 | # remove leading 0 | 49 | # remove leading 0 |
51 | _release = tuple( | 50 | _release = tuple( |
52 | reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) | 51 | reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) |
53 | ) | 52 | ) |
53 | |||
54 | _patch = patch_l.upper() | ||
55 | |||
54 | if pre_l is None and pre_v is None: | 56 | if pre_l is None and pre_v is None: |
55 | _pre = float('inf') | 57 | _pre = float('inf') |
56 | else: | 58 | else: |
57 | _pre = float(pre_v) if pre_v else float('-inf') | 59 | _pre = float(pre_v) if pre_v else float('-inf') |
58 | return _release, _pre | 60 | return _release, _patch, _pre |
diff --git a/meta/lib/oeqa/selftest/cases/cve_check.py b/meta/lib/oeqa/selftest/cases/cve_check.py index 35e2b29a9a..3f343a2841 100644 --- a/meta/lib/oeqa/selftest/cases/cve_check.py +++ b/meta/lib/oeqa/selftest/cases/cve_check.py | |||
@@ -23,5 +23,14 @@ class CVECheck(OESelftestTestCase): | |||
23 | self.assertTrue( result, msg="Failed to compare version '1.0_dev' <= '1.0'") | 23 | self.assertTrue( result, msg="Failed to compare version '1.0_dev' <= '1.0'") |
24 | 24 | ||
25 | # ignore "p1" and "p2", so these should be equal | 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") | 26 | result = Version("1.0p2") == Version("1.0p1") |
27 | self.assertTrue( result ,msg="Failed to compare version '1.0p2' to '1.0p1'") | 27 | self.assertTrue( result ,msg="Failed to compare version '1.0p2' to '1.0p1'") |
28 | # ignore the "b" and "r" | ||
29 | result = Version("1.0b") == Version("1.0r") | ||
30 | self.assertTrue( result ,msg="Failed to compare version '1.0b' to '1.0r'") | ||
31 | |||
32 | # consider the trailing alphabet as patched level when comparing | ||
33 | result = Version("1.0b","alphabetical") < Version("1.0r","alphabetical") | ||
34 | self.assertTrue( result ,msg="Failed to compare version with suffix '1.0b' < '1.0r'") | ||
35 | result = Version("1.0b","alphabetical") > Version("1.0","alphabetical") | ||
36 | self.assertTrue( result ,msg="Failed to compare version with suffix '1.0b' > '1.0'") | ||