From cbd83d7364be51dcebc1f8e3111195cdc5b2d483 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 21 Oct 2020 21:20:37 +0300 Subject: [PATCH] Add nested ComplexOptChecks support Now we can do things like OR(opt1, AND(opt2, opt3)). Cool! Refers to #48 --- kconfig_hardened_check/__init__.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index aa08c12..f03efcb 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -146,6 +146,10 @@ class PresenceCheck: class ComplexOptCheck: def __init__(self, *opts): self.opts = opts + if not self.opts: + sys.exit('[!] ERROR: empty {} check'.format(self.__class__.__name__)) + if not isinstance(opts[0], OptCheck): + sys.exit('[!] ERROR: invalid {} check: {}'.format(self.__class__.__name__, opts)) self.result = None @property @@ -192,9 +196,13 @@ class OR(ComplexOptCheck): for i, opt in enumerate(self.opts): ret = opt.check() if ret: - if i == 0 or not hasattr(opt, 'expected'): + if opt.result != 'OK' or i == 0: + # Preserve additional explanation of this OK result. + # Simple OK is enough only for the main option that + # this OR-check is about. self.result = opt.result else: + # Simple OK is not enough for additional checks. self.result = 'OK: CONFIG_{} "{}"'.format(opt.name, opt.expected) return True self.result = self.opts[0].result @@ -215,9 +223,13 @@ class AND(ComplexOptCheck): self.result = opt.result return ret if not ret: - if hasattr(opt, 'expected'): + # This FAIL is caused by additional checks, + # and not by the main option that this AND-check is about. + if opt.result.startswith('FAIL: \"'): + # Describe the reason of the FAIL. self.result = 'FAIL: CONFIG_{} not "{}"'.format(opt.name, opt.expected) else: + # This FAIL message is self-explaining. self.result = opt.result return False @@ -577,23 +589,30 @@ def print_checklist(mode, checklist, with_results): print('[+] Config check is finished: \'OK\' - {}{} / \'FAIL\' - {}{}'.format(ok_count, ok_suppressed, fail_count, fail_suppressed)) -def perform_checks(checklist, parsed_options, kernel_version): - for opt in checklist: +def perform_check(opt, parsed_options, kernel_version): if hasattr(opt, 'opts'): # prepare ComplexOptCheck for o in opt.opts: + if hasattr(o, 'opts'): + # Recursion for nested ComplexOptChecks + perform_check(o, parsed_options, kernel_version) if hasattr(o, 'state'): o.state = parsed_options.get(o.name, None) if hasattr(o, 'ver'): o.ver = kernel_version else: - # prepare simple check + # prepare simple check, opt.state is mandatory if not hasattr(opt, 'state'): sys.exit('[!] ERROR: bad simple check {}'.format(vars(opt))) opt.state = parsed_options.get(opt.name, None) opt.check() +def perform_checks(checklist, parsed_options, kernel_version): + for opt in checklist: + perform_check(opt, parsed_options, kernel_version) + + def parse_config_file(parsed_options, fname): with open(fname, 'r') as f: opt_is_on = re.compile("CONFIG_[a-zA-Z0-9_]*=[a-zA-Z0-9_\"]*") -- 2.31.1