diff --git a/src/plugins/analysis/init_systems/code/init_system.py b/src/plugins/analysis/init_systems/code/init_system.py index 5c1587f2d..273d6737f 100644 --- a/src/plugins/analysis/init_systems/code/init_system.py +++ b/src/plugins/analysis/init_systems/code/init_system.py @@ -1,155 +1,190 @@ +from __future__ import annotations + import re +from enum import Enum +from pathlib import Path +from typing import TYPE_CHECKING, Optional, Union -from analysis.PluginBase import AnalysisBasePlugin -from helperFunctions.data_conversion import make_unicode_string -from objects.file import FileObject +from pydantic import BaseModel, Field +from semver import Version -FILE_IGNORES = ['README', 'README.md', 'README.txt', 'INSTALL', 'VERSION'] +from analysis.plugin import AnalysisPluginV0 +if TYPE_CHECKING: + from io import FileIO -class AnalysisPlugin(AnalysisBasePlugin): - """ - This Plugin searches for Init-Scripts and lists the Services or Script-Files - It displays a short description (if provided) or else the filename +FILE_IGNORES = {'README', 'README.md', 'README.txt', 'INSTALL', 'VERSION'} - Credits: - Original version by Stefan Viergutz created during Firmware Bootcamp WT16/17 at University of Bonn - Refactored and improved by Fraunhofer FKIE - """ - NAME = 'init_systems' - DESCRIPTION = 'detect and analyze auto start services' - DEPENDENCIES = ['file_type'] # noqa: RUF012 - VERSION = '0.4.2' - FILE = __file__ +class InitType(str, Enum): + init_tab = 'inittab' + initscript = 'initscript' + rc = 'rc' + runit = 'RunIt' + sys_v_init = 'SysVInit' + systemd = 'SystemD' + upstart = 'UpStart' - def additional_setup(self): - self.content = None - @staticmethod - def _is_text_file(file_object): - return file_object.processed_analysis['file_type']['result']['mime'] in ['text/plain'] +class SystemDData(BaseModel): + exec_start: Optional[str] = None + description: Optional[str] = None - @staticmethod - def _get_file_path(file_object: FileObject): - return list(file_object.virtual_file_path.values())[0][0] - - def _get_systemd_config(self, file_object): - result = {} - match_description = self._findall_regex(r'(?:Description=)(.*)', self.content) - match_exec = self._findall_regex(r'(?<=ExecStart=).*', self.content) - if match_exec: - result['ExecStart'] = '\n'.join(match_exec) - description = match_description if match_description else [] - description = self._add_quotes(description) - result['description'] = description if description else [file_object.file_name] - result['init_type'] = ['SystemD'] - result['summary'] = result['init_type'] - return result - - def _get_rc_config(self, _): - result = {} - matches = self._findall_regex(r'^(?!#)(.+)', self.content) - if matches: - result['script'] = '\n'.join(matches) - result['init_type'] = ['rc'] - result['summary'] = result['init_type'] - return result - - def _get_inittab_config(self, _): - result = {} - matches_sysinit = self._findall_regex(r'^[^#].*(?<=sysinit:)([^#].*)', self.content) - matches_respawn = self._findall_regex(r'^[^#].*(?<=respawn:)([^#].*)', self.content) - all_matches = [] - all_matches.extend(list(matches_sysinit)) - all_matches.extend(list(matches_respawn)) - if all_matches: - result['inittab'] = '\n'.join(all_matches) - result['init_type'] = ['inittab'] - result['summary'] = result['init_type'] - return result - - def _get_initscript_config(self, _): - result = {} - matches = self._findall_regex(r'^(?!#)(.+)', self.content) - if matches: - result['script'] = '\n'.join(matches) - result['init_type'] = ['initscript'] - result['summary'] = result['init_type'] - return result - - def _get_upstart_config(self, file_object): - result = {} - match_description = self._findall_regex(r'^[^#].*(?<=description)\s*(.*)', self.content) - match_exec = self._findall_regex(r'[^#]^exec\s*((?:.*\\\n)*.*)', self.content) - match_pre_start = self._findall_regex( - r'(?<=pre-start script\n)(?:(?:[\S\s]*?)[\n]*)(?=\nend script)', self.content + +class InitTabData(BaseModel): + sysinit: Optional[str] = None + respawn: Optional[str] = None + + +class UpstartData(BaseModel): + exec: Optional[str] = None + pre_start: Optional[str] = None + description: Optional[str] = None + + +class SysVInitData(BaseModel): + description: Optional[str] = None + short_description: Optional[str] = None + + +class AnalysisPlugin(AnalysisPluginV0): + class Schema(BaseModel): + init_type: Optional[InitType] = Field( + None, description='The type of init system that was identified for this file' + ) + data: Optional[Union[SystemDData, InitTabData, UpstartData, SysVInitData]] = Field( + None, + description='Optional meta information and init data contained in this init script', + ) + is_init: bool = False + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, value): + init_type = value.get('init_type') + if init_type == InitType.systemd: + value['data'] = SystemDData(**value['data']) + elif init_type == InitType.init_tab: + value['data'] = InitTabData(**value['data']) + elif init_type == InitType.upstart: + value['data'] = UpstartData(**value['data']) + elif init_type == InitType.sys_v_init: + value['data'] = SysVInitData(**value['data']) + return cls(**value) + + def __init__(self): + super().__init__( + metadata=( + self.MetaData( + name='init_systems', + mime_whitelist=['text/plain'], + description='detect and analyze initialization scripts', + version=Version(1, 0, 0), + Schema=self.Schema, + ) + ) ) - match_script = self._findall_regex(r'(?<=^script\n)(?:(?:[\S\s]*?)[\n]*)(?=\nend script)', self.content) - result['description'] = match_description if match_description else [file_object.file_name] - if match_exec: - result['exec'] = '\n'.join(match_exec) - if match_pre_start: - result['pre-start'] = '\n'.join(match_pre_start) - if match_script: - result['script'] = '\n'.join(match_script) - result['init_type'] = ['UpStart'] - result['summary'] = result['init_type'] - return result - - def _get_runit_config(self, file_object): - # TODO description = filepath - result = {} - match_exec = self._findall_regex(r'^([^#](?:.*\\\n)*.*)', self.content) - if match_exec: - result['script'] = '\n'.join(match_exec) - result['description'] = [file_object.file_name] - result['init_type'] = ['RunIt'] - result['summary'] = result['init_type'] - return result - - def _get_sysvinit_config(self, file_object): - result = {} - match_desc1 = self._findall_regex(r'Short-Description:\s*(.*)', self.content) - match_desc2 = self._findall_regex(r'DESC=\"*([^\"|\n]*)', self.content) - matches = self._findall_regex(r'^(?!#)(.+)', self.content) - description = match_desc1 if match_desc1 else match_desc2 if match_desc2 else [] - description_formatted = self._add_quotes(description) - result['description'] = description_formatted if description_formatted else [file_object.file_name] - if matches: - result['script'] = '\n'.join(matches) - result['init_type'] = ['rc'] - result['init_type'] = ['SysVInit'] - result['summary'] = result['init_type'] - return result - - def process_object(self, file_object): - if self._is_text_file(file_object) and (file_object.file_name not in FILE_IGNORES): - file_path = self._get_file_path(file_object) - self.content = make_unicode_string(file_object.binary) - if '/inittab' in file_path: - file_object.processed_analysis[self.NAME] = self._get_inittab_config(file_object) - if 'systemd/system/' in file_path: - file_object.processed_analysis[self.NAME] = self._get_systemd_config(file_object) - if file_path.endswith(('etc/rc', 'etc/rc.local', 'etc/rc.firsttime', 'etc/rc.securelevel')): - file_object.processed_analysis[self.NAME] = self._get_rc_config(file_object) - if file_path.endswith('etc/initscript'): - file_object.processed_analysis[self.NAME] = self._get_initscript_config(file_object) - if 'etc/init/' in file_path or 'etc/event.d/' in file_path: - file_object.processed_analysis[self.NAME] = self._get_upstart_config(file_object) - if 'etc/service/' in file_path or 'etc/sv/' in file_path: - file_object.processed_analysis[self.NAME] = self._get_runit_config(file_object) - if 'etc/init.d/' in file_path or 'etc/rc.d/' in file_path: - file_object.processed_analysis[self.NAME] = self._get_sysvinit_config(file_object) - else: - file_object.processed_analysis[self.NAME] = {'summary': []} - return file_object - @staticmethod - def _findall_regex(pattern, content): - regex_compiled = re.compile(pattern, re.MULTILINE) - return regex_compiled.findall(content) + SYSTEMD_EXECSTART_REGEX = re.compile(r'ExecStart=(.*)') + SYSTEMD_DESCRIPTION_REGEX = re.compile(r'Description=(.*)') + + def _get_systemd_config(self, file_handle: FileIO) -> Schema: + content = file_handle.read().decode(errors='ignore') + return self.Schema( + is_init=True, + init_type=InitType.systemd, + data=SystemDData( + exec_start=_match(content, self.SYSTEMD_EXECSTART_REGEX), + description=_match(content, self.SYSTEMD_DESCRIPTION_REGEX), + ), + ) + + INITTAB_SYSINIT_REGEX = re.compile(r'^[^#].*(?<=sysinit:)([^#].*)', re.MULTILINE) + INITTAB_RESPAWN_REGEX = re.compile(r'^[^#].*(?<=respawn:)([^#].*)', re.MULTILINE) + + def _get_inittab_config(self, file_handle: FileIO) -> Schema: + content = file_handle.read().decode(errors='ignore') + return self.Schema( + is_init=True, + init_type=InitType.init_tab, + data=InitTabData( + sysinit=_match(content, self.INITTAB_SYSINIT_REGEX), + respawn=_match(content, self.INITTAB_RESPAWN_REGEX), + ), + ) + + UPSTART_DESCRIPTION_REGEX = re.compile(r'^[^#].*(?<=description)\s*(.*)', re.MULTILINE) + UPSTART_EXEC_REGEX = re.compile(r'[^#]^exec\s*((?:.*\\\n)*.*)', re.MULTILINE) + UPSTART_PRESTART_REGEX = re.compile(r'(?<=pre-start script\n)[\S\s]*?\n*(?=\nend script)', re.MULTILINE) + + def _get_upstart_config(self, file_handle: FileIO) -> Schema: + content = file_handle.read().decode(errors='ignore') + return self.Schema( + is_init=True, + init_type=InitType.upstart, + data=UpstartData( + description=_match(content, self.UPSTART_DESCRIPTION_REGEX), + exec=_match(content, self.UPSTART_EXEC_REGEX), + pre_start=_match(content, self.UPSTART_PRESTART_REGEX), + ), + ) + + SYSVINIT_SHORT_DESC_REGEX = re.compile(r'Short-Description:\s*(.*)', re.MULTILINE) + SYSVINIT_DESC_REGEX = re.compile(r'DESC=\"*([^\"|\n]*)', re.MULTILINE) + + def _get_sysvinit_config(self, file_handle: FileIO) -> Schema: + content = file_handle.read().decode(errors='ignore') + return self.Schema( + is_init=True, + init_type=InitType.sys_v_init, + data=SysVInitData( + description=_match(content, self.SYSVINIT_DESC_REGEX), + short_description=_match(content, self.SYSVINIT_SHORT_DESC_REGEX), + ), + ) + + def analyze(self, file_handle: FileIO, virtual_file_path: dict, analyses: dict[str, BaseModel | dict]) -> Schema: + del analyses + file_path = list(virtual_file_path.values())[0][0] + if Path(file_path).name not in FILE_IGNORES: + result = self._get_script_type_from_path(file_path, file_handle) + if result.is_init and not self._has_no_content(file_handle): + return result + return self.Schema(is_init=False) + + def _get_script_type_from_path(self, file_path: str, file_handle: FileIO) -> Schema: # noqa: PLR0911 + if '/inittab' in file_path: + return self._get_inittab_config(file_handle) + if 'systemd/system/' in file_path: + return self._get_systemd_config(file_handle) + if file_path.endswith(('etc/rc', 'etc/rc.local', 'etc/rc.firsttime', 'etc/rc.securelevel')): + return self.Schema(is_init=True, init_type=InitType.rc) + if file_path.endswith('etc/initscript'): + return self.Schema(is_init=True, init_type=InitType.initscript) + if 'etc/init/' in file_path or 'etc/event.d/' in file_path: + return self._get_upstart_config(file_handle) + if 'etc/service/' in file_path or 'etc/sv/' in file_path: + return self.Schema(is_init=True, init_type=InitType.runit) + if 'etc/init.d/' in file_path or 'etc/rc.d/' in file_path: + return self._get_sysvinit_config(file_handle) + return self.Schema(is_init=False) + + def summarize(self, result: Schema) -> list[str]: + if result.is_init and result.init_type: + return [result.init_type] + return [] @staticmethod - def _add_quotes(unquoted_list): - return [f'"{element}"' for element in unquoted_list] + def _has_no_content(file_handle: FileIO) -> bool: + file_handle.seek(0) + content = file_handle.read().decode(errors='ignore') + return all(line.startswith('#') for line in content.splitlines() if line) + + +def _match(content: str, regex: re.Pattern) -> str | None: + if match := regex.findall(content): + return '\n'.join(match) + return None diff --git a/src/plugins/analysis/init_systems/test/test_plugin_init_system.py b/src/plugins/analysis/init_systems/test/test_plugin_init_system.py index 640522838..bfb3a23eb 100644 --- a/src/plugins/analysis/init_systems/test/test_plugin_init_system.py +++ b/src/plugins/analysis/init_systems/test/test_plugin_init_system.py @@ -1,154 +1,106 @@ -import os -from copy import deepcopy +from io import FileIO +from pathlib import Path import pytest -from common_helper_files import get_dir_of_file -from objects.file import FileObject from plugins.analysis.init_systems.code.init_system import AnalysisPlugin -_test_init_dir = os.path.join(get_dir_of_file(__file__), 'data') # noqa: PTH118 - - -def _get_fo(path): - fo = FileObject(file_path=os.path.join(_test_init_dir, path)) # noqa: PTH118 - fo.processed_analysis['file_type'] = {'result': {'mime': 'text/plain'}} - fo.root_uid = fo.uid - fo.virtual_file_path = {'parent_uid': [path]} - return fo +TEST_FILES_DIR = Path(__file__).parent / 'data' +TEST_FILES = { + 'systemd': 'etc/systemd/system/foobar', + 'inittab': 'etc/inittab', + 'rclocal': 'etc/rc.local', + 'upstart': 'etc/init/baz.conf', + 'runit': 'etc/service/lighttpd/run', + 'runit_symlink': 'etc/service/example/run', + 'runit_origin': 'etc/sv/example/run', + 'only_comments': 'etc/inittab.invalid', + 'initd': 'etc/init.d/skeleton', + 'README': 'etc/init.d/README', + 'initscript': 'etc/initscript', +} @pytest.mark.AnalysisPluginTestConfig(plugin_class=AnalysisPlugin) class TestAnalysisPluginInit: - test_file_not_text = FileObject(file_path=f'{_test_init_dir}/etc/systemd/system/foobar') - test_file_not_text.processed_analysis['file_type'] = {'result': {'mime': 'application/zip'}} # noqa: RUF012 - - test_files = { # noqa: RUF012 - 'systemd': 'etc/systemd/system/foobar', - 'inittab': 'etc/inittab', - 'rclocal': 'etc/rc.local', - 'upstart': 'etc/init/baz.conf', - 'runit': 'etc/service/lighttpd/run', - 'runit_symlink': 'etc/service/example/run', - 'runit_origin': 'etc/sv/example/run', - 'only_comments': 'etc/inittab.invalid', - 'initd': 'etc/init.d/skeleton', - 'README': 'etc/init.d/README', - 'initscript': 'etc/initscript', - } - test_fos = {f'test_file_{test_file}': _get_fo(path) for test_file, path in test_files.items()} # noqa: RUF012 - - def test_root_uid_is_none(self, analysis_plugin): - fo = deepcopy(self.test_fos['test_file_initd']) - fo.root_uid = None - fo.parent_firmware_uids = set(fo.virtual_file_path) - processed_file = analysis_plugin.process_object(fo) - # missing fo.root_uid should not break the analysis - assert processed_file.processed_analysis[analysis_plugin.NAME]['summary'] != [] + @staticmethod + def _analyze_test_file(analysis_plugin, test_file: str): + path = TEST_FILES[test_file] + result = analysis_plugin.analyze(FileIO(TEST_FILES_DIR / path), {'parent_uid': [path]}, {}) + summary = analysis_plugin.summarize(result) + return result, summary def test_get_systemd_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_systemd']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'systemd') - assert '/usr/sbin/foobar -n' in result['ExecStart'], 'record not found' - assert '[Unit]' not in result['ExecStart'], '[Unit] should not be listed' - assert 'Description=Foo Bar Service' not in result['description'], 'record not sanitized' - assert ['"Foo Bar Service"'] == result['description'], 'description missing' - assert ['SystemD'] == result['init_type'], 'init type missing' - assert ['SystemD'] == result['summary'], 'record not found in summary' + assert result.data is not None + assert '/usr/sbin/foobar -n' in result.data.exec_start, 'ExecStart record not found' + assert '[Unit]' not in result.data.exec_start, '[Unit] should not be listed' + assert 'Description=' not in result.data.description, 'record not sanitized' + assert result.data.description == 'Foo Bar Service', 'description missing' + assert result.init_type == 'SystemD', 'init type missing' + assert summary == ['SystemD'], 'record not found in summary' def test_get_rc_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_rclocal']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'rclocal') - assert ( - '/usr/bin/foo # ein Programm\n/usr/local/bin/bar.sh # ein Shellskript\n/etc/init.d/foobar start # ein Dienst\nexit 0' # noqa: SIM300, E501 - == result['script'] - ), 'record not found' - assert '#!/bin/sh -e' not in result['script'], 'Comments should not be listed' - assert ['rc'] == result['init_type'], 'init type missing' - assert ['rc'] == result['summary'], 'init type missing' + assert result.init_type == 'rc', 'init type missing' + assert summary == ['rc'], 'init type missing' def test_get_inittab_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_inittab']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'inittab') - assert '/etc/init.d/rcS' in result['inittab'], 'record not found' - assert '/sbin/getty -L 9600 ttyS0 vt320' in result['inittab'], 'record not found' - assert ['inittab'] == result['init_type'], 'init type missing' - assert ['inittab'] == result['summary'], 'record not found in summary' + assert '/etc/init.d/rcS' in result.data.sysinit, 'record not found' + assert '/sbin/getty -L 9600 ttyS0 vt320' in result.data.respawn, 'record not found' + assert result.init_type == 'inittab', 'init type missing' + assert summary == ['inittab'], 'record not found in summary' def test_get_initscript_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_initscript']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'initscript') - assert ['initscript'] == result['init_type'], 'init type missing' - assert ['initscript'] == result['summary'], 'record not found in summary' + assert result.init_type == 'initscript', 'init type missing' + assert summary == ['initscript'], 'record not found in summary' def test_get_upstart_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_upstart']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'upstart') - assert result['pre-start'] == ' echo "[`date`] baz starting..." >> /var/log/baz.log', 'record not found' - assert '/bin/baz.sh -runonce \\\n-silent' in result['exec'], 'record not found' - assert 'script' not in result['script'], 'script should not be listed' - assert ['"Simple Baz application"'] == result['description'], 'description missing' - assert ['UpStart'] == result['init_type'], 'init type missing' - assert ['UpStart'] == result['summary'], 'description missing' + assert result.data.pre_start == ' echo "[`date`] baz starting..." >> /var/log/baz.log', 'record not found' + assert '/bin/baz.sh -runonce \\\n-silent' in result.data.exec, 'record not found' + assert result.data.description == '"Simple Baz application"', 'description missing' + assert result.init_type == 'UpStart', 'init type missing' + assert summary == ['UpStart'], 'description missing' def test_get_runit_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_runit']) - result = processed_file.processed_analysis[analysis_plugin.NAME] - - assert result['script'] == 'sv -w7 check postgresql\nexec 2>&1 myprocess \\\nlast line', 'record not found' - assert 'exec 2>&1 myprocess \\\nlast line' in result['script'], 'record not found' - assert '#!/bin/sh -e' not in result['script'], 'should not be listed' - assert ['RunIt'] == result['init_type'], 'init type missing' - assert ['RunIt'] == result['summary'], 'description missing' - - processed_file2 = analysis_plugin.process_object(self.test_fos['test_file_runit_symlink']) - result2 = processed_file2.processed_analysis[analysis_plugin.NAME] - assert 'exec chpst -u foo /opt/example/foo-service.sh' in result2['script'], 'record not found' - assert ['RunIt'] == result['init_type'], 'init type missing' - assert ['RunIt'] == result2['summary'], 'description missing' - - processed_file3 = analysis_plugin.process_object(self.test_fos['test_file_runit_origin']) - result3 = processed_file3.processed_analysis[analysis_plugin.NAME] - assert 'exec chpst -u foo /opt/example/foo-service.sh' in result3['script'], 'record not found' - assert ['RunIt'] == result['init_type'], 'init type missing' - assert ['RunIt'] == result3['summary'], 'description missing' + result, summary = self._analyze_test_file(analysis_plugin, 'runit') - def test_get_sysvinit_config(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_initd']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + assert result.init_type == 'RunIt', 'init type missing' + assert summary == ['RunIt'], 'description missing' - assert ['"Example initscript"'] == result['description'], 'description missing' - assert ( - 'if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then\n set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script\nfi' # noqa: E501 - in result['script'] - ), 'record not found' - assert ['SysVInit'] == result['init_type'], 'init type missing' - assert ['SysVInit'] == result['summary'], 'description missing' + def test_get_runit_config2(self, analysis_plugin): + result, summary = self._analyze_test_file(analysis_plugin, 'runit_symlink') + assert result.init_type == 'RunIt', 'init type missing' + assert summary == ['RunIt'], 'description missing' - def test_get_not_text_file(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_file_not_text) - result = processed_file.processed_analysis[analysis_plugin.NAME] + def test_get_runit_config3(self, analysis_plugin): + result, summary = self._analyze_test_file(analysis_plugin, 'runit_origin') + assert result.init_type == 'RunIt', 'init type missing' + assert summary == ['RunIt'], 'description missing' - assert [] == result['summary'], 'should be empty summary' + def test_get_sysvinit_config(self, analysis_plugin): + result, summary = self._analyze_test_file(analysis_plugin, 'initd') + + assert result.data.short_description == 'Example initscript', 'short description missing' + assert result.data.description == 'Description of the service', 'description missing' + assert result.init_type == 'SysVInit', 'init type missing' + assert summary == ['SysVInit'], 'description missing' def test_readme_file(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_README']) - result = processed_file.processed_analysis[analysis_plugin.NAME] + result, summary = self._analyze_test_file(analysis_plugin, 'README') - assert [] == result['summary'], 'should be empty summary' + assert result.is_init is False + assert summary == [], 'should be empty summary' def test_only_comments_file(self, analysis_plugin): - processed_file = analysis_plugin.process_object(self.test_fos['test_file_only_comments']) - result = processed_file.processed_analysis[analysis_plugin.NAME] - - assert {} == result, 'should be empty for comments only in file' - - def test_add_quotes(self, analysis_plugin): - unquoted = ['test', '2'] + result, _ = self._analyze_test_file(analysis_plugin, 'only_comments') - assert ['"test"', '"2"'] == analysis_plugin._add_quotes(unquoted), 'strings should be in double quotes' + assert result.is_init is False, 'should be empty for comments only in file' diff --git a/src/plugins/analysis/init_systems/view/init_system.html b/src/plugins/analysis/init_systems/view/init_system.html index cadb65ca6..e7719e79f 100644 --- a/src/plugins/analysis/init_systems/view/init_system.html +++ b/src/plugins/analysis/init_systems/view/init_system.html @@ -2,25 +2,22 @@ {% block analysis_result_details %} - {% if analysis_result['init_type'] %} + {% if analysis_result.is_init %} - Init system - {{ analysis_result['init_type'][0]}} + Init system + {{ analysis_result.init_type }} {% endif %} - {% if analysis_result['description'] %} - - Description - {{ analysis_result['description'][0]}} - + {% if analysis_result.data %} + {% for key, value in analysis_result.data.items() | sort %} + + {{ key }} + +
{{ value }}
+ + + {% endfor %} {% endif %} - {% for key, value in analysis_result.items() | sort %} - - {{ key }} - -
{{ value }}
- - - {% endfor %} {% endblock %}