Code refactoring to improve test coverage (I)
[kconfig-hardened-check.git] / kernel_hardening_checker / __init__.py
index 7a90a0247fad077fe208d7cea9545a1fa96ee1ff..043dae8c8362b446e9a25e9291d4e095c7e85d9d 100644 (file)
@@ -8,28 +8,35 @@ Author: Alexander Popov <alex.popov@linux.com>
 This module performs input/output.
 """
 
 This module performs input/output.
 """
 
-# pylint: disable=missing-function-docstring,line-too-long,invalid-name,too-many-branches,too-many-statements
+# pylint: disable=missing-function-docstring,line-too-long,too-many-branches,too-many-statements
 
 
+import os
 import gzip
 import sys
 from argparse import ArgumentParser
 from typing import List, Tuple, Dict, TextIO
 import re
 import json
 import gzip
 import sys
 from argparse import ArgumentParser
 from typing import List, Tuple, Dict, TextIO
 import re
 import json
-from .__about__ import __version__
 from .checks import add_kconfig_checks, add_cmdline_checks, normalize_cmdline_options, add_sysctl_checks
 from .engine import StrOrNone, TupleOrNone, ChecklistObjType
 from .engine import print_unknown_options, populate_with_data, perform_checks, override_expected_value
 
 
 from .checks import add_kconfig_checks, add_cmdline_checks, normalize_cmdline_options, add_sysctl_checks
 from .engine import StrOrNone, TupleOrNone, ChecklistObjType
 from .engine import print_unknown_options, populate_with_data, perform_checks, override_expected_value
 
 
-def _open(file: str, *args, **kwargs) -> TextIO:
-    if file.endswith('.gz'):
-        return gzip.open(file, *args, **kwargs)
-    return open(file, *args, **kwargs)
+# kernel-hardening-checker version
+__version__ = '0.6.6'
+
+
+def _open(file: str) -> TextIO:
+    try:
+        if file.endswith('.gz'):
+            return gzip.open(file, 'rt', encoding='utf-8')
+        return open(file, 'rt', encoding='utf-8')
+    except FileNotFoundError:
+        sys.exit(f'[!] ERROR: unable to open {file}, are you sure it exists?')
 
 
 def detect_arch(fname: str, archs: List[str]) -> Tuple[StrOrNone, str]:
 
 
 def detect_arch(fname: str, archs: List[str]) -> Tuple[StrOrNone, str]:
-    with _open(fname, 'rt', encoding='utf-8') as f:
+    with _open(fname) as f:
         arch_pattern = re.compile(r"CONFIG_[a-zA-Z0-9_]+=y$")
         arch = None
         for line in f.readlines():
         arch_pattern = re.compile(r"CONFIG_[a-zA-Z0-9_]+=y$")
         arch = None
         for line in f.readlines():
@@ -46,7 +53,7 @@ def detect_arch(fname: str, archs: List[str]) -> Tuple[StrOrNone, str]:
 
 
 def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
 
 
 def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
-    with _open(fname, 'rt', encoding='utf-8') as f:
+    with _open(fname) as f:
         ver_pattern = re.compile(r"^# Linux/.+ Kernel Configuration$|^Linux version .+")
         for line in f.readlines():
             if ver_pattern.match(line):
         ver_pattern = re.compile(r"^# Linux/.+ Kernel Configuration$|^Linux version .+")
         for line in f.readlines():
             if ver_pattern.match(line):
@@ -55,7 +62,7 @@ def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
                 ver_str = parts[2].split('-', 1)[0]
                 ver_numbers = ver_str.split('.')
                 if len(ver_numbers) >= 3:
                 ver_str = parts[2].split('-', 1)[0]
                 ver_numbers = ver_str.split('.')
                 if len(ver_numbers) >= 3:
