From 39e83d23363c664435c1050afb1862a6780c38fc Mon Sep 17 00:00:00 2001 From: geoffrey Date: Sun, 11 Jun 2023 20:19:40 +0200 Subject: [PATCH] Change arbo and create config file --- .gitignore | 2 + {issues => audit}/__init__.py | 0 .../application/plugins/calls}/calls.py | 0 audit/system/plugins/postfix/__init__.py | 1 + .../system/plugins/postfix/parsing.py | 0 .../system/plugins/postfix}/postfix.py | 0 audit/system/plugins/sysctl/__init__.py | 1 + .../system/plugins/sysctl/parsing.py | 1 - .../system/plugins/sysctl}/sysctl.py | 0 config.py | 6 - config.yaml | 15 +++ core/config.py | 103 ++++++++++++++++++ core/dispatcher.py | 20 ++++ core/main.py | 65 +++++++++++ core/postfix.py | 10 +- report.py => core/report.py | 36 +++--- core/sysctl.py | 9 +- design_pattern.txt | 1 + issues/system.py | 6 - main.py | 36 +----- parsing.py | 41 ------- requirements.txt | 2 + utils.py | 8 ++ 23 files changed, 252 insertions(+), 111 deletions(-) rename {issues => audit}/__init__.py (100%) rename {issues => audit/application/plugins/calls}/calls.py (100%) create mode 100644 audit/system/plugins/postfix/__init__.py rename parsing/postfix.py => audit/system/plugins/postfix/parsing.py (100%) rename {issues => audit/system/plugins/postfix}/postfix.py (100%) create mode 100644 audit/system/plugins/sysctl/__init__.py rename parsing/sysctl.py => audit/system/plugins/sysctl/parsing.py (98%) rename {issues => audit/system/plugins/sysctl}/sysctl.py (100%) delete mode 100644 config.py create mode 100644 config.yaml create mode 100644 core/config.py create mode 100644 core/dispatcher.py create mode 100644 core/main.py rename report.py => core/report.py (57%) create mode 100644 design_pattern.txt delete mode 100644 issues/system.py delete mode 100644 parsing.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 8ac3823..83e6903 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__/ __pycache__/** **.swp +reports/ +reports/** diff --git a/issues/__init__.py b/audit/__init__.py similarity index 100% rename from issues/__init__.py rename to audit/__init__.py diff --git a/issues/calls.py b/audit/application/plugins/calls/calls.py similarity index 100% rename from issues/calls.py rename to audit/application/plugins/calls/calls.py diff --git a/audit/system/plugins/postfix/__init__.py b/audit/system/plugins/postfix/__init__.py new file mode 100644 index 0000000..e5a0d9b --- /dev/null +++ b/audit/system/plugins/postfix/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff --git a/parsing/postfix.py b/audit/system/plugins/postfix/parsing.py similarity index 100% rename from parsing/postfix.py rename to audit/system/plugins/postfix/parsing.py diff --git a/issues/postfix.py b/audit/system/plugins/postfix/postfix.py similarity index 100% rename from issues/postfix.py rename to audit/system/plugins/postfix/postfix.py diff --git a/audit/system/plugins/sysctl/__init__.py b/audit/system/plugins/sysctl/__init__.py new file mode 100644 index 0000000..e5a0d9b --- /dev/null +++ b/audit/system/plugins/sysctl/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff --git a/parsing/sysctl.py b/audit/system/plugins/sysctl/parsing.py similarity index 98% rename from parsing/sysctl.py rename to audit/system/plugins/sysctl/parsing.py index 0b3d7f6..3a8be2f 100644 --- a/parsing/sysctl.py +++ b/audit/system/plugins/sysctl/parsing.py @@ -78,7 +78,6 @@ class Parsing(ParsingBase): def _generateReport(self, objects): # We can generate the report for sysctl in self._reports['file']['sysctl']: - #self._reports['file']['sysctl'][sysctl] = vulnerabilityFound[sysctl] self._reports['file']['sysctl'][sysctl] = objects[sysctl] def _parsingFile(self, line, obj, vulnerabilityFound) -> bool: diff --git a/issues/sysctl.py b/audit/system/plugins/sysctl/sysctl.py similarity index 100% rename from issues/sysctl.py rename to audit/system/plugins/sysctl/sysctl.py diff --git a/config.py b/config.py deleted file mode 100644 index 656da7c..0000000 --- a/config.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python3 - -# Constantes -HIGH = "high" -MEDIUM = "medium" -LOW = "low" diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..a6c5854 --- /dev/null +++ b/config.yaml @@ -0,0 +1,15 @@ +# Audit system +system: + exclude_plugins: + - "" + postfix: + postfix_file: "/etc/postfix/master.cf" + sysctl: + sysctl_file: "/etc/sysctl.conf" + +# Audit application +application: + pattern_file: + - ".py" + - ".php" + - ".c" diff --git a/core/config.py b/core/config.py new file mode 100644 index 0000000..a56327c --- /dev/null +++ b/core/config.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +import yaml +from utils import ConfigError + +# Constantes +HIGH = "high" +MEDIUM = "medium" +LOW = "low" + +AUDIT_SYSTEM = [ + "sysctl", + "postfix", +] + +AUDIT_APPLICATION = [ + 'keywords', + 'calls', +] + +def generateConfig() -> dict: + config = dict() + # System + config["system"] = dict() + config["system"]["postfix"] = dict() + config["system"]["postfix"]["postfix_file"] = "/etc/postfix/main.cf" + config["system"]["sysctl"] = dict() + config["system"]["sysctl"]["sysctl_file"] = "/etc/sysctl.conf" + config["system"]["exclude_plugins"] = list() + # Application + config["application"] = dict() + config["application"]["pattern_file"] = list() + + return config + +def _get_exclude_plugins(): + pass + +def parsingConfigFile(filename, configs): + # This function overwrite the config + try: + if not filename.endswith(".yaml"): + raise ConfigError( + "You must specified a YAML config file", + filename + ) + with open(filename, 'rb') as f: + yamlConfig = yaml.safe_load(f) + + # Mapping config file to the config dict + # TODO: recursive function + for category in yamlConfig: + if "system" in category: + for plugin in yamlConfig["system"]: + if plugin not in configs["system"]: + raise ConfigError( + f"{plugin} unknown", + filename + ) + for flag in yamlConfig["system"][plugin]: + try: + if flag is not None: + if isinstance(configs["system"][plugin], list): + configs["system"][plugin].append(flag) + else: + if flag not in configs["system"][plugin]: + raise ConfigError( + f"{flag} unknown", + filename + ) + configs["system"][plugin][flag] = yamlConfig["system"][plugin][flag] + except TypeError as e: + raise e + + #if "application" in category: + # for plugin in yamlConfig["application"]: + # for flag in yamlConfig["application"][plugin]: + # try: + # configs["application"][plugin][flag] = yamlConfig["application"][plugin][flag] + # except TypeError: + # pass + + + #fdata = f.read() + #lines = fdata.splitlines() + + #for line in lines: + # line = line.decode('utf-8') + # try: + # sLine = line.split("=") + # flag = sLine[0].strip() + # value = sLine[1].strip() + + # if flag in configs: + # if flag == "exclude_plugins": + # value = value.replace("\"", "") + # value = value.split(",") + # configs[flag] = value + # except IndexError: + # pass + + except FileNotFoundError: + print(f"Config file {filename} not found. Bypass it") diff --git a/core/dispatcher.py b/core/dispatcher.py new file mode 100644 index 0000000..75b2c6a --- /dev/null +++ b/core/dispatcher.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +class Dispatcher: + _plugins = dict() + + def __init__(self) -> None: + pass + + def runPlugin(self, plugin, *args) -> dict: + """ + We run the puglin. The result of this function + is the report of the audit + """ + return self._plugins[plugin](self, *args) + + @classmethod + def register_plugins(cls, plugin): + cls._plugins[plugin.__name__] = plugin + return plugin + diff --git a/core/main.py b/core/main.py new file mode 100644 index 0000000..f2be5f5 --- /dev/null +++ b/core/main.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +from argparse import ArgumentParser +from core.sysctl import Sysctl +from core.postfix import Postfix +from core.report import generateHtmlReport +from core.config import AUDIT_SYSTEM, AUDIT_APPLICATION, generateConfig, parsingConfigFile +from core.dispatcher import Dispatcher + + +def checkArguments(): + args = ArgumentParser(description="Check Gitlab repositories") + args.add_argument('-a', '--audit', help="Kind of audit", choices=['system', 'application']) + args.add_argument('-c', '--config', help="Config file") + return args.parse_args() + + +def main(): + args = checkArguments() + + # If audit is not specified + if args.audit is None: + print("Please, you must specify the audit type") + exit(1) + + # If config file is specified + configs = generateConfig() + if args.config is not None: + parsingConfigFile(args.config, configs) + + # Report + report = dict() + report['system'] = dict() + + # Create our dispatcher + dispatcher = Dispatcher() + + print(configs) + + if args.audit == "system": + for audit in AUDIT_SYSTEM: + if audit not in configs["system"]["exclude_plugins"]: + report["system"][audit] = dispatcher.runPlugin(audit, configs["system"][audit]) + + if args.audit == "application": + pass + + print(report) + generateHtmlReport(report) + +@Dispatcher.register_plugins +def sysctl(*args) -> dict: + sysctl = Sysctl(args[1]) + sysctl.runAudit() + return sysctl.getReports() + +@Dispatcher.register_plugins +def postfix(*args) -> dict: + arguments = args[1] + postfix = Postfix() + postfix.runAudit() + return postfix.getReports() + +if __name__ == "__main__": + main() diff --git a/core/postfix.py b/core/postfix.py index 037fa35..9cedf9d 100644 --- a/core/postfix.py +++ b/core/postfix.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -from parsing.postfix import Parsing -from issues.postfix import postfix +from audit.system.plugins.postfix.parsing import Parsing +from audit.system.plugins.postfix.postfix import postfix class Postfix: def __init__(self): @@ -14,3 +14,9 @@ class Postfix: def _postfix(self): self._objects = postfix() + + def runAudit(self): + print("Running test for postfix") + + def getReports(self) -> dict: + return self._reports diff --git a/report.py b/core/report.py similarity index 57% rename from report.py rename to core/report.py index 66c1c5a..2b7b918 100644 --- a/report.py +++ b/core/report.py @@ -4,27 +4,24 @@ from datetime import datetime def generateHtmlReport(data): today = datetime.now().isoformat()[0:10].replace("-", "_") - html = "" \ - "" \ - "" \ - "" \ - "" \ + html = _getHeader() + html += "" \ f"

Reports of {today}

" body = str() # For sysctl - for entry in data['sysctl']: - body += f"

Sysctl

" + #for entry in data['sysctl']: + # body += f"

Sysctl

" - # For file - body += f"

File

" - for f in data['sysctl']['file']: - body += f"

{data['sysctl']['file']['filename']}

" - for vul in data['sysctl']['file']['sysctl']: - #print(data['sysctl']['file']['sysctl'][vul]) - body += f"
{vul}
" - body += f"

" - body += f"Results:
" + # # For file + # body += f"

File

" + #for f in data['sysctl']['file']: + # body += f"

{data['sysctl']['file']['filename']}

" + # for vul in data['sysctl']['file']['sysctl']: + # #print(data['sysctl']['file']['sysctl'][vul]) + # body += f"
{vul}
" + # body += f"

" + # body += f"Results:
" #for result in data['sysctl']['file']['sysctl'][vul]: # print(result) # body += f"Line: {result['lineNumber']}
" @@ -39,3 +36,10 @@ def generateHtmlReport(data): with open(f"reports/reports_{today}.html", "w") as f: f.write(html) +def _getHeader() -> str: + header = "" \ + "" \ + "" \ + "" \ + + return header diff --git a/core/sysctl.py b/core/sysctl.py index 6986862..7c6f74e 100644 --- a/core/sysctl.py +++ b/core/sysctl.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 -from parsing.sysctl import Parsing -from issues.sysctl import sysctl +from audit.system.plugins.sysctl.parsing import Parsing +from audit.system.plugins.sysctl.sysctl import sysctl class Sysctl: - def __init__(self): + def __init__(self, args): self._objects = dict() self._reports = dict() self._audit = list() self._audit.append({ 'audit': 'file', - 'value': '/etc/sysctl.conf', + 'value': args["sysctl_file"], }) self._audit.append({ 'audit': 'process', @@ -27,6 +27,7 @@ class Sysctl: self._objects = sysctl() def runAudit(self): + print("Running test for sysctl") # Read /etc/sysctl.conf self._parsing.runParsing() #self._reports.append(self._parsing.getResults()) diff --git a/design_pattern.txt b/design_pattern.txt new file mode 100644 index 0000000..fa4c46d --- /dev/null +++ b/design_pattern.txt @@ -0,0 +1 @@ +https://refactoring.guru/design-patterns/python diff --git a/issues/system.py b/issues/system.py deleted file mode 100644 index 780c8cf..0000000 --- a/issues/system.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python3 - -def system() -> list: - system = list() - - return system diff --git a/main.py b/main.py index eba5bfa..1cfcbe2 100644 --- a/main.py +++ b/main.py @@ -1,41 +1,7 @@ #!/usr/bin/env python -from argparse import ArgumentParser -from core.sysctl import Sysctl -from core.postfix import Postfix -from report import generateHtmlReport +from core.main import main -def checkArguments(): - args = ArgumentParser(description="Check Gitlab repositories") - args.add_argument('-a', '--audit', help="Kind of audit", choices=['system', 'application']) - return args.parse_args() - -def main(): - args = checkArguments() - - # If audit is not specified - if args.audit is None: - print("Please, you must specify the audit type") - exit(1) - - # Report - report = dict() - report['system'] = None - - # Audit application - if args.audit == "application": - pass - - # Audit system - if args.audit == "system": - sysctl = Sysctl() - sysctl.runAudit() - - # Getting reports - report['sysctl'] = sysctl.getReports() - - generateHtmlReport(report) - if __name__ == "__main__": main() diff --git a/parsing.py b/parsing.py deleted file mode 100644 index 8bc18a4..0000000 --- a/parsing.py +++ /dev/null @@ -1,41 +0,0 @@ -import re -from json import dumps - -class Parsing: - def __init__(self, objects, audit): - self._parsing = dict() - self._results = dict() - self._objects = objects - self._audit = audit - - def runParsing(self): - for audit in self._audit: - if audit['audit'] == 'file': - with open(audit['value'], 'rb') as fdata: - self._parseFile(fdata) - - def _parseFile(self, fdata): - data = fdata.read() - lines = data.splitlines() - - for line in lines: - self._parsingFile(line, self._objects['sysctl']) - - def _parsingFile(self, line, item) -> dict: - """ - This function parse the line and try to find the item in it - """ - res = None - - line = line.decode("utf-8") - for entry in item: - groupLine = re.search(entry['item'], line) - if groupLine: - sLine = line.split('=') - - return res - - def getResults(self) -> dict: - result = dict() - - return result diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..33afd92 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyyaml +jinja2 diff --git a/utils.py b/utils.py index 13d411d..92b214c 100644 --- a/utils.py +++ b/utils.py @@ -4,6 +4,14 @@ import re from subprocess import run +class ConfigError(Exception): + """Raised when the config file fails validation.""" + + def __init__(self, message, config_file): + self.config_file = config_file + self.message = f"{config_file} : {message}" + super().__init__(self.message) + def identifySystem(): os = None with open('/etc/issue', 'r') as f: