Add test for `print_unknown_options`
authorWillenst <willenst@gmail.com>
Thu, 22 Aug 2024 18:28:07 +0000 (20:28 +0200)
committerWillenst <willenst@gmail.com>
Tue, 27 Aug 2024 13:40:07 +0000 (15:40 +0200)
kernel_hardening_checker/test_engine.py

index 50328cab26853cb9562b4a05357ed24f19bfc2d3..c6bbbb33a7d13a53e6292f661be7c65b76a3845d 100755 (executable)
@@ -18,7 +18,7 @@ import json
 import inspect
 from typing import Union, Optional, List, Dict, Tuple
 from .engine import StrOrBool, ChecklistObjType, KconfigCheck, CmdlineCheck, SysctlCheck, VersionCheck, OR, AND
-from .engine import populate_with_data, perform_checks, override_expected_value
+from .engine import populate_with_data, perform_checks, override_expected_value, print_unknown_options
 
 
 ResultType = List[Union[Dict[str, StrOrBool], str]]
@@ -115,6 +115,24 @@ class TestEngine(unittest.TestCase):
         sys.stdout = stdout_backup
         result.append(captured_output.getvalue())
 
+    @staticmethod
+    def get_unknown_options(checklist: List[ChecklistObjType],
+                            parsed_kconfig_options: Optional[Dict[str, str]],
+                            parsed_cmdline_options: Optional[Dict[str, str]],
+                            parsed_sysctl_options: Optional[Dict[str, str]],
+                            result: ResultType) -> None:
+        captured_output = io.StringIO()
+        stdout_backup = sys.stdout
+        sys.stdout = captured_output
+        if parsed_kconfig_options:
+            print_unknown_options(checklist, parsed_kconfig_options, 'kconfig')
+        if parsed_cmdline_options:
+            print_unknown_options(checklist, parsed_cmdline_options, 'cmdline')
+        if parsed_sysctl_options:
+            print_unknown_options(checklist, parsed_sysctl_options, 'sysctl')
+        sys.stdout = stdout_backup
+        result.append(captured_output.getvalue())
+
     def test_simple_kconfig(self) -> None:
         # 1. prepare the checklist
         config_checklist = [] # type: List[ChecklistObjType]