-                    if all(map(lambda x: x.isdigit(), ver_numbers)):
+                    if all(map(lambda x: x.isdecimal(), ver_numbers)):
                         return tuple(map(int, ver_numbers)), 'OK'
                 msg = f'failed to parse the version "{parts[2]}"'
                 return None, msg
                         return tuple(map(int, ver_numbers)), 'OK'
                 msg = f'failed to parse the version "{parts[2]}"'
                 return None, msg
@@ -65,7 +72,7 @@ def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
 def detect_compiler(fname: str) -> Tuple[StrOrNone, str]:
     gcc_version = None
     clang_version = None
 def detect_compiler(fname: str) -> Tuple[StrOrNone, str]:
     gcc_version = None
     clang_version = None
-    with _open(fname, 'rt', encoding='utf-8') as f:
+    with _open(fname) as f:
         for line in f.readlines():
             if line.startswith('CONFIG_GCC_VERSION='):
                 gcc_version = line[19:-1]
         for line in f.readlines():
             if line.startswith('CONFIG_GCC_VERSION='):
                 gcc_version = line[19:-1]
@@ -109,12 +116,12 @@ def print_checklist(mode: StrOrNone, checklist: List[ChecklistObjType], with_res
                 ok_count += 1
                 if mode == 'show_fail':
                     continue
                 ok_count += 1
                 if mode == 'show_fail':
                     continue
-            elif opt.result.startswith('FAIL'):
+            else:
+                assert(opt.result.startswith('FAIL')), \
+                       f'unexpected result "{opt.result}" of {opt.name} check'
                 fail_count += 1
                 if mode == 'show_ok':
                     continue
                 fail_count += 1
                 if mode == 'show_ok':
                     continue
-            else:
-                assert(False), f'unexpected result "{opt.result}" of {opt.name} check'
         opt.table_print(mode, with_results)
         print()
         if mode == 'verbose':
         opt.table_print(mode, with_results)
         print()
         if mode == 'verbose':
@@ -133,7 +140,7 @@ def print_checklist(mode: StrOrNone, checklist: List[ChecklistObjType], with_res
 
 
 def parse_kconfig_file(_mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
 
 
 def parse_kconfig_file(_mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
-    with _open(fname, 'rt', encoding='utf-8') as f:
+    with _open(fname) as f:
         opt_is_on = re.compile(r"CONFIG_[a-zA-Z0-9_]+=.+$")
         opt_is_off = re.compile(r"# CONFIG_[a-zA-Z0-9_]+ is not set$")
 
         opt_is_on = re.compile(r"CONFIG_[a-zA-Z0-9_]+=.+$")
         opt_is_off = re.compile(r"# CONFIG_[a-zA-Z0-9_]+ is not set$")
 
@@ -162,8 +169,14 @@ def parse_kconfig_file(_mode: StrOrNone, parsed_options: Dict[str, str], fname:
 
 
 def parse_cmdline_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
 
 
 def parse_cmdline_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
+    if not os.path.isfile(fname):
+        sys.exit(f'[!] ERROR: unable to open {fname}, are you sure it exists?')
+
     with open(fname, 'r', encoding='utf-8') as f:
         line = f.readline()
     with open(fname, 'r', encoding='utf-8') as f:
         line = f.readline()
+        if not line:
+            sys.exit(f'[!] ERROR: empty "{fname}"')
+
         opts = line.split()
 
         line = f.readline()
         opts = line.split()
 
         line = f.readline()
@@ -184,6 +197,9 @@ def parse_cmdline_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: s
 
 
 def parse_sysctl_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
 
 
 def parse_sysctl_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
+    if not os.path.isfile(fname):
+        sys.exit(f'[!] ERROR: unable to open {fname}, are you sure it exists?')
+
     with open(fname, 'r', encoding='utf-8') as f:
         sysctl_pattern = re.compile(r"[a-zA-Z0-9/\._-]+ =.*$")
         for line in f.readlines():
     with open(fname, 'r', encoding='utf-8') as f:
         sysctl_pattern = re.compile(r"[a-zA-Z0-9/\._-]+ =.*$")
         for line in f.readlines():