@@ -556,3 +574,132 @@ name_6                                  |sysctl | expected_6 |decision_6|     re
                  {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2_new', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'OK', 'check_result_bool': True},
                  {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3_new', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'OK', 'check_result_bool': True}]
         )
+
+    def test_print_unknown_options_simple(self) -> None:
+        # 1. prepare simple checklist
+        config_checklist = [] # type: List[ChecklistObjType]
+        config_checklist += [KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1')]
+        config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
+        config_checklist += [SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
+
+        # 2. prepare parsed options
+        parsed_kconfig_options  = {}
+        parsed_cmdline_options  = {}
+        parsed_sysctl_options  = {}
+
+        parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_1'] = 'expected_1'
+
+        parsed_cmdline_options['name_2'] = 'expected_2'
+        parsed_cmdline_options['NOCHECK_name_2'] = 'expected_2'
+
+        parsed_sysctl_options['name_3'] = 'expected_3'
+        parsed_sysctl_options['NOCHECK_name_3'] = 'expected_3'
+
+        # 3. run the print_unknown_options
+        result = [] # type: ResultType
+        self.get_unknown_options(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, result)
+
+        # 4. check that the results are correct
+        self.assertEqual(
+            result,
+            ['[?] No check for kconfig option CONFIG_NOCHECK_NAME_1 (expected_1)\n'
+             '[?] No check for cmdline option NOCHECK_name_2 (expected_2)\n'
+             '[?] No check for sysctl option NOCHECK_name_3 (expected_3)\n'])
+
+    def test_print_unknown_options_complex(self) -> None:
+        # 1. prepare partially complex checklist
+        config_checklist = [] # type: List[ChecklistObjType]
+        config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
+                                KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'))]
+        config_checklist += [AND(CmdlineCheck('reason_3', 'decision_3', 'name_3', 'expected_3'),
+                                 KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'))]
+        config_checklist += [OR(SysctlCheck('reason_5', 'decision_5', 'name_5', 'expected_5'),
+                                KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'))]
+
+        # 2. prepare parsed options
+        parsed_kconfig_options  = {}
+        parsed_cmdline_options  = {}
+        parsed_sysctl_options  = {}
+
+        parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_1'] = 'expected_1'
+        parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_2'] = 'expected_2'
+        parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_4'] = 'expected_4'
+        parsed_kconfig_options['CONFIG_NAME_6'] = 'expected_6'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_6'] = 'expected_6'
+
+        parsed_cmdline_options['name_3'] = 'expected_3'
+        parsed_cmdline_options['NOCHECK_name_3'] = 'expected_3'
+
+        parsed_sysctl_options['name_5'] = 'expected_5'
+        parsed_sysctl_options['NOCHECK_name_5'] = 'expected_5'
+
+        # 3. run the print_unknown_options
+        result = [] # type: ResultType
+        self.get_unknown_options(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, result)
+
+        # 4. check that the results are correct
+        self.assertEqual(
+            result,
+            ['[?] No check for kconfig option CONFIG_NOCHECK_NAME_1 (expected_1)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_2 (expected_2)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_4 (expected_4)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_6 (expected_6)\n'
+             '[?] No check for cmdline option NOCHECK_name_3 (expected_3)\n'
+             '[?] No check for sysctl option NOCHECK_name_5 (expected_5)\n'])
+
+    def test_print_unknown_options_complex_nested(self) -> None:
+        # 1. prepare partially complex checklist
+        config_checklist = [] # type: List[ChecklistObjType]
+        config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
+                                AND(KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'),
+                                    KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3')))]
+        config_checklist += [OR(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
+                                AND(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
+                                    VersionCheck((5, 9, 0))))]
+        config_checklist += [OR(CmdlineCheck('reason_6', 'decision_6', 'name_6', 'expected_6'),
+                                AND(SysctlCheck('reason_7', 'decision_7', 'name_7', 'expected_7'),
+                                    KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'expected_8')))]
+
+        # 2. prepare parsed options
+        parsed_kconfig_options  = {}
+        parsed_cmdline_options  = {}
+        parsed_sysctl_options  = {}
+
+        parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_1'] = 'expected_1'
+        parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_2'] = 'expected_2'
+        parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_3'] = 'expected_3'
+        parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_4'] = 'expected_4'
+        parsed_kconfig_options['CONFIG_NAME_5'] = 'expected_5'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_5'] = 'expected_5'
+        parsed_kconfig_options['CONFIG_NAME_8'] = 'expected_8'
+        parsed_kconfig_options['CONFIG_NOCHECK_NAME_8'] = 'expected_8'
+
+        parsed_cmdline_options['name_6'] = 'expected_6'
+        parsed_cmdline_options['NOCHECK_name_6'] = 'expected_6'
+
+        parsed_sysctl_options['name_7'] = 'expected_7'
+        parsed_sysctl_options['NOCHECK_name_7'] = 'expected_7'
+
+        # 3. run the print_unknown_options
+        result = [] # type: ResultType
+        self.get_unknown_options(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, result)
+
+        # 4. check that the results are correct
+        self.assertEqual(
+            result,
+            ['[?] No check for kconfig option CONFIG_NOCHECK_NAME_1 (expected_1)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_2 (expected_2)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_3 (expected_3)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_4 (expected_4)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_5 (expected_5)\n'
+             '[?] No check for kconfig option CONFIG_NOCHECK_NAME_8 (expected_8)\n'
+             '[?] No check for cmdline option NOCHECK_name_6 (expected_6)\n'
+             '[?] No check for sysctl option NOCHECK_name_7 (expected_7)\n'])