From fafe596135b3d9e3ef11c4eabe0fda323e1365fa Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Mon, 28 Mar 2022 11:28:00 +0200 Subject: [PATCH 001/209] nginx load_conf and obtain_vhost --- modules/configuration/configuration.py | 84 +++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 740793f..3028a51 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -64,7 +64,25 @@ def __obtain_vhost(self, port=None): if not port or port in list(loaded_vhost.keys())[0]: yield loaded_vhost elif self.__type == self.Type.NGINX: - raise NotImplementedError + def __gen(conf_server): + for server in conf_server: + if any(isinstance(el, list) for el in server['listen']): + for _port in server['listen']: + if not port or port in _port[0]: + yield {_port[0]: server} + else: + if not port or port in server['listen'][0]: + yield {server['listen'][0]: server} + + for file, conf in self.__loaded_conf.items(): + if 'server' in conf: + yield from __gen(conf['server']) + + if 'http' in conf: + for http in conf['http']: + if 'server' in http: + yield from __gen(http['server']) + def __load_conf(self, path) -> dict: """ @@ -80,6 +98,7 @@ def __load_conf(self, path) -> dict: assert ( file.exists() ), f"Can't find the APACHE/NGINX file to parse at {file.absolute()}" + if self.__type == self.Type.AUTO: try: results = self.__load_apache_conf(file) @@ -94,6 +113,7 @@ def __load_conf(self, path) -> dict: results = self.__load_apache_conf(file) else: results = self.__load_nginx_conf(file) + return results def __load_apache_conf(self, file: Path) -> dict: @@ -117,7 +137,67 @@ def __load_nginx_conf(self, file: Path) -> dict: :return: loaded configuration :rtype: dict """ - return nginx_parse(str(file.absolute())) + + def __structure(payload, struct): + """ + Funzione ricorsiva per generare una struttura chiave:blocco. + + :param payload: output della libreria parsing nginx + :type payload: list + :param struct: modifica la reference a questo dict + :type struct: dict + """ + for directive in payload: + directive_key = directive['directive'] + special = False + + if directive_key not in struct: + struct[directive_key] = [] + elif 'block' not in directive: + # se esiste già questa chiave ma non è un inizio di sottoblocco, + # allora è una lista di lista, ad indicare più direttive con uguale chiave + # ma distinto valore. + # Esempio: + # { + # listen 80; + # listen 443 ssl; + # } + # diventa + # {'listen': [['80'], ['443', 'ssl']]} + if any(isinstance(el, str) for el in struct[directive_key]): + # prima volta che scopro che ci sono più chiavi uguali, + # quindi modifico il valore della chiave in array, e aggiungo + # ciò che ho attualmente nel loop... + struct[directive_key] = [struct[directive_key], directive['args']] + special = True + elif any(isinstance(el, list) for el in struct[directive_key]): + struct[directive_key].append(directive['args']) + special = True + + if 'block' in directive: + struct[directive_key].append({}) + index = len(struct[directive_key]) - 1 + + if len(directive['args']) != 0: + arg = repr(directive['args']) # repr della lista per argomento di un blocco + struct[directive_key][index][arg] = {} # Sottoblocco con chiave gli argomenti di un blocco, esempio location >>> = /50x.html <<< {...} + __structure(directive['block'], struct[directive_key][index][arg]) + else: + __structure(directive['block'], struct[directive_key][index]) + elif not special: # se non è un inizio di sottoblocco e non è già stato elaborato in precedenza + struct[directive_key] = directive['args'] + + payload = nginx_parse(str(file.absolute())) + + if payload['status'] != 'ok' or len(payload['errors']) > 0: + raise Exception(f"Error parsing nginx config: {payload['errors']}") + + struct = {} + for file in payload['config']: + struct[file['file']] = {}; + __structure(file['parsed'], struct[file['file']]) + + return struct def __is_config_enabled(self, module) -> bool: """ From fb6847c160e59cdf6d83ce6072df120282e6213b Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Mon, 28 Mar 2022 11:28:59 +0200 Subject: [PATCH 002/209] Refactor Type enum to WebserverType --- modules/configuration/configuration.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 3028a51..eb00f1d 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -13,7 +13,7 @@ class Configuration: Apache/Nginx configuration file parser """ - class Type(Enum): + class WebserverType(Enum): """ Enum for configuration file types """ @@ -22,16 +22,16 @@ class Type(Enum): APACHE = 1 NGINX = 2 - def __init__(self, path: str, type_: Type = Type.AUTO, port=None): + def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=None): """ :param path: path to the configuration file :type path: str - :param type_: Type of the configuration file. - :type type_: Type + :param type_: WebserverType of the configuration file. + :type type_: WebserverType :param port: port to use for the check. :type port: str """ - Validator([(path, str), (type_, self.Type), (port if port else "", str)]) + Validator([(path, str), (type_, self.WebserverType), (port if port else "", str)]) self.__path = path self.__type = type_ self.__port = port @@ -50,8 +50,8 @@ def __obtain_vhost(self, port=None): :return: list of virtualhosts :rtype: list """ - assert self.__type != self.Type.AUTO, "Can't use this method with AUTO type." - if self.__type == self.Type.APACHE: + assert self.__type != self.WebserverType.AUTO, "Can't use this method with AUTO type." + if self.__type == self.WebserverType.APACHE: if "VirtualHost" not in self.__loaded_conf: self.__loaded_conf["VirtualHost"] = [] loaded_vhost = self.__loaded_conf["VirtualHost"] @@ -63,7 +63,7 @@ def __obtain_vhost(self, port=None): else: if not port or port in list(loaded_vhost.keys())[0]: yield loaded_vhost - elif self.__type == self.Type.NGINX: + elif self.__type == self.WebserverType.NGINX: def __gen(conf_server): for server in conf_server: if any(isinstance(el, list) for el in server['listen']): @@ -99,17 +99,17 @@ def __load_conf(self, path) -> dict: file.exists() ), f"Can't find the APACHE/NGINX file to parse at {file.absolute()}" - if self.__type == self.Type.AUTO: + if self.__type == self.WebserverType.AUTO: try: results = self.__load_apache_conf(file) - self.__type = self.Type.APACHE + self.__type = self.WebserverType.APACHE except Exception as e: self.__logging.debug( f"Couldn't parse config as apache: {e}\ntrying with nginx..." ) results = self.__load_nginx_conf(file) - self.__type = self.Type.NGINX - elif self.__type == self.Type.APACHE: + self.__type = self.WebserverType.NGINX + elif self.__type == self.WebserverType.APACHE: results = self.__load_apache_conf(file) else: results = self.__load_nginx_conf(file) From 7005e8e6405873861cce5c056fff4077c151d439 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Tue, 29 Mar 2022 13:55:29 +0200 Subject: [PATCH 003/209] New utils.type -> WebserverType and PortType --- modules/configuration/configuration.py | 33 +++++++++------------ modules/configuration/configuration_base.py | 20 ++++--------- utils/type.py | 19 ++++++++++++ 3 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 utils/type.py diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index eb00f1d..79d409c 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -1,8 +1,7 @@ -from enum import Enum from pathlib import Path - from modules.configuration.configuration_base import Config_base from utils.logger import Logger +from utils.type import WebserverType from utils.validation import Validator from crossplane import parse as nginx_parse from apacheconfig import make_loader @@ -13,15 +12,6 @@ class Configuration: Apache/Nginx configuration file parser """ - class WebserverType(Enum): - """ - Enum for configuration file types - """ - - AUTO = 0 - APACHE = 1 - NGINX = 2 - def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=None): """ :param path: path to the configuration file @@ -31,7 +21,7 @@ def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=No :param port: port to use for the check. :type port: str """ - Validator([(path, str), (type_, self.WebserverType), (port if port else "", str)]) + Validator([(path, str), (type_, WebserverType), (port if port else "", str)]) self.__path = path self.__type = type_ self.__port = port @@ -50,8 +40,8 @@ def __obtain_vhost(self, port=None): :return: list of virtualhosts :rtype: list """ - assert self.__type != self.WebserverType.AUTO, "Can't use this method with AUTO type." - if self.__type == self.WebserverType.APACHE: + assert self.__type != WebserverType.AUTO, "Can't use this method with webserver AUTO type." + if self.__type == WebserverType.APACHE: if "VirtualHost" not in self.__loaded_conf: self.__loaded_conf["VirtualHost"] = [] loaded_vhost = self.__loaded_conf["VirtualHost"] @@ -63,11 +53,14 @@ def __obtain_vhost(self, port=None): else: if not port or port in list(loaded_vhost.keys())[0]: yield loaded_vhost - elif self.__type == self.WebserverType.NGINX: + elif self.__type == WebserverType.NGINX: def __gen(conf_server): for server in conf_server: + # Nella struttura custom, se lista di lista allora il blocco server + # contiene più di una direttiva 'listen' if any(isinstance(el, list) for el in server['listen']): for _port in server['listen']: + # Assumo che il primo elemento della (sotto)lista sia la porta if not port or port in _port[0]: yield {_port[0]: server} else: @@ -99,17 +92,17 @@ def __load_conf(self, path) -> dict: file.exists() ), f"Can't find the APACHE/NGINX file to parse at {file.absolute()}" - if self.__type == self.WebserverType.AUTO: + if self.__type == WebserverType.AUTO: try: results = self.__load_apache_conf(file) - self.__type = self.WebserverType.APACHE + self.__type = WebserverType.APACHE except Exception as e: self.__logging.debug( f"Couldn't parse config as apache: {e}\ntrying with nginx..." ) results = self.__load_nginx_conf(file) - self.__type = self.WebserverType.NGINX - elif self.__type == self.WebserverType.APACHE: + self.__type = WebserverType.NGINX + elif self.__type == WebserverType.APACHE: results = self.__load_apache_conf(file) else: results = self.__load_nginx_conf(file) @@ -366,6 +359,8 @@ def __blackbox( self.__logging.debug(f"Analyzing vulnerability {name} in vhost {vhost_name}..") if vhost_name not in boolean_results: boolean_results[vhost_name] = {} + + module.conf.set_webserver(self.__type) is_empty = module.conf.is_empty(vhost) module_result = module.conf.condition( diff --git a/modules/configuration/configuration_base.py b/modules/configuration/configuration_base.py index b463522..4d3c5d3 100644 --- a/modules/configuration/configuration_base.py +++ b/modules/configuration/configuration_base.py @@ -1,6 +1,6 @@ -from enum import Enum from ssl import OPENSSL_VERSION import logging +from utils.type import PortType, WebserverType from utils.validation import Validator @@ -83,23 +83,13 @@ def __compare(self, ver1, ver2, reverse=False): return (ver1 < ver2) if not reverse else (ver1 > ver2) -class Type: - """ - Type of configuration. - """ - - NONE = 0 - HTTP = 80 - SSL = 443 - - class Config_base: """ Interface for configuration base. """ openSSL = OpenSSL() - VHOST_USE = Type.NONE + VHOST_USE = PortType.NONE def condition(self, vhost): """ @@ -345,7 +335,7 @@ class Parse_configuration_strict_security(Config_base): Check if vhost is vulnerable to misconfigured TLS strict security. """ - VHOST_USE = Type.SSL + VHOST_USE = PortType.SSL def __init__(self): self.__key = "Header" @@ -408,7 +398,7 @@ class Parse_configuration_checks_compression(Config_base): :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - VHOST_USE = Type.NONE + VHOST_USE = PortType.NONE def __init__(self, openssl: str): self.__openssl = openssl @@ -498,7 +488,7 @@ class Parse_configuration_checks_redirect(Config_base): Check if vhost is vulnerable to misconfigured TLS redirect. """ - VHOST_USE = Type.HTTP + VHOST_USE = PortType.HTTP def __init__(self): self.__keys = ["RewriteEngine", "RewriteRule"] diff --git a/utils/type.py b/utils/type.py new file mode 100644 index 0000000..66b9b27 --- /dev/null +++ b/utils/type.py @@ -0,0 +1,19 @@ +from enum import Enum + +class WebserverType(Enum): + """ + Enum for configuration file types + """ + + AUTO = 0 + APACHE = 1 + NGINX = 2 + +class PortType(Enum): + """ + Type of configuration. + """ + + NONE = 0 + HTTP = 80 + SSL = 443 From cee53267b694cbe4d1193dbc24de7ae6ba1f248c Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Tue, 29 Mar 2022 13:56:23 +0200 Subject: [PATCH 004/209] Split configuration_base flow between apache and nginx --- modules/configuration/apache/__init__.py | 0 .../apache/apache_configuration_base.py | 415 +++++++++++++++++ modules/configuration/configuration_base.py | 287 +++++------- modules/configuration/nginx/__init__.py | 0 .../nginx/nginx_configuration_base.py | 435 ++++++++++++++++++ 5 files changed, 955 insertions(+), 182 deletions(-) create mode 100644 modules/configuration/apache/__init__.py create mode 100644 modules/configuration/apache/apache_configuration_base.py create mode 100644 modules/configuration/nginx/__init__.py create mode 100644 modules/configuration/nginx/nginx_configuration_base.py diff --git a/modules/configuration/apache/__init__.py b/modules/configuration/apache/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/configuration/apache/apache_configuration_base.py b/modules/configuration/apache/apache_configuration_base.py new file mode 100644 index 0000000..63fade9 --- /dev/null +++ b/modules/configuration/apache/apache_configuration_base.py @@ -0,0 +1,415 @@ +import logging +from utils.validation import Validator + + +class Apache_parse_configuration_protocols(): + """ + Check if vhost is vulnerable to TLS SSLProtocol bad configuration. + """ + + def __init__(self, openssl: str, protocols: dict): + """ + :param openssl: OpenSSL version. + :type openssl: str + :param protocols: TLS/SSL protocols to check. + :type protocols: dict + """ + self.__openssl = openssl + self.__protocols = protocols + self.__key = "SSLProtocol" + Validator([(openssl, str), (protocols, dict)]) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the contextual directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the contextual directive. + :rtype: bool + """ + return self.__key not in vhost or not vhost[self.__key] + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using only the TLS version x. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version to check. + :type version: int + :returns: True if vhost is using ONLY the TLS version x. + :rtype: bool + """ + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def fix(self, vhost): + """ + Fix TLS/SSL protocol bad configuration. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + key = self.__key + backup = vhost[key] if key in vhost else "" + v = Validator() + for cipher, operation in self.__protocols.items(): + v.string(cipher) + vhost[key] = ( + f"{(vhost[key] if key in vhost and vhost[key] else 'ALL ')}" + f"{' ' if key in vhost and vhost[key] else ''}{operation}{cipher}" + ) + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to TLS SSLProtocol bad configuration. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to TLS SSLProtocol bad configuration. + :rtype: bool + """ + + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and True in ( + operation + cipher.lower() + not in (vhost[key].lower() if key in vhost else "") + for cipher, operation in self.__protocols.items() + ) + else: + return True in ( + operation + cipher.lower() + not in (vhost[key].lower() if key in vhost else "") + for cipher, operation in self.__protocols.items() + ) # is vulnerable if True + +class Apache_parse_configuration_ciphers(): + """ + Check if vhost is vulnerable to misconfigured TLS cipher. + """ + + def __init__(self, openssl: str, ciphers: list): + self.__openssl = openssl + self.__ciphers = ciphers + self.__key = "SSLCipherSuite" + Validator([(openssl, str), (ciphers, list)]) + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using ONLY the TLS version x. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version to check. + :type version: int + :returns: True if vhost is using ONLY the TLS version x. + :rtype: bool + """ + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the contextual directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the contextual directive. + :rtype: bool + """ + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS cipher in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + key = self.__key + v = Validator() + backup = vhost[key] if key in vhost else "" + for cipher in self.__ciphers: + v.string(cipher) + vhost[key] = ( + f"{vhost[key] if key in vhost and vhost[key] else ''}" + f"{':' if key in vhost and vhost[key] else ''}!{cipher.upper()}" + ) + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS cipher. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS cipher. + :rtype: bool + + """ + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and True in ( + "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + for cipher in self.__ciphers + ) + else: + return True in ( + "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + for cipher in self.__ciphers + ) # is vulnerable if True + +class Apache_parse_configuration_strict_security(): + """ + Check if vhost is vulnerable to misconfigured TLS strict security. + """ + + def __init__(self): + self.__key = "Header" + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the header directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the header directive. + :rtype: bool + """ + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS strict security in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + key = self.__key + backup = vhost[key] if key in vhost else "" + to_add = 'always set Strict-Transport-Security "max-age=63072000"' + if key in vhost: + vhost[key] += f";{to_add}" + else: + vhost[key] = to_add + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS strict security. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS strict security. + :rtype: bool + """ + + return ( + self.__key not in vhost + or "Strict-Transport-Security" not in vhost[self.__key] + ) # vulnerable if True + +class Apache_parse_configuration_checks_compression(): + """ + Check if vhost is vulnerable to misconfigured TLS compression. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + + def __init__(self, openssl: str): + self.__openssl = openssl + self.__key = "SSLCompression" + self.__value = "Off" + Validator([(openssl, str)]) + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using only a specific version of TLS. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version. + :type version: int + :returns: True if vhost is using only a specific version of TLS. + :rtype: bool + """ + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the SSLCompression directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the SSLCompression directive. + :rtype: bool + """ + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS compression in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + key = self.__key + backup = vhost[key] if key in vhost else "" + vhost[key] = self.__value + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS compression. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS compression. + :rtype: bool + """ + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and (key not in vhost or vhost[key] != self.__value) + + else: + return ( + key not in vhost or vhost[key] != self.__value + ) # is vulnerable if True + +class Apache_parse_configuration_checks_redirect(): + """ + Check if vhost is vulnerable to misconfigured TLS redirect. + """ + + def __init__(self): + self.__keys = ["RewriteEngine", "RewriteRule"] + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the RewriteEngine and RewriteRule directives. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. + :rtype: bool + """ + return True in (key not in vhost for key in self.__keys) + + def fix(self, vhost): + """ + Fix misconfigured TLS redirect in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + RewriteEngine, RewriteRule = self.__keys + backup_rewrite_engine = vhost[RewriteEngine] if RewriteEngine in vhost else "" + backup_rewrite_rule = vhost[RewriteRule] if RewriteRule in vhost else "" + vhost[RewriteEngine] = "on" + vhost[RewriteRule] = "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + return { + "before": { + "RewriteEngine": backup_rewrite_engine, + "RewriteRule": backup_rewrite_rule, + }, + "after": { + "RewriteEngine": vhost[RewriteEngine], + "RewriteRule": vhost[RewriteRule], + }, + } + + def condition(self, vhost, openssl=None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS redirect. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS redirect. + :rtype: bool + """ + RewriteEngine, RewriteRule = self.__keys + return ( + RewriteEngine not in vhost + or RewriteRule not in vhost + or vhost[RewriteEngine] != "on" + or vhost[RewriteRule] != "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + ) # vulnerable if True diff --git a/modules/configuration/configuration_base.py b/modules/configuration/configuration_base.py index 4d3c5d3..eabe8a8 100644 --- a/modules/configuration/configuration_base.py +++ b/modules/configuration/configuration_base.py @@ -3,6 +3,9 @@ from utils.type import PortType, WebserverType from utils.validation import Validator +from modules.configuration.apache.apache_configuration_base import * +from modules.configuration.nginx.nginx_configuration_base import * + class OpenSSL: """ @@ -91,6 +94,15 @@ class Config_base: openSSL = OpenSSL() VHOST_USE = PortType.NONE + def set_webserver(self, webserver: WebserverType): + """ + Dummy set webserver type. + + :param webserver: Webserver type. + :type webserver: :class:`~letsencrypt_apache.configuration.WebserverType` + """ + raise NotImplementedError + def condition(self, vhost): """ Dummy condition method. @@ -125,12 +137,31 @@ def is_empty(self, vhost): """ raise NotImplementedError - class Parse_configuration_protocols(Config_base): """ Check if vhost is vulnerable to TLS SSLProtocol bad configuration. """ + def __init__(self, openssl: str, protocols: dict): + """ + :param openssl: OpenSSL version. + :type openssl: str + :param protocols: TLS/SSL protocols to check. + :type protocols: dict + """ + self.__openssl = openssl + self.__protocols = protocols + Validator([(openssl, str), (protocols, dict)]) + self.__execution_class = None + self.__webserver_type = WebserverType.AUTO + + def set_webserver(self, webserver: WebserverType): + self.__webserver_type = webserver + if webserver == WebserverType.APACHE: + self.__execution_class = Apache_parse_configuration_protocols(self.__openssl, self.__protocols) + elif webserver == WebserverType.NGINX: + self.__execution_class = Nginx_parse_configuration_protocols(self.__openssl, self.__protocols) + def is_empty(self, vhost): """ Check if vhost doesn't have the contextual directive. @@ -140,7 +171,8 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ - return self.__key not in vhost or not vhost[self.__key] + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_empty(vhost) def is_tls(self, vhost, version=3): """ @@ -153,22 +185,8 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using ONLY the TLS version x. :rtype: bool """ - return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" - ) - - def __init__(self, openssl: str, protocols: dict): - """ - :param openssl: OpenSSL version. - :type openssl: str - :param protocols: TLS/SSL protocols to check. - :type protocols: dict - """ - self.__openssl = openssl - self.__protocols = protocols - self.__key = "SSLProtocol" - Validator([(openssl, str), (protocols, dict)]) + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_tls(vhost, version) def fix(self, vhost): """ @@ -177,19 +195,8 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - key = self.__key - backup = vhost[key] if key in vhost else "" - v = Validator() - for cipher, operation in self.__protocols.items(): - v.string(cipher) - vhost[key] = ( - f"{(vhost[key] if key in vhost and vhost[key] else 'ALL ')}" - f"{' ' if key in vhost and vhost[key] else ''}{operation}{cipher}" - ) - return { - "before": f"{key} {backup}" if backup else "", - "after": f"{key} {vhost[key]}", - } + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.fix(vhost) def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -204,32 +211,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to TLS SSLProtocol bad configuration. :rtype: bool """ - - if self.is_tls(vhost, version=3): - logging.debug("TLSv1.3 Detected as mutually allowed.") - return False - key = self.__key - openssl_greater_than = self.__openssl - if openssl is None: - openssl = "" - Validator([(openssl, str)]) - if not ignore_openssl: - if openssl: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) - else: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) - - return not is_safe and True in ( - operation + cipher.lower() - not in (vhost[key].lower() if key in vhost else "") - for cipher, operation in self.__protocols.items() - ) - else: - return True in ( - operation + cipher.lower() - not in (vhost[key].lower() if key in vhost else "") - for cipher, operation in self.__protocols.items() - ) # is vulnerable if True + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.condition(vhost, openssl, ignore_openssl) class Parse_configuration_ciphers(Config_base): @@ -240,8 +223,16 @@ class Parse_configuration_ciphers(Config_base): def __init__(self, openssl: str, ciphers: list): self.__openssl = openssl self.__ciphers = ciphers - self.__key = "SSLCipherSuite" Validator([(openssl, str), (ciphers, list)]) + self.__execution_class = None + self.__webserver_type = WebserverType.AUTO + + def set_webserver(self, webserver: WebserverType): + self.__webserver_type = webserver + if webserver == WebserverType.APACHE: + self.__execution_class = Apache_parse_configuration_ciphers(self.__openssl, self.__ciphers) + elif webserver == WebserverType.NGINX: + self.__execution_class = Nginx_parse_configuration_ciphers(self.__openssl, self.__ciphers) def is_tls(self, vhost, version=3): """ @@ -254,10 +245,8 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using ONLY the TLS version x. :rtype: bool """ - return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" - ) + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_tls(vhost, version) def is_empty(self, vhost): """ @@ -268,28 +257,12 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ - return self.__key not in vhost or not vhost[self.__key] + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_empty(vhost) def fix(self, vhost): - """ - Fix misconfigured TLS cipher in vhost. - - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` - """ - key = self.__key - v = Validator() - backup = vhost[key] if key in vhost else "" - for cipher in self.__ciphers: - v.string(cipher) - vhost[key] = ( - f"{vhost[key] if key in vhost and vhost[key] else ''}" - f"{':' if key in vhost and vhost[key] else ''}!{cipher.upper()}" - ) - return { - "before": f"{key} {backup}" if backup else "", - "after": f"{key} {vhost[key]}", - } + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.fix(vhost) def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -305,29 +278,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :rtype: bool """ - if self.is_tls(vhost, version=3): - logging.debug("TLSv1.3 Detected as mutually allowed.") - return False - key = self.__key - openssl_greater_than = self.__openssl - if openssl is None: - openssl = "" - Validator([(openssl, str)]) - if not ignore_openssl: - if openssl: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) - else: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) - - return not is_safe and True in ( - "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") - for cipher in self.__ciphers - ) - else: - return True in ( - "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") - for cipher in self.__ciphers - ) # is vulnerable if True + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.condition(vhost, openssl, ignore_openssl) class Parse_configuration_strict_security(Config_base): @@ -338,7 +290,15 @@ class Parse_configuration_strict_security(Config_base): VHOST_USE = PortType.SSL def __init__(self): - self.__key = "Header" + self.__execution_class = None + self.__webserver_type = WebserverType.AUTO + + def set_webserver(self, webserver: WebserverType): + self.__webserver_type = webserver + if webserver == WebserverType.APACHE: + self.__execution_class = Apache_parse_configuration_strict_security() + elif webserver == WebserverType.NGINX: + self.__execution_class = Nginx_parse_configuration_strict_security() def is_empty(self, vhost): """ @@ -349,7 +309,8 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the header directive. :rtype: bool """ - return self.__key not in vhost or not vhost[self.__key] + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_empty(vhost) def fix(self, vhost): """ @@ -358,17 +319,8 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - key = self.__key - backup = vhost[key] if key in vhost else "" - to_add = 'always set Strict-Transport-Security "max-age=63072000"' - if key in vhost: - vhost[key] += f";{to_add}" - else: - vhost[key] = to_add - return { - "before": f"{key} {backup}" if backup else "", - "after": f"{key} {vhost[key]}", - } + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.fix(vhost) def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -383,11 +335,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS strict security. :rtype: bool """ - - return ( - self.__key not in vhost - or "Strict-Transport-Security" not in vhost[self.__key] - ) # vulnerable if True + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.condition(vhost, openssl, ignore_openssl) class Parse_configuration_checks_compression(Config_base): @@ -402,9 +351,17 @@ class Parse_configuration_checks_compression(Config_base): def __init__(self, openssl: str): self.__openssl = openssl - self.__key = "SSLCompression" - self.__value = "Off" Validator([(openssl, str)]) + self.__execution_class = None + self.__webserver_type = WebserverType.AUTO + + def set_webserver(self, webserver: WebserverType): + self.__webserver_type = webserver + if webserver == WebserverType.APACHE: + self.__execution_class = Apache_parse_configuration_checks_compression(self.__openssl) + elif webserver == WebserverType.NGINX: + self.__execution_class = Nginx_parse_configuration_checks_compression(self.__openssl) + def is_tls(self, vhost, version=3): """ @@ -417,10 +374,8 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using only a specific version of TLS. :rtype: bool """ - return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" - ) + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_tls(vhost, version) def is_empty(self, vhost): """ @@ -431,7 +386,8 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the SSLCompression directive. :rtype: bool """ - return self.__key not in vhost or not vhost[self.__key] + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_empty(vhost) def fix(self, vhost): """ @@ -440,13 +396,8 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - key = self.__key - backup = vhost[key] if key in vhost else "" - vhost[key] = self.__value - return { - "before": f"{key} {backup}" if backup else "", - "after": f"{key} {vhost[key]}", - } + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.fix(vhost) def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -461,27 +412,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS compression. :rtype: bool """ - if self.is_tls(vhost, version=3): - logging.debug("TLSv1.3 Detected as mutually allowed.") - return False - key = self.__key - openssl_greater_than = self.__openssl - if openssl is None: - openssl = "" - Validator([(openssl, str)]) - if not ignore_openssl: - if openssl: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) - else: - is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) - - return not is_safe and (key not in vhost or vhost[key] != self.__value) - - else: - return ( - key not in vhost or vhost[key] != self.__value - ) # is vulnerable if True - + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.condition(vhost, openssl, ignore_openssl) class Parse_configuration_checks_redirect(Config_base): """ @@ -491,7 +423,15 @@ class Parse_configuration_checks_redirect(Config_base): VHOST_USE = PortType.HTTP def __init__(self): - self.__keys = ["RewriteEngine", "RewriteRule"] + self.__execution_class = None + self.__webserver_type = WebserverType.AUTO + + def set_webserver(self, webserver: WebserverType): + self.__webserver_type = webserver + if webserver == WebserverType.APACHE: + self.__execution_class = Apache_parse_configuration_checks_redirect() + elif webserver == WebserverType.NGINX: + self.__execution_class = Nginx_parse_configuration_checks_redirect() def is_empty(self, vhost): """ @@ -502,7 +442,8 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. :rtype: bool """ - return True in (key not in vhost for key in self.__keys) + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.is_empty(vhost) def fix(self, vhost): """ @@ -511,21 +452,8 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - RewriteEngine, RewriteRule = self.__keys - backup_rewrite_engine = vhost[RewriteEngine] if RewriteEngine in vhost else "" - backup_rewrite_rule = vhost[RewriteRule] if RewriteRule in vhost else "" - vhost[RewriteEngine] = "on" - vhost[RewriteRule] = "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" - return { - "before": { - "RewriteEngine": backup_rewrite_engine, - "RewriteRule": backup_rewrite_rule, - }, - "after": { - "RewriteEngine": vhost[RewriteEngine], - "RewriteRule": vhost[RewriteRule], - }, - } + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.fix(vhost) def condition(self, vhost, openssl=None, ignore_openssl=False): """ @@ -540,10 +468,5 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS redirect. :rtype: bool """ - RewriteEngine, RewriteRule = self.__keys - return ( - RewriteEngine not in vhost - or RewriteRule not in vhost - or vhost[RewriteEngine] != "on" - or vhost[RewriteRule] != "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" - ) # vulnerable if True + assert self.__execution_class is not None, "Webserver type not set." + return self.__execution_class.condition(vhost, openssl, ignore_openssl) diff --git a/modules/configuration/nginx/__init__.py b/modules/configuration/nginx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py new file mode 100644 index 0000000..f5dfedf --- /dev/null +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -0,0 +1,435 @@ +import logging +from utils.validation import Validator + + +class Nginx_parse_configuration_protocols(): + """ + Check if vhost is vulnerable to TLS SSLProtocol bad configuration. + """ + + def __init__(self, openssl: str, protocols: dict): + """ + :param openssl: OpenSSL version. + :type openssl: str + :param protocols: TLS/SSL protocols to check. + :type protocols: dict + """ + raise NotImplementedError + self.__openssl = openssl + self.__protocols = protocols + self.__key = "SSLProtocol" + Validator([(openssl, str), (protocols, dict)]) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the contextual directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the contextual directive. + :rtype: bool + """ + raise NotImplementedError + return self.__key not in vhost or not vhost[self.__key] + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using only the TLS version x. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version to check. + :type version: int + :returns: True if vhost is using ONLY the TLS version x. + :rtype: bool + """ + raise NotImplementedError + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def fix(self, vhost): + """ + Fix TLS/SSL protocol bad configuration. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + raise NotImplementedError + key = self.__key + backup = vhost[key] if key in vhost else "" + v = Validator() + for cipher, operation in self.__protocols.items(): + v.string(cipher) + vhost[key] = ( + f"{(vhost[key] if key in vhost and vhost[key] else 'ALL ')}" + f"{' ' if key in vhost and vhost[key] else ''}{operation}{cipher}" + ) + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to TLS SSLProtocol bad configuration. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to TLS SSLProtocol bad configuration. + :rtype: bool + """ + raise NotImplementedError + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and True in ( + operation + cipher.lower() + not in (vhost[key].lower() if key in vhost else "") + for cipher, operation in self.__protocols.items() + ) + else: + return True in ( + operation + cipher.lower() + not in (vhost[key].lower() if key in vhost else "") + for cipher, operation in self.__protocols.items() + ) # is vulnerable if True + +class Nginx_parse_configuration_ciphers(): + """ + Check if vhost is vulnerable to misconfigured TLS cipher. + """ + + def __init__(self, openssl: str, ciphers: list): + raise NotImplementedError + self.__openssl = openssl + self.__ciphers = ciphers + self.__key = "SSLCipherSuite" + Validator([(openssl, str), (ciphers, list)]) + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using ONLY the TLS version x. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version to check. + :type version: int + :returns: True if vhost is using ONLY the TLS version x. + :rtype: bool + """ + raise NotImplementedError + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the contextual directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the contextual directive. + :rtype: bool + """ + raise NotImplementedError + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS cipher in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + raise NotImplementedError + key = self.__key + v = Validator() + backup = vhost[key] if key in vhost else "" + for cipher in self.__ciphers: + v.string(cipher) + vhost[key] = ( + f"{vhost[key] if key in vhost and vhost[key] else ''}" + f"{':' if key in vhost and vhost[key] else ''}!{cipher.upper()}" + ) + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS cipher. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS cipher. + :rtype: bool + """ + raise NotImplementedError + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and True in ( + "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + for cipher in self.__ciphers + ) + else: + return True in ( + "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + for cipher in self.__ciphers + ) # is vulnerable if True + +class Nginx_parse_configuration_strict_security(): + """ + Check if vhost is vulnerable to misconfigured TLS strict security. + """ + + def __init__(self): + raise NotImplementedError + self.__key = "Header" + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the header directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the header directive. + :rtype: bool + """ + raise NotImplementedError + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS strict security in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + raise NotImplementedError + key = self.__key + backup = vhost[key] if key in vhost else "" + to_add = 'always set Strict-Transport-Security "max-age=63072000"' + if key in vhost: + vhost[key] += f";{to_add}" + else: + vhost[key] = to_add + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS strict security. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS strict security. + :rtype: bool + """ + raise NotImplementedError + return ( + self.__key not in vhost + or "Strict-Transport-Security" not in vhost[self.__key] + ) # vulnerable if True + +class Nginx_parse_configuration_checks_compression(): + """ + Check if vhost is vulnerable to misconfigured TLS compression. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + + def __init__(self, openssl: str): + raise NotImplementedError + self.__openssl = openssl + self.__key = "SSLCompression" + self.__value = "Off" + Validator([(openssl, str)]) + + def is_tls(self, vhost, version=3): + """ + Check if vhost is using only a specific version of TLS. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param version: TLS version. + :type version: int + :returns: True if vhost is using only a specific version of TLS. + :rtype: bool + """ + raise NotImplementedError + return ( + "SSLProtocol" in vhost + and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + ) + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the SSLCompression directive. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the SSLCompression directive. + :rtype: bool + """ + raise NotImplementedError + return self.__key not in vhost or not vhost[self.__key] + + def fix(self, vhost): + """ + Fix misconfigured TLS compression in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + raise NotImplementedError + key = self.__key + backup = vhost[key] if key in vhost else "" + vhost[key] = self.__value + return { + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}", + } + + def condition(self, vhost, openssl: str = None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS compression. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS compression. + :rtype: bool + """ + raise NotImplementedError + if self.is_tls(vhost, version=3): + logging.debug("TLSv1.3 Detected as mutually allowed.") + return False + key = self.__key + openssl_greater_than = self.__openssl + if openssl is None: + openssl = "" + Validator([(openssl, str)]) + if not ignore_openssl: + if openssl: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) + else: + is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) + + return not is_safe and (key not in vhost or vhost[key] != self.__value) + + else: + return ( + key not in vhost or vhost[key] != self.__value + ) # is vulnerable if True + +class Nginx_parse_configuration_checks_redirect(): + """ + Check if vhost is vulnerable to misconfigured TLS redirect. + """ + + def __init__(self): + raise NotImplementedError + self.__keys = ["RewriteEngine", "RewriteRule"] + + def is_empty(self, vhost): + """ + Check if vhost doesn't have the RewriteEngine and RewriteRule directives. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. + :rtype: bool + """ + raise NotImplementedError + return True in (key not in vhost for key in self.__keys) + + def fix(self, vhost): + """ + Fix misconfigured TLS redirect in vhost. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + """ + raise NotImplementedError + RewriteEngine, RewriteRule = self.__keys + backup_rewrite_engine = vhost[RewriteEngine] if RewriteEngine in vhost else "" + backup_rewrite_rule = vhost[RewriteRule] if RewriteRule in vhost else "" + vhost[RewriteEngine] = "on" + vhost[RewriteRule] = "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + return { + "before": { + "RewriteEngine": backup_rewrite_engine, + "RewriteRule": backup_rewrite_rule, + }, + "after": { + "RewriteEngine": vhost[RewriteEngine], + "RewriteRule": vhost[RewriteRule], + }, + } + + def condition(self, vhost, openssl=None, ignore_openssl=False): + """ + Check if vhost is vulnerable to misconfigured TLS redirect. + + :param vhost: VirtualHost object. + :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param openssl: OpenSSL version. + :type openssl: str + :param ignore_openssl: Ignore OpenSSL version. + :type ignore_openssl: bool + :returns: True if vhost is vulnerable to misconfigured TLS redirect. + :rtype: bool + """ + raise NotImplementedError + RewriteEngine, RewriteRule = self.__keys + return ( + RewriteEngine not in vhost + or RewriteRule not in vhost + or vhost[RewriteEngine] != "on" + or vhost[RewriteRule] != "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + ) # vulnerable if True From 76f8dc032884d759fa26919365927172b71822aa Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Thu, 31 Mar 2022 22:30:31 +0200 Subject: [PATCH 005/209] Fix for nginx parsing --- modules/configuration/configuration.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 79d409c..e471fcb 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -190,6 +190,10 @@ def __structure(payload, struct): struct[file['file']] = {}; __structure(file['parsed'], struct[file['file']]) + # Remove file if it doesn't have any 'http' or 'server' block + if 'http' not in struct[file['file']] and 'server' not in struct[file['file']]: + del struct[file['file']] + return struct def __is_config_enabled(self, module) -> bool: @@ -223,7 +227,9 @@ def __check_global(self, modules: dict, openssl: str, ignore_openssl: bool): module, name, fix=False, - vhost=self.__loaded_conf, + vhost=self.__loaded_conf + if self.__type == WebserverType.APACHE + else next(val['http'][0] for file, val in self.__loaded_conf.items() if 'http' in val), vhost_name="global", openssl=openssl, ignore_openssl=ignore_openssl, @@ -361,6 +367,7 @@ def __blackbox( boolean_results[vhost_name] = {} module.conf.set_webserver(self.__type) + is_empty = module.conf.is_empty(vhost) module_result = module.conf.condition( From 5d6a1e444a777d36fa423bf33a61854b47c42d3d Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Thu, 31 Mar 2022 22:31:24 +0200 Subject: [PATCH 006/209] Fix PortType no ihneritance from Enum --- utils/type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/type.py b/utils/type.py index 66b9b27..b6cbca1 100644 --- a/utils/type.py +++ b/utils/type.py @@ -9,7 +9,7 @@ class WebserverType(Enum): APACHE = 1 NGINX = 2 -class PortType(Enum): +class PortType: # No inheritance from Enum """ Type of configuration. """ From de755c3ee7e38cca72cb5931d704beea2923febc Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Thu, 31 Mar 2022 22:31:36 +0200 Subject: [PATCH 007/209] Nginx configurations wip --- .../nginx/nginx_configuration_base.py | 73 +++++++------------ 1 file changed, 28 insertions(+), 45 deletions(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index f5dfedf..ad2e7b3 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -14,10 +14,9 @@ def __init__(self, openssl: str, protocols: dict): :param protocols: TLS/SSL protocols to check. :type protocols: dict """ - raise NotImplementedError self.__openssl = openssl self.__protocols = protocols - self.__key = "SSLProtocol" + self.__key = "ssl_protocols" Validator([(openssl, str), (protocols, dict)]) def is_empty(self, vhost): @@ -25,11 +24,10 @@ def is_empty(self, vhost): Check if vhost doesn't have the contextual directive. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ - raise NotImplementedError return self.__key not in vhost or not vhost[self.__key] def is_tls(self, vhost, version=3): @@ -43,10 +41,10 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using ONLY the TLS version x. :rtype: bool """ - raise NotImplementedError return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + "ssl_protocols" in vhost + and len(vhost["ssl_protocols"]) == 1 + and any(protocol.lower() == f"tlsv1.{version}" for protocol in vhost["ssl_protocols"]) ) def fix(self, vhost): @@ -84,7 +82,6 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to TLS SSLProtocol bad configuration. :rtype: bool """ - raise NotImplementedError if self.is_tls(vhost, version=3): logging.debug("TLSv1.3 Detected as mutually allowed.") return False @@ -100,14 +97,14 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) return not is_safe and True in ( - operation + cipher.lower() - not in (vhost[key].lower() if key in vhost else "") + cipher.lower() + not in ([protocol.lower() for protocol in vhost[key]] if key in vhost else "") for cipher, operation in self.__protocols.items() ) else: return True in ( - operation + cipher.lower() - not in (vhost[key].lower() if key in vhost else "") + cipher.lower() + not in ([protocol.lower() for protocol in vhost[key]] if key in vhost else "") for cipher, operation in self.__protocols.items() ) # is vulnerable if True @@ -117,10 +114,9 @@ class Nginx_parse_configuration_ciphers(): """ def __init__(self, openssl: str, ciphers: list): - raise NotImplementedError self.__openssl = openssl self.__ciphers = ciphers - self.__key = "SSLCipherSuite" + self.__key = "ssl_ciphers" Validator([(openssl, str), (ciphers, list)]) def is_tls(self, vhost, version=3): @@ -134,10 +130,10 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using ONLY the TLS version x. :rtype: bool """ - raise NotImplementedError return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + "ssl_protocols" in vhost + and len(vhost["ssl_protocols"]) == 1 + and any(protocol.lower() == f"tlsv1.{version}" for protocol in vhost["ssl_protocols"]) ) def is_empty(self, vhost): @@ -149,7 +145,6 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ - raise NotImplementedError return self.__key not in vhost or not vhost[self.__key] def fix(self, vhost): @@ -187,7 +182,6 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS cipher. :rtype: bool """ - raise NotImplementedError if self.is_tls(vhost, version=3): logging.debug("TLSv1.3 Detected as mutually allowed.") return False @@ -203,12 +197,12 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) return not is_safe and True in ( - "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + "!" + cipher.lower() not in ([c.lower() for c in vhost[key]] if key in vhost else "") for cipher in self.__ciphers ) else: return True in ( - "!" + cipher.lower() not in (vhost[key].lower() if key in vhost else "") + "!" + cipher.lower() not in ([c.lower() for c in vhost[key]] if key in vhost else "") for cipher in self.__ciphers ) # is vulnerable if True @@ -218,8 +212,7 @@ class Nginx_parse_configuration_strict_security(): """ def __init__(self): - raise NotImplementedError - self.__key = "Header" + self.__key = "add_header" def is_empty(self, vhost): """ @@ -230,7 +223,6 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the header directive. :rtype: bool """ - raise NotImplementedError return self.__key not in vhost or not vhost[self.__key] def fix(self, vhost): @@ -266,7 +258,6 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS strict security. :rtype: bool """ - raise NotImplementedError return ( self.__key not in vhost or "Strict-Transport-Security" not in vhost[self.__key] @@ -281,9 +272,8 @@ class Nginx_parse_configuration_checks_compression(): """ def __init__(self, openssl: str): - raise NotImplementedError self.__openssl = openssl - self.__key = "SSLCompression" + self.__key = "ssl_compression" self.__value = "Off" Validator([(openssl, str)]) @@ -298,10 +288,10 @@ def is_tls(self, vhost, version=3): :returns: True if vhost is using only a specific version of TLS. :rtype: bool """ - raise NotImplementedError return ( - "SSLProtocol" in vhost - and vhost["SSLProtocol"].lower() == f"tlsv1.{version}" + "ssl_protocols" in vhost + and len(vhost["ssl_protocols"]) == 1 + and any(protocol.lower() == f"tlsv1.{version}" for protocol in vhost["ssl_protocols"]) ) def is_empty(self, vhost): @@ -313,8 +303,7 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the SSLCompression directive. :rtype: bool """ - raise NotImplementedError - return self.__key not in vhost or not vhost[self.__key] + return True def fix(self, vhost): """ @@ -323,14 +312,8 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - raise NotImplementedError - key = self.__key - backup = vhost[key] if key in vhost else "" - vhost[key] = self.__value - return { - "before": f"{key} {backup}" if backup else "", - "after": f"{key} {vhost[key]}", - } + # no directive fix available for nginx + pass def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -345,7 +328,6 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS compression. :rtype: bool """ - raise NotImplementedError if self.is_tls(vhost, version=3): logging.debug("TLSv1.3 Detected as mutually allowed.") return False @@ -354,6 +336,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): if openssl is None: openssl = "" Validator([(openssl, str)]) + # nginx non ha direttive per ssl compression, dipende solo dalla versione utilizzata if not ignore_openssl: if openssl: is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) @@ -373,7 +356,7 @@ class Nginx_parse_configuration_checks_redirect(): """ def __init__(self): - raise NotImplementedError + return self.__keys = ["RewriteEngine", "RewriteRule"] def is_empty(self, vhost): @@ -385,7 +368,7 @@ def is_empty(self, vhost): :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. :rtype: bool """ - raise NotImplementedError + return return True in (key not in vhost for key in self.__keys) def fix(self, vhost): @@ -395,7 +378,7 @@ def fix(self, vhost): :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - raise NotImplementedError + return RewriteEngine, RewriteRule = self.__keys backup_rewrite_engine = vhost[RewriteEngine] if RewriteEngine in vhost else "" backup_rewrite_rule = vhost[RewriteRule] if RewriteRule in vhost else "" @@ -425,7 +408,7 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS redirect. :rtype: bool """ - raise NotImplementedError + return RewriteEngine, RewriteRule = self.__keys return ( RewriteEngine not in vhost From 5e133b321c59ca029e95451e3fcc1ecf517edc3e Mon Sep 17 00:00:00 2001 From: matteounitn <32160291+matteounitn@users.noreply.github.com> Date: Fri, 1 Apr 2022 00:11:12 +0200 Subject: [PATCH 008/209] Webhook implementation --- modules/configuration/configuration.py | 44 +++++++++-------- modules/core.py | 11 +++++ modules/report.py | 67 ++++++++++++++++++++++++++ run.py | 9 ++++ tlsa/tlsa.py | 4 ++ utils/prune.py | 2 +- 6 files changed, 115 insertions(+), 22 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 740793f..1addbb3 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -176,12 +176,12 @@ def __check_usage(self, module, vhost_name) -> bool: return True def __vhost_wrapper( - self, - modules: dict, - online=False, - fix=False, - openssl: str = None, - ignore_openssl: bool = False, + self, + modules: dict, + online=False, + fix=False, + openssl: str = None, + ignore_openssl: bool = False, ): """ Wrapper for the vhosts. @@ -206,7 +206,7 @@ def __vhost_wrapper( for vhost_name, vhost in virtualhost.items(): for name, module in modules.items(): if self.__is_config_enabled(module) and self.__check_usage( - module, vhost_name + module, vhost_name ): if not online: self.__blackbox( @@ -227,7 +227,11 @@ def __vhost_wrapper( self.__logging.debug( f"The module {name} isn't compatible. Skipping..." ) - return boolean_results if is_executed else {'General rules': boolean_results_global} + return ( + boolean_results + if is_executed + else {"General rules": boolean_results_global} + ) def __hybrid(self, module, name, vhost, vhost_name) -> dict: """ @@ -248,16 +252,16 @@ def __hybrid(self, module, name, vhost, vhost_name) -> dict: return module.conf.fix(vhost) def __blackbox( - self, - module, - name, - fix, - vhost, - vhost_name, - openssl, - ignore_openssl, - boolean_results, - global_value, + self, + module, + name, + fix, + vhost, + vhost_name, + openssl, + ignore_openssl, + boolean_results, + global_value, ): """ Internal method to check the configuration blackbox. @@ -366,9 +370,7 @@ def save(self, file_name: str = None): file = Path(path) file.touch() - options = { - 'namedblocks': False - } + options = {"namedblocks": False} with make_loader(**options) as loader: loader.dump(filepath=str(file.absolute()), dct=self.__loaded_conf) self.__logging.info(f"Saved configuration in file {file.absolute()}") diff --git a/modules/core.py b/modules/core.py index 885a584..7258bda 100644 --- a/modules/core.py +++ b/modules/core.py @@ -55,6 +55,7 @@ def __init__( openssl_version=None, ignore_openssl=False, stix=False, + webhook="", ): """ :param hostname_or_path: hostname or path to scan @@ -79,6 +80,8 @@ def __init__( :type ignore_openssl: bool :param stix: generate stix report :type stix: bool + :param webhook: webhook to send the report to + :type webhook: str """ if to_exclude is None: to_exclude = [] @@ -101,6 +104,7 @@ def __init__( openssl_version=openssl_version, ignore_openssl=ignore_openssl, stix=stix, + webhook=webhook, ) self.__cache[configuration] = self.__load_configuration(modules) self.__exec( @@ -153,6 +157,12 @@ def input(self, **kwargs): str, ), (kwargs["stix"], bool), + ( + "" + if "webhook" not in kwargs or not kwargs["webhook"] + else kwargs["webhook"], + str, + ), ] ) kwargs["to_exclude"] = list(map(str.lower, kwargs["to_exclude"])) @@ -397,6 +407,7 @@ def __call_output_modules(self, res: dict): and self.__input_dict["group_by"] == "module" else Report_module.Mode.HOSTS, stix=self.__input_dict["stix"], + webhook=self.__input_dict["webhook"], ) self.__logging.debug("Output generated.") diff --git a/modules/report.py b/modules/report.py index f0e8170..475e910 100644 --- a/modules/report.py +++ b/modules/report.py @@ -1,6 +1,8 @@ from enum import Enum from os import mkdir +import requests as requests + from modules.stix.stix import Stix from utils.validation import Validator, rec_search_key from utils import output @@ -164,6 +166,61 @@ def __extract_results(self, res: dict) -> tuple: res[hostname] = res[hostname]["results"] return res, modules + # sending results to the webhook with an exception safe way + def __send_webhook( + self, + webhook_url: str, + results: dict, + modules: dict, + post=True, + result_param="results", + modules_param="modules", + other_params=None, + ): + """ + Sends the results to the webhook. + + :param webhook_url: Webhook URL. + :type webhook_url: str + :param results: Dictionary containing the results of the scan. + :type results: dict + :param modules: Dictionary containing the loaded modules. + :type modules: dict + """ + if other_params is None: + other_params = {} + self.__logging.debug(f"Sending results to webhook..") + try: + json_data = { + result_param: pformat(results, indent=2), + modules_param: pformat(modules, indent=2), + "version": version, + "date": str(datetime.today()), + } + json_data.update(other_params) + headers = { + "Content-Type": "application/json", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.95 " + "Safari/537.36", + } + if post: + requests.post( + webhook_url, + headers=headers, + json=json_data, + ) + else: + + requests.get( + webhook_url, + headers=headers, + params=json_data, + ) + except Exception as e: + self.__logging.error(f"Error sending results to webhook: {e}") + def run(self, **kwargs): """ Runs the report. @@ -176,6 +233,7 @@ def run(self, **kwargs): * *path* (string) -- Path to the report. * *mode* (Mode) -- Report mode. * *stix* (bool) -- If True, the report will be generated in STIX format. + * *webhook* (string) -- Webhook to send the report to. """ self.input(**kwargs) @@ -183,6 +241,8 @@ def run(self, **kwargs): assert "results" in self.__input_dict, "Missing results list" assert "mode" in self.__input_dict, "Missing mode" assert "stix" in self.__input_dict, "Missing stix flag" + if "webhook" not in self.__input_dict or self.__input_dict["webhook"] is None: + self.__input_dict["webhook"] = "" path = self.__input_dict["path"] self.__path = Path(path) @@ -193,6 +253,7 @@ def run(self, **kwargs): (self.__input_dict["results"], dict), (self.__input_dict["mode"], self.Mode), (self.__input_dict["stix"], bool), + (self.__input_dict["webhook"], str), ] ) @@ -260,5 +321,11 @@ def run(self, **kwargs): Stix(type_of_analysis=self.__input_dict["mode"].value).build_and_save( results_to_stix, modules, str(stix_output_path) ) + self.__logging.debug("Checks if needs webhook...") + if "webhook" in self.__input_dict and self.__input_dict["webhook"]: + self.__logging.info("Starting webhook...") + self.__send_webhook( + self.__input_dict["webhook"], results=results, modules=modules + ) # todo: add PDF library diff --git a/run.py b/run.py index b900f59..be807e5 100644 --- a/run.py +++ b/run.py @@ -135,6 +135,15 @@ help="Generate STIX2 compliant output.", default=False, ) + parser.add_argument( + "--webhook", + dest="webhook", + action="store", + type=str, + nargs="?", + default="", + help="Add a webhook url to send the results.", + ) # todo add default aliases configurations for analysis # configurations.add_argument() args = parser.parse_args() diff --git a/tlsa/tlsa.py b/tlsa/tlsa.py index 4daf72f..2297093 100644 --- a/tlsa/tlsa.py +++ b/tlsa/tlsa.py @@ -124,6 +124,7 @@ def __start_analysis(self, args): group_by=args.group_by, apply_fix=args.apply_fix, stix=args.stix, + webhook=args.webhook, ) elif args.apk: Core( @@ -135,6 +136,7 @@ def __start_analysis(self, args): type_of_analysis=Core.Analysis.APK, group_by=args.group_by, stix=args.stix, + webhook=args.webhook, ) elif args.domain_file: Core( @@ -146,6 +148,7 @@ def __start_analysis(self, args): type_of_analysis=Core.Analysis.DOMAINS, group_by=args.group_by, stix=args.stix, + webhook=args.webhook, ) elif args.file: if isinstance(args.configuration, list): @@ -164,6 +167,7 @@ def __start_analysis(self, args): stix=args.stix, openssl_version=args.openssl, ignore_openssl=args.ignore_openssl, + webhook=args.webhook, ) else: # must be args.list, unless argparse throws error. diff --git a/utils/prune.py b/utils/prune.py index 37943db..d3a5986 100644 --- a/utils/prune.py +++ b/utils/prune.py @@ -10,6 +10,6 @@ def pruner(data): for k, v in data.items(): if isinstance(v, dict): v = pruner(v) - if not v in (u"", None, {}): + if not v in ("", None, {}): new_data[k] = v return new_data From 1da4bd64c989dde56fd14b26d6a6da2fe41d4028 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Mon, 4 Apr 2022 10:28:33 +0200 Subject: [PATCH 009/209] Nginx check_redirects --- .../nginx/nginx_configuration_base.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index ad2e7b3..5fee569 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -336,6 +336,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): if openssl is None: openssl = "" Validator([(openssl, str)]) + # nginx non ha direttive per ssl compression, dipende solo dalla versione utilizzata if not ignore_openssl: if openssl: @@ -343,12 +344,11 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): else: is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) - return not is_safe and (key not in vhost or vhost[key] != self.__value) + return not is_safe else: - return ( - key not in vhost or vhost[key] != self.__value - ) # is vulnerable if True + return True # potentially always vulnerable without OpenSSL version check + # is vulnerable if True class Nginx_parse_configuration_checks_redirect(): """ @@ -356,20 +356,18 @@ class Nginx_parse_configuration_checks_redirect(): """ def __init__(self): - return - self.__keys = ["RewriteEngine", "RewriteRule"] + self.__key = "return"; def is_empty(self, vhost): """ - Check if vhost doesn't have the RewriteEngine and RewriteRule directives. + Check if vhost doesn't have the 'return' directive. :param vhost: VirtualHost object. :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. :rtype: bool """ - return - return True in (key not in vhost for key in self.__keys) + return self.__key not in vhost or "301" not in vhost[self.__key] def fix(self, vhost): """ @@ -408,11 +406,8 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS redirect. :rtype: bool """ - return - RewriteEngine, RewriteRule = self.__keys return ( - RewriteEngine not in vhost - or RewriteRule not in vhost - or vhost[RewriteEngine] != "on" - or vhost[RewriteRule] != "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + self.__key not in vhost + or "301" not in vhost[self.__key] + or ("https" not in args for args in vhost[self.__key]) ) # vulnerable if True From d6d04bf4f7a24f69110da151a4ac8d6a7c37d379 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Mon, 4 Apr 2022 10:53:53 +0200 Subject: [PATCH 010/209] Fix openSSL class ihneritance --- .../apache/apache_configuration_base.py | 9 ++++++--- modules/configuration/configuration_base.py | 12 ++++++------ .../configuration/nginx/nginx_configuration_base.py | 9 ++++++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/modules/configuration/apache/apache_configuration_base.py b/modules/configuration/apache/apache_configuration_base.py index 63fade9..2ce6cc3 100644 --- a/modules/configuration/apache/apache_configuration_base.py +++ b/modules/configuration/apache/apache_configuration_base.py @@ -7,7 +7,7 @@ class Apache_parse_configuration_protocols(): Check if vhost is vulnerable to TLS SSLProtocol bad configuration. """ - def __init__(self, openssl: str, protocols: dict): + def __init__(self, openssl: str, protocols: dict, openssl_class): """ :param openssl: OpenSSL version. :type openssl: str @@ -16,6 +16,7 @@ def __init__(self, openssl: str, protocols: dict): """ self.__openssl = openssl self.__protocols = protocols + self.openSSL = openssl_class self.__key = "SSLProtocol" Validator([(openssl, str), (protocols, dict)]) @@ -112,9 +113,10 @@ class Apache_parse_configuration_ciphers(): Check if vhost is vulnerable to misconfigured TLS cipher. """ - def __init__(self, openssl: str, ciphers: list): + def __init__(self, openssl: str, ciphers: list, openssl_class): self.__openssl = openssl self.__ciphers = ciphers + self.openSSL = openssl_class self.__key = "SSLCipherSuite" Validator([(openssl, str), (ciphers, list)]) @@ -269,8 +271,9 @@ class Apache_parse_configuration_checks_compression(): :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - def __init__(self, openssl: str): + def __init__(self, openssl: str, openssl_class): self.__openssl = openssl + self.openSSL = openssl_class self.__key = "SSLCompression" self.__value = "Off" Validator([(openssl, str)]) diff --git a/modules/configuration/configuration_base.py b/modules/configuration/configuration_base.py index eabe8a8..d2f83c1 100644 --- a/modules/configuration/configuration_base.py +++ b/modules/configuration/configuration_base.py @@ -158,9 +158,9 @@ def __init__(self, openssl: str, protocols: dict): def set_webserver(self, webserver: WebserverType): self.__webserver_type = webserver if webserver == WebserverType.APACHE: - self.__execution_class = Apache_parse_configuration_protocols(self.__openssl, self.__protocols) + self.__execution_class = Apache_parse_configuration_protocols(self.__openssl, self.__protocols, self.openSSL) elif webserver == WebserverType.NGINX: - self.__execution_class = Nginx_parse_configuration_protocols(self.__openssl, self.__protocols) + self.__execution_class = Nginx_parse_configuration_protocols(self.__openssl, self.__protocols, self.openSSL) def is_empty(self, vhost): """ @@ -230,9 +230,9 @@ def __init__(self, openssl: str, ciphers: list): def set_webserver(self, webserver: WebserverType): self.__webserver_type = webserver if webserver == WebserverType.APACHE: - self.__execution_class = Apache_parse_configuration_ciphers(self.__openssl, self.__ciphers) + self.__execution_class = Apache_parse_configuration_ciphers(self.__openssl, self.__ciphers, self.openSSL) elif webserver == WebserverType.NGINX: - self.__execution_class = Nginx_parse_configuration_ciphers(self.__openssl, self.__ciphers) + self.__execution_class = Nginx_parse_configuration_ciphers(self.__openssl, self.__ciphers, self.openSSL) def is_tls(self, vhost, version=3): """ @@ -358,9 +358,9 @@ def __init__(self, openssl: str): def set_webserver(self, webserver: WebserverType): self.__webserver_type = webserver if webserver == WebserverType.APACHE: - self.__execution_class = Apache_parse_configuration_checks_compression(self.__openssl) + self.__execution_class = Apache_parse_configuration_checks_compression(self.__openssl, self.openSSL) elif webserver == WebserverType.NGINX: - self.__execution_class = Nginx_parse_configuration_checks_compression(self.__openssl) + self.__execution_class = Nginx_parse_configuration_checks_compression(self.__openssl, self.openSSL) def is_tls(self, vhost, version=3): diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index 5fee569..ca87a13 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -7,7 +7,7 @@ class Nginx_parse_configuration_protocols(): Check if vhost is vulnerable to TLS SSLProtocol bad configuration. """ - def __init__(self, openssl: str, protocols: dict): + def __init__(self, openssl: str, protocols: dict, openssl_class): """ :param openssl: OpenSSL version. :type openssl: str @@ -16,6 +16,7 @@ def __init__(self, openssl: str, protocols: dict): """ self.__openssl = openssl self.__protocols = protocols + self.openSSL = openssl_class self.__key = "ssl_protocols" Validator([(openssl, str), (protocols, dict)]) @@ -113,9 +114,10 @@ class Nginx_parse_configuration_ciphers(): Check if vhost is vulnerable to misconfigured TLS cipher. """ - def __init__(self, openssl: str, ciphers: list): + def __init__(self, openssl: str, ciphers: list, openssl_class): self.__openssl = openssl self.__ciphers = ciphers + self.openSSL = openssl_class self.__key = "ssl_ciphers" Validator([(openssl, str), (ciphers, list)]) @@ -271,9 +273,10 @@ class Nginx_parse_configuration_checks_compression(): :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` """ - def __init__(self, openssl: str): + def __init__(self, openssl: str, openssl_class): self.__openssl = openssl self.__key = "ssl_compression" + self.openSSL = openssl_class self.__value = "Off" Validator([(openssl, str)]) From fd5f1a28b75bdc5b03304b07e379dcfecb9e5dbb Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Tue, 5 Apr 2022 11:36:50 +0200 Subject: [PATCH 011/209] Fix conditions and some tests --- .../nginx/nginx_configuration_base.py | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index ca87a13..adfd201 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -24,7 +24,7 @@ def is_empty(self, vhost): """ Check if vhost doesn't have the contextual directive. - :param vhost: VirtualHost object. + :param vhost: "VirtualHost" object. :type vhost: dict :returns: True if vhost doesn't have the contextual directive. :rtype: bool @@ -35,8 +35,8 @@ def is_tls(self, vhost, version=3): """ Check if vhost is using only the TLS version x. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param version: TLS version to check. :type version: int :returns: True if vhost is using ONLY the TLS version x. @@ -52,8 +52,8 @@ def fix(self, vhost): """ Fix TLS/SSL protocol bad configuration. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ raise NotImplementedError key = self.__key @@ -74,8 +74,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ Check if vhost is vulnerable to TLS SSLProtocol bad configuration. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -91,6 +91,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): if openssl is None: openssl = "" Validator([(openssl, str)]) + if not ignore_openssl: if openssl: is_safe = self.openSSL.is_safe(ver1=openssl_greater_than, ver2=openssl) @@ -103,6 +104,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): for cipher, operation in self.__protocols.items() ) else: + # Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3]; + # Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2; return True in ( cipher.lower() not in ([protocol.lower() for protocol in vhost[key]] if key in vhost else "") @@ -125,8 +128,8 @@ def is_tls(self, vhost, version=3): """ Check if vhost is using ONLY the TLS version x. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param version: TLS version to check. :type version: int :returns: True if vhost is using ONLY the TLS version x. @@ -142,8 +145,8 @@ def is_empty(self, vhost): """ Check if vhost doesn't have the contextual directive. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ @@ -153,8 +156,8 @@ def fix(self, vhost): """ Fix misconfigured TLS cipher in vhost. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ raise NotImplementedError key = self.__key @@ -175,8 +178,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ Check if vhost is vulnerable to misconfigured TLS cipher. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -199,12 +202,15 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): is_safe = self.openSSL.is_safe(ver1=openssl_greater_than) return not is_safe and True in ( - "!" + cipher.lower() not in ([c.lower() for c in vhost[key]] if key in vhost else "") + "!" + cipher.lower() not in (vhost[key][0].lower() if (key in vhost and len(vhost[key]) != 0) else "") for cipher in self.__ciphers ) else: + # Syntax: ssl_ciphers ciphers; + # Default: ssl_ciphers HIGH:!aNULL:!MD5; + # The ciphers are specified in the format understood by the OpenSSL library return True in ( - "!" + cipher.lower() not in ([c.lower() for c in vhost[key]] if key in vhost else "") + "!" + cipher.lower() not in (vhost[key][0].lower() if (key in vhost and len(vhost[key]) != 0) else "") for cipher in self.__ciphers ) # is vulnerable if True @@ -220,8 +226,8 @@ def is_empty(self, vhost): """ Check if vhost doesn't have the header directive. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :returns: True if vhost doesn't have the header directive. :rtype: bool """ @@ -231,8 +237,8 @@ def fix(self, vhost): """ Fix misconfigured TLS strict security in vhost. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ raise NotImplementedError key = self.__key @@ -251,8 +257,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ Check if vhost is vulnerable to misconfigured TLS strict security. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -269,8 +275,8 @@ class Nginx_parse_configuration_checks_compression(): """ Check if vhost is vulnerable to misconfigured TLS compression. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ def __init__(self, openssl: str, openssl_class): @@ -284,8 +290,8 @@ def is_tls(self, vhost, version=3): """ Check if vhost is using only a specific version of TLS. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param version: TLS version. :type version: int :returns: True if vhost is using only a specific version of TLS. @@ -301,8 +307,8 @@ def is_empty(self, vhost): """ Check if vhost doesn't have the SSLCompression directive. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :returns: True if vhost doesn't have the SSLCompression directive. :rtype: bool """ @@ -312,8 +318,8 @@ def fix(self, vhost): """ Fix misconfigured TLS compression in vhost. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ # no directive fix available for nginx pass @@ -322,8 +328,8 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ Check if vhost is vulnerable to misconfigured TLS compression. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -365,8 +371,8 @@ def is_empty(self, vhost): """ Check if vhost doesn't have the 'return' directive. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. :rtype: bool """ @@ -376,8 +382,8 @@ def fix(self, vhost): """ Fix misconfigured TLS redirect in vhost. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict """ return RewriteEngine, RewriteRule = self.__keys @@ -400,8 +406,8 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): """ Check if vhost is vulnerable to misconfigured TLS redirect. - :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :param vhost: "VirtualHost" object. + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -409,8 +415,10 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS redirect. :rtype: bool """ + # Syntax: return_code URL; return ( self.__key not in vhost or "301" not in vhost[self.__key] - or ("https" not in args for args in vhost[self.__key]) + or ("301" in vhost[self.__key] + and "https" not in vhost[self.__key][-1].lower()) ) # vulnerable if True From d3faabdb265e1833e406bb91c7f3c939d200faef Mon Sep 17 00:00:00 2001 From: matteounitn <32160291+matteounitn@users.noreply.github.com> Date: Wed, 6 Apr 2022 21:06:45 +0200 Subject: [PATCH 012/209] webhook --- modules/report.py | 5 ++++- utils/booleanize.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/modules/report.py b/modules/report.py index 475e910..b6a4ea3 100644 --- a/modules/report.py +++ b/modules/report.py @@ -16,6 +16,7 @@ from distutils.dir_util import copy_tree as cp from utils.prune import pruner from pprint import pformat +from utils.booleanize import boolean_results_hosts, boolean_results_modules class Report: @@ -325,7 +326,9 @@ def run(self, **kwargs): if "webhook" in self.__input_dict and self.__input_dict["webhook"]: self.__logging.info("Starting webhook...") self.__send_webhook( - self.__input_dict["webhook"], results=results, modules=modules + self.__input_dict["webhook"], + results=results["results"], + modules=modules, ) # todo: add PDF library diff --git a/utils/booleanize.py b/utils/booleanize.py index 527753a..2188a4d 100644 --- a/utils/booleanize.py +++ b/utils/booleanize.py @@ -16,3 +16,39 @@ def boolean_results(modules: list or dict, raw_results: dict) -> dict: for module in modules: b_res[module] = True if module in res and res else False return b_res + + +def boolean_results_hosts(modules: list or dict, raw_results: dict) -> dict: + """ + Booleanize the results of one or more modules. + :param modules: list of modules to be booleanized + :type modules: list + :param raw_results: dictionary of raw results + :type raw_results: dict + :return: dictionary of booleanized results + :rtype: dict + """ + b_res = {} + res = pruner(raw_results) + for host in raw_results: + b_res[host] = {} + for module in modules: + b_res[host][module] = True if module in res[host] and res[host] else False + return b_res + + +def boolean_results_modules(raw_results: dict) -> dict: + """ + Booleanize the results of one or more modules. + :param raw_results: dictionary of raw results + :type raw_results: dict + :return: dictionary of booleanized results + :rtype: dict + """ + b_res = {} + res = pruner(raw_results) + for module in raw_results: + b_res[module] = {} + for host in raw_results[module]: + b_res[module][host] = True if module in res[host] and res[host] else False + return b_res From 26ac29520e9ad8f69a89753f16d5dfbecf8adbec Mon Sep 17 00:00:00 2001 From: matteounitn <32160291+matteounitn@users.noreply.github.com> Date: Wed, 6 Apr 2022 22:16:37 +0200 Subject: [PATCH 013/209] added prometheus output (beta) with parameter --- modules/core.py | 11 +++++++++ modules/report.py | 57 +++++++++++++++++++++++++++++++++++++++++++++-- run.py | 9 ++++++++ tlsa/tlsa.py | 4 ++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index 7258bda..5b83dea 100644 --- a/modules/core.py +++ b/modules/core.py @@ -56,6 +56,7 @@ def __init__( ignore_openssl=False, stix=False, webhook="", + prometheus="", ): """ :param hostname_or_path: hostname or path to scan @@ -82,6 +83,8 @@ def __init__( :type stix: bool :param webhook: webhook to send the report to :type webhook: str + :param prometheus: prometheus output + :type prometheus: str """ if to_exclude is None: to_exclude = [] @@ -105,6 +108,7 @@ def __init__( ignore_openssl=ignore_openssl, stix=stix, webhook=webhook, + prometheus=prometheus, ) self.__cache[configuration] = self.__load_configuration(modules) self.__exec( @@ -163,6 +167,12 @@ def input(self, **kwargs): else kwargs["webhook"], str, ), + ( + "" + if "prometheus" not in kwargs or not kwargs["prometheus"] + else kwargs["prometheus"], + str, + ), ] ) kwargs["to_exclude"] = list(map(str.lower, kwargs["to_exclude"])) @@ -408,6 +418,7 @@ def __call_output_modules(self, res: dict): else Report_module.Mode.HOSTS, stix=self.__input_dict["stix"], webhook=self.__input_dict["webhook"], + prometheus=self.__input_dict["prometheus"], ) self.__logging.debug("Output generated.") diff --git a/modules/report.py b/modules/report.py index b6a4ea3..c5503d0 100644 --- a/modules/report.py +++ b/modules/report.py @@ -16,7 +16,6 @@ from distutils.dir_util import copy_tree as cp from utils.prune import pruner from pprint import pformat -from utils.booleanize import boolean_results_hosts, boolean_results_modules class Report: @@ -50,6 +49,8 @@ def input(self, **kwargs): * *path* (string) -- Path to the report. * *mode* (Mode) -- Report mode. * *stix* (bool) -- If True, the report will be in STIX format. + * *webhook* (string) -- Webhook to send the report to. + * *prometheus* (string) -- Prometheus file path. """ self.__input_dict = kwargs @@ -235,6 +236,7 @@ def run(self, **kwargs): * *mode* (Mode) -- Report mode. * *stix* (bool) -- If True, the report will be generated in STIX format. * *webhook* (string) -- Webhook to send the report to. + * *prometheus* (string) -- Prometheus output path. """ self.input(**kwargs) @@ -255,6 +257,12 @@ def run(self, **kwargs): (self.__input_dict["mode"], self.Mode), (self.__input_dict["stix"], bool), (self.__input_dict["webhook"], str), + ( + "" + if "prometheus" not in self.__input_dict or not self.__input_dict["prometheus"] + else kwargs["prometheus"], + str, + ), ] ) @@ -327,8 +335,53 @@ def run(self, **kwargs): self.__logging.info("Starting webhook...") self.__send_webhook( self.__input_dict["webhook"], - results=results["results"], + results=results, modules=modules, ) + if 'prometheus' in self.__input_dict and self.__input_dict['prometheus'] != '': + self.__logging.info("Starting prometheus...") + + output_path_prometheus = f"{output_file.absolute().parent}{sep}{output_file.stem}_prometheus.log" if not self.__input_dict['prometheus'] else self.__input_dict['prometheus'] + Prometheus(results=results, modules=modules).run(output_path_prometheus) # todo: add PDF library +class Prometheus: + """ + This class generates a prometheus compliant output + """ + def __init__(self,results,modules): + self.__logging = Logger("Prometheus") + Validator( + [ + (results,dict), + (modules,dict), + ] + ) + self.results = results + self.modules = modules + self.output=[] + + + def generate_output(self): + """ + This method will generate the output in the form of + tls_check{vhost=hostname_analyzed,vulnerability=Module_name} 1 if vulnerable, 0 if not + """ + self.__logging.debug("Generating output...") + for module in self.modules: + for host in self.results: + if module in self.results[host]: + self.output.append(f"tls_check{{vhost=\"{host}\",vulnerability=\"{module}\"}} 1") + else: + self.output.append(f"tls_check{{vhost=\"{host}\",vulnerability=\"{module}\"}} 0") + + + + + def run(self,file_name:str): + self.generate_output() + with open(file_name,"w") as f: + self.__logging.debug(f"Writing output in file {file_name}") + for line in self.output: + f.write(line+"\n") + self.__logging.info(f"Prometheus output generated at {file_name}") diff --git a/run.py b/run.py index be807e5..9aba90a 100644 --- a/run.py +++ b/run.py @@ -144,6 +144,15 @@ default="", help="Add a webhook url to send the results.", ) + parser.add_argument( + "--prometheus", + dest="prometheus", + action="store", + type=str, + nargs="?", + default="", + help="Generate the prometheus output in a default path or in the specified path.", + ) # todo add default aliases configurations for analysis # configurations.add_argument() args = parser.parse_args() diff --git a/tlsa/tlsa.py b/tlsa/tlsa.py index 2297093..e289f66 100644 --- a/tlsa/tlsa.py +++ b/tlsa/tlsa.py @@ -125,6 +125,7 @@ def __start_analysis(self, args): apply_fix=args.apply_fix, stix=args.stix, webhook=args.webhook, + prometheus=args.prometheus, ) elif args.apk: Core( @@ -137,6 +138,7 @@ def __start_analysis(self, args): group_by=args.group_by, stix=args.stix, webhook=args.webhook, + prometheus=args.prometheus, ) elif args.domain_file: Core( @@ -149,6 +151,7 @@ def __start_analysis(self, args): group_by=args.group_by, stix=args.stix, webhook=args.webhook, + prometheus=args.prometheus, ) elif args.file: if isinstance(args.configuration, list): @@ -168,6 +171,7 @@ def __start_analysis(self, args): openssl_version=args.openssl, ignore_openssl=args.ignore_openssl, webhook=args.webhook, + prometheus=args.prometheus, ) else: # must be args.list, unless argparse throws error. From ece02c24640adb58bbd0900d8532b1f4c8ba54ba Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 10 Apr 2022 23:39:39 +0200 Subject: [PATCH 014/209] Start of introduction of TLS-Scanner --- configs/mitigations/ALPACA.json | 18 ++++++++++++++++++ configs/mitigations/RACCOON.json | 19 +++++++++++++++++++ .../{POODLE.json => SSL_POODLE.json} | 0 configs/mitigations/TLS_POODLE.json | 18 ++++++++++++++++++ .../server/{poodle.json => sslpoodle.json} | 4 ++-- default_server.json | 2 +- modules/core.py | 4 ++++ modules/server/{poodle.py => sslpoodle.py} | 6 +++--- 8 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 configs/mitigations/ALPACA.json create mode 100644 configs/mitigations/RACCOON.json rename configs/mitigations/{POODLE.json => SSL_POODLE.json} (100%) create mode 100644 configs/mitigations/TLS_POODLE.json rename configs/modules/server/{poodle.json => sslpoodle.json} (83%) rename modules/server/{poodle.py => sslpoodle.py} (89%) diff --git a/configs/mitigations/ALPACA.json b/configs/mitigations/ALPACA.json new file mode 100644 index 0000000..e578e98 --- /dev/null +++ b/configs/mitigations/ALPACA.json @@ -0,0 +1,18 @@ +{ + "Entry": { + "Name": "ALPACA", + "ExtendedName": "Application Layer Protocol Confusion-Analyzing and Mitigating Cracks in TLS Authentication", + "CVE": "", + "CVSS3": "", + "#comment": "", + "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", + "Mitigation": { + "Textual": "Enable the TLS Extensions SNI and ALPN.", + "Apache": "", + "Nginx": "" + + } + }, + "#comment": " https://alpaca-attack.com/ ", + "#omit-xml-declaration": "yes" +} \ No newline at end of file diff --git a/configs/mitigations/RACCOON.json b/configs/mitigations/RACCOON.json new file mode 100644 index 0000000..8f09167 --- /dev/null +++ b/configs/mitigations/RACCOON.json @@ -0,0 +1,19 @@ +{ + "Entry": { + "Name": "Raccoon", + "ExtendedName": "Raccoon", + "CVE": "2020-1968", + "CVSS3": "3.7 (Low)", + "#comment": " AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N ", + "Description": "The Raccoon attack exploits a flaw in the TLS specification which can lead to an attacker being able to compute the pre-master secret in connections which have used a Diffie-Hellman (DH) based ciphersuite. In such a case this would result in the attacker being able to eavesdrop on all encrypted communications sent over that TLS connection.", + "Mitigation": { + "Textual": "Using of 'static' DH ciphersuites should be completly avoided. Also reuising the same DHE secret for multiple connections should be avoided.", + "Apache": "", + "Nginx": "" + + } + }, + "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2020-1968 ", + "#comment1": " https://raccoon-attack.com/ ", + "#omit-xml-declaration": "yes" +} \ No newline at end of file diff --git a/configs/mitigations/POODLE.json b/configs/mitigations/SSL_POODLE.json similarity index 100% rename from configs/mitigations/POODLE.json rename to configs/mitigations/SSL_POODLE.json diff --git a/configs/mitigations/TLS_POODLE.json b/configs/mitigations/TLS_POODLE.json new file mode 100644 index 0000000..5b8e69c --- /dev/null +++ b/configs/mitigations/TLS_POODLE.json @@ -0,0 +1,18 @@ +{ + "Entry": { + "Name": "TLS Poodle", + "ExtendedName": "TLS Padding Oracle On Downgraded Legacy Encryption", + "CVE": "", + "CVSS3": "", + "#comment": "", + "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", + "Mitigation": { + "Textual": "Enable the TLS Extensions SNI and ALPN.", + "Apache": "", + "Nginx": "" + + } + }, + "#comment": " https://alpaca-attack.com/ ", + "#omit-xml-declaration": "yes" +} \ No newline at end of file diff --git a/configs/modules/server/poodle.json b/configs/modules/server/sslpoodle.json similarity index 83% rename from configs/modules/server/poodle.json rename to configs/modules/server/sslpoodle.json index 0a16a5f..dc86d06 100644 --- a/configs/modules/server/poodle.json +++ b/configs/modules/server/sslpoodle.json @@ -8,8 +8,8 @@ } ], "description":"This modules runs the vulnerability check for POODLE.", - "path":"modules/server/poodle.py", - "class_name":"Poodle", + "path":"modules/server/sslpoodle.py", + "class_name":"SSLPoodle", "output":[ { "name":"results", diff --git a/default_server.json b/default_server.json index 5056e34..d9f67bc 100644 --- a/default_server.json +++ b/default_server.json @@ -19,7 +19,7 @@ "mitzvah", "nomore", "pfs", - "poodle", + "sslpoodle", "renegotiation", "robot", "sloth", diff --git a/modules/core.py b/modules/core.py index 885a584..66aebd6 100644 --- a/modules/core.py +++ b/modules/core.py @@ -103,11 +103,15 @@ def __init__( stix=stix, ) self.__cache[configuration] = self.__load_configuration(modules) + print(self.__cache[configuration]) + self.__exec( type_of_analysis=self.__input_dict["type_of_analysis"], hostname_or_path=self.__input_dict["hostname_or_path"], configuration=self.__input_dict["configuration"], ) + '''''' + def __string_output_type(self, kwargs_type: Report) -> str: """ diff --git a/modules/server/poodle.py b/modules/server/sslpoodle.py similarity index 89% rename from modules/server/poodle.py rename to modules/server/sslpoodle.py index 7ec323c..d43d0bd 100644 --- a/modules/server/poodle.py +++ b/modules/server/sslpoodle.py @@ -4,13 +4,13 @@ from utils.mitigations import load_mitigation -class Poodle(Testssl_base): +class SSLPoodle(Testssl_base): """ Analysis of the poodle testssl results """ conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) - stix = Bundled(mitigation_object=load_mitigation("POODLE")) + stix = Bundled(mitigation_object=load_mitigation("SSL POODLE")) def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ @@ -27,7 +27,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") if condition: - result["mitigation"] = load_mitigation("POODLE") + result["mitigation"] = load_mitigation("SSL POODLE") return result if condition else {} # to override From 3e39f615f78049ad929df06ad3ea4d9b290146f0 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:38:41 +0200 Subject: [PATCH 015/209] WIP conf.fix --- .../nginx/nginx_configuration_base.py | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index adfd201..167d31d 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -55,16 +55,24 @@ def fix(self, vhost): :param vhost: "VirtualHost" object. :type vhost: dict """ - raise NotImplementedError key = self.__key - backup = vhost[key] if key in vhost else "" + ciphers = ['SSLv2', 'SSLv3','TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'] + ciphers_default = ['TLSv1', 'TLSv1.1', 'TLSv1.2'] + backup = vhost[key].copy() if key in vhost else [] v = Validator() for cipher, operation in self.__protocols.items(): v.string(cipher) - vhost[key] = ( - f"{(vhost[key] if key in vhost and vhost[key] else 'ALL ')}" - f"{' ' if key in vhost and vhost[key] else ''}{operation}{cipher}" - ) + if operation == '-': + if key in vhost: + if len(vhost[key]) == 1: + if vhost[key][0].lower() == cipher.lower(): + vhost[key] = ciphers_default + elif cipher in vhost[key]: + vhost[key].remove(cipher) + else: + vhost[key] = ciphers_default + else: + raise NotImplementedError return { "before": f"{key} {backup}" if backup else "", "after": f"{key} {vhost[key]}", @@ -159,16 +167,20 @@ def fix(self, vhost): :param vhost: "VirtualHost" object. :type vhost: dict """ - raise NotImplementedError key = self.__key v = Validator() - backup = vhost[key] if key in vhost else "" + backup = vhost[key].copy() if key in vhost else [] for cipher in self.__ciphers: v.string(cipher) - vhost[key] = ( - f"{vhost[key] if key in vhost and vhost[key] else ''}" - f"{':' if key in vhost and vhost[key] else ''}!{cipher.upper()}" - ) + if key in vhost: + if len(vhost[key]) == 1: + # ssl_ciphers directive has only one argument + vhost[key][0] += f":!{cipher.upper()}" + else: + vhost[key] = [f'HIGH:!aNULL:!MD5:!{cipher.upper()}'] + else: + vhost[key] = [f'HIGH:!aNULL:!MD5:!{cipher.upper()}'] + return { "before": f"{key} {backup}" if backup else "", "after": f"{key} {vhost[key]}", @@ -240,12 +252,12 @@ def fix(self, vhost): :param vhost: "VirtualHost" object. :type vhost: dict """ - raise NotImplementedError key = self.__key - backup = vhost[key] if key in vhost else "" - to_add = 'always set Strict-Transport-Security "max-age=63072000"' + backup = vhost[key].copy() if key in vhost else [] + to_add = ['Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'] if key in vhost: - vhost[key] += f";{to_add}" + vhost[key] = [vhost[key]] + vhost[key].append(to_add) else: vhost[key] = to_add return { @@ -322,7 +334,7 @@ def fix(self, vhost): :type vhost: dict """ # no directive fix available for nginx - pass + return {} def condition(self, vhost, openssl: str = None, ignore_openssl=False): """ @@ -385,21 +397,17 @@ def fix(self, vhost): :param vhost: "VirtualHost" object. :type vhost: dict """ - return - RewriteEngine, RewriteRule = self.__keys - backup_rewrite_engine = vhost[RewriteEngine] if RewriteEngine in vhost else "" - backup_rewrite_rule = vhost[RewriteRule] if RewriteRule in vhost else "" - vhost[RewriteEngine] = "on" - vhost[RewriteRule] = "^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]" + key = self.__key + backup = vhost[key].copy() if key in vhost else [] + if key not in vhost: + vhost[key] = ['301', 'https://$host$request_uri'] + else: + # TODO: Verificare quale altro c'è già? + pass + return { - "before": { - "RewriteEngine": backup_rewrite_engine, - "RewriteRule": backup_rewrite_rule, - }, - "after": { - "RewriteEngine": vhost[RewriteEngine], - "RewriteRule": vhost[RewriteRule], - }, + "before": f"{key} {backup}" if backup else "", + "after": f"{key} {vhost[key]}" } def condition(self, vhost, openssl=None, ignore_openssl=False): From c81f4d499b6b84c7a9201761fc8c9fb7e0edc61d Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Tue, 12 Apr 2022 15:34:53 +0200 Subject: [PATCH 016/209] Half implementation - (SSL/TLS)Poodle - Alpaca --- configs/modules/server/alpaca.json | 20 ++ configs/modules/server/sslpoodle.json | 2 +- configs/modules/server/tlspoodle.json | 20 ++ modules/core.py | 77 ++++- modules/server/alpaca.py | 51 ++++ modules/server/tlspoodle.py | 50 ++++ modules/server/tlsscanner_base.py | 168 +++++++++++ modules/server/wrappers/testssl.py | 7 + modules/server/wrappers/tlsscanner.py | 403 ++++++++++++++++++++++++++ 9 files changed, 793 insertions(+), 5 deletions(-) create mode 100644 configs/modules/server/alpaca.json create mode 100644 configs/modules/server/tlspoodle.json create mode 100644 modules/server/alpaca.py create mode 100644 modules/server/tlspoodle.py create mode 100644 modules/server/tlsscanner_base.py create mode 100644 modules/server/wrappers/tlsscanner.py diff --git a/configs/modules/server/alpaca.json b/configs/modules/server/alpaca.json new file mode 100644 index 0000000..44f109a --- /dev/null +++ b/configs/modules/server/alpaca.json @@ -0,0 +1,20 @@ +{ + "input":[ + { + "name":"hostname", + "type":"str", + "description":"Hostname to analyze", + "required":"True" + } + ], + "description":"This modules runs the vulnerability check for Alpaca.", + "path":"modules/server/alpaca.py", + "class_name":"Alpaca", + "output":[ + { + "name":"results", + "type":"dict", + "description":"results of the scan" + } + ] + } \ No newline at end of file diff --git a/configs/modules/server/sslpoodle.json b/configs/modules/server/sslpoodle.json index dc86d06..5018e39 100644 --- a/configs/modules/server/sslpoodle.json +++ b/configs/modules/server/sslpoodle.json @@ -7,7 +7,7 @@ "required":"True" } ], - "description":"This modules runs the vulnerability check for POODLE.", + "description":"This modules runs the vulnerability check for SSL POODLE.", "path":"modules/server/sslpoodle.py", "class_name":"SSLPoodle", "output":[ diff --git a/configs/modules/server/tlspoodle.json b/configs/modules/server/tlspoodle.json new file mode 100644 index 0000000..53b7808 --- /dev/null +++ b/configs/modules/server/tlspoodle.json @@ -0,0 +1,20 @@ +{ + "input":[ + { + "name":"hostname", + "type":"str", + "description":"Hostname to analyze", + "required":"True" + } + ], + "description":"This modules runs the vulnerability check for TLS POODLE.", + "path":"modules/server/tlspoodle.py", + "class_name":"TLSPoodle", + "output":[ + { + "name":"results", + "type":"dict", + "description":"results of the scan" + } + ] + } \ No newline at end of file diff --git a/modules/core.py b/modules/core.py index 66aebd6..25ee6fe 100644 --- a/modules/core.py +++ b/modules/core.py @@ -3,7 +3,9 @@ from modules.configuration.configuration import Configuration from modules.server.testssl_base import Testssl_base +from modules.server.tlsscanner_base import TLS_Scanner_base from modules.server.wrappers.testssl import Testssl +from modules.server.wrappers.tlsscanner import TLS_Scanner from utils.booleanize import boolean_results from utils.logger import Logger from utils.colors import Color @@ -104,13 +106,13 @@ def __init__( ) self.__cache[configuration] = self.__load_configuration(modules) print(self.__cache[configuration]) - + '''''' self.__exec( type_of_analysis=self.__input_dict["type_of_analysis"], hostname_or_path=self.__input_dict["hostname_or_path"], configuration=self.__input_dict["configuration"], ) - '''''' + def __string_output_type(self, kwargs_type: Report) -> str: @@ -229,6 +231,33 @@ def __add_testssl_args(self, module: Testssl_base, testssl_args: list) -> list: if self.__is_testssl(module): testssl_args += module._arguments return testssl_args + + def __is_tls_scanner(self, module: object) -> bool: + """ + Checks if the module is a tls_scanner module + + :param module: module to check + :type module: object + :return: True if the module is a tls_scanner module + :rtype: bool + """ + return isinstance(module, TLS_Scanner_base) + + def __add_tls_scanner_args(self, module: TLS_Scanner_base, tls_scanner_args: list) -> list: + """ + Adds tls_scanner arguments from the module + + :param module: module to add arguments from + :type module: TLS_Scanner_base + :param tls_scanner_args: list of arguments + :type tls_scanner_args: list + :return: list of arguments + :rtype: list + + """ + if self.__is_tls_scanner(module): + tls_scanner_args += module._arguments + return tls_scanner_args def __conf_analysis( self, @@ -306,6 +335,37 @@ def __preanalysis_testssl( ) self.__logging.debug(f"Preanalysis testssl done.") + def __preanalysis_tls_scanner( + self, tls_scanner_args: list, type_of_analysis: Analysis, hostname: str, port: str + ): + """ + Preanalysis of tls scanner + + :param tls_scanner_args: arguments for tls_scanner + :type tls_scanner_args: list + :param type_of_analysis: type of analysis + :type type_of_analysis: Analysis + :param hostname: hostname + :type hostname: str + :param port: port to use + :type port: str + :return: preanalysis + :rtype: dict + """ + if tls_scanner_args and ( + type_of_analysis == self.Analysis.HOST + or type_of_analysis == self.Analysis.DOMAINS + ): + self.__logging.debug( + f"Starting preanalysis tls_scanner with args {tls_scanner_args}..." + ) + TLS_Scanner().run( + hostname=f"{hostname}:{port}", + args=tls_scanner_args, + force=True, # this should solve for multiple scans on the same IP with different ports + ) + self.__logging.debug(f"Preanalysis tls_scanner done.") + def __load_modules(self, parsed_configuration: dict) -> (dict, dict, list): """ Loads the modules @@ -319,6 +379,7 @@ def __load_modules(self, parsed_configuration: dict) -> (dict, dict, list): loaded_modules = {} loaded_arguments = {} testssl_args = [] + tls_scanner_args = [] for name, module_args in parsed_configuration.items(): if name not in self.__input_dict["to_exclude"]: Module, args = module_args @@ -335,9 +396,12 @@ def __load_modules(self, parsed_configuration: dict) -> (dict, dict, list): testssl_args = self.__add_testssl_args( loaded_modules[name], testssl_args ) + tls_scanner_args = self.__add_tls_scanner_args( + loaded_modules[name], tls_scanner_args + ) else: self.__logging.debug(f"Module {name} excluded, skipping..") - return loaded_modules, loaded_arguments, testssl_args + return loaded_modules, loaded_arguments, testssl_args, tls_scanner_args def __run_analysis( self, @@ -522,7 +586,7 @@ def __exec_anaylsis( self.__logging.info(f"Loading modules..") # loading modules - loaded_modules, loaded_arguments, testssl_args = self.__load_modules( + loaded_modules, loaded_arguments, testssl_args, tls_scanner_args = self.__load_modules( parsed_configuration ) # preanalysis if needed @@ -539,6 +603,10 @@ def __exec_anaylsis( testssl_args, type_of_analysis, hostname_or_path, port ) + self.__preanalysis_tls_scanner( + tls_scanner_args, type_of_analysis, hostname_or_path, port + ) + results = self.__run_analysis( loaded_modules, type_of_analysis, @@ -559,6 +627,7 @@ def __exec_anaylsis( port=port, ) self.__logging.info(f"Analysis of {hostname_or_path} done.") + print("Results", results) return loaded_modules, results # todo add output attack trees diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py new file mode 100644 index 0000000..932552b --- /dev/null +++ b/modules/server/alpaca.py @@ -0,0 +1,51 @@ +from modules.configuration.configuration_base import Parse_configuration_protocols +from modules.server.tlsscanner_base import TLS_Scanner_base +from modules.stix.stix_base import Bundled +from utils.mitigations import load_mitigation + + +class Alpaca(TLS_Scanner_base): + """ + Analysis of the poodle testssl results + """ + + conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? + stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + Sets the mitigations for the poodle results + + :param result: the result to set the mitigations in + :type result: dict + :param key: the key to set the mitigations for + :type key: str + :param condition: the condition to set the mitigations for + :type condition: bool + :return: the result with the mitigations + :rtype: dict + """ + print("Loaded mitigation TLS Poodle") + condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") + if condition: + result["mitigation"] = load_mitigation("TLS POODLE") + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Sets the arguments for the testssl command + """ + self._arguments = ["Sni","Alpn","Extension","ProtocolVersion","CipherSuite","Alpaca"] + + # to override + def _worker(self, results): + """ + The worker method, which runs the testssl command + + :param results: dict + :return: dict + :rtype: dict + """ + print("Alpaca output",results) + return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py new file mode 100644 index 0000000..c8a7621 --- /dev/null +++ b/modules/server/tlspoodle.py @@ -0,0 +1,50 @@ +from modules.configuration.configuration_base import Parse_configuration_protocols +from modules.server.tlsscanner_base import TLS_Scanner_base +from modules.stix.stix_base import Bundled +from utils.mitigations import load_mitigation + + +class TLSPoodle(TLS_Scanner_base): + """ + Analysis of the poodle testssl results + """ + + conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? + stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + Sets the mitigations for the poodle results + + :param result: the result to set the mitigations in + :type result: dict + :param key: the key to set the mitigations for + :type key: str + :param condition: the condition to set the mitigations for + :type condition: bool + :return: the result with the mitigations + :rtype: dict + """ + print("Loaded mitigation TLS Poodle") + condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") + if condition: + result["mitigation"] = load_mitigation("TLS POODLE") + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Sets the arguments for the testssl command + """ + self._arguments = ["Sni","ProtocolVersion","CipherSuite","TlsPoodle"] + + # to override + def _worker(self, results): + """ + The worker method, which runs the testssl command + + :param results: dict + :return: dict + :rtype: dict + """ + return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) diff --git a/modules/server/tlsscanner_base.py b/modules/server/tlsscanner_base.py new file mode 100644 index 0000000..9e543c2 --- /dev/null +++ b/modules/server/tlsscanner_base.py @@ -0,0 +1,168 @@ +import logging + +from modules.server.wrappers.tlsscanner import TLS_Scanner +from utils.mitigations import load_mitigation +from utils.urls import port_parse, url_domain +from utils.validation import Validator + + +class TLS_Scanner_base: + """ + TLS-Scanner is a tool created by the Chair for Network and Data Security + from the Ruhr-University Bochum to assist pentesters and security researchers + in the evaluation of TLS Server configurations. + + This is a base class for the different vulnerabilities found by TLS-Scanner.jar. + """ + + def __init__(self): + self._input_dict = {} + self._arguments = [] + self._instance = TLS_Scanner() + self._output_dict = {} + self._mitigations = {} + self._set_arguments() + + def input(self, **kwargs): + """ + This method is used to set the input parameters for the analysis. + + :param kwargs: + :type kwargs: dict + + :Keyword Arguments: + * *hostname* (``str``) -- Hostname to be analyzed. + * *force* (``bool``) -- Force the analysis. + * *port* (``str``) -- Port to be analyzed. + * *keys* (``list``) -- List of keys to be analyzed. + """ + self._input_dict = kwargs + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + This method is used to set the mitigations for the different vulnerabilities. + + :param result: The result of the analysis. + :type result: dict + :param key: The key of the result. + :type key: str + :param condition: If the condition is met. + :type condition: bool + :return: The result with mitigation. + :rtype: dict + """ + if condition: + result["mitigation"] = load_mitigation( + key, raise_error=False + ) # todo: remove, debug until we have all mitigations + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Dummy method to be overridden + + :raise NotImplementedError: + """ + raise NotImplementedError("This method should be reimplemented!") + + # to override + def _worker(self, results): + """ + Dummy method to be overridden + + :raise NotImplementedError: + :param results: results of the analysis. + :type results: dict + + :return: The results of the analysis. + :rtype: dict + """ + raise NotImplementedError("This method should be reimplemented!") + + def _obtain_results(self, results: dict, keys: list): + """ + This method is used to obtain the results of the analysis. + + :param results: The results of the analysis. + :type results: dict + :param keys: The keys of the results. + :type keys: list + :return: The results of the analysis. + :rtype: dict + """ + val = Validator([(results, dict), (keys, list)]) + out = {} + for ip in results: + for key in keys: + val.string(key) + if key not in results[ip]: + results[ip][key] = {"finding": "ERROR_NOT_FOUND"} + # check for severity != OK or info or warn + condition = "severity" in results[ip][key] and ( + results[ip][key]["severity"] != "OK" + and results[ip][key]["severity"] != "INFO" + and results[ip][key]["severity"] != "WARN" + ) + conditioned_result = self._set_mitigations( + results[ip][key], key, condition + ) + if conditioned_result: + out = conditioned_result + if "ip" not in out: + out["ip"] = [] + if ip not in out["ip"]: + out["ip"].append(ip) + if "key" not in out: + out["key"] = [] + if key not in out["key"]: + out["key"].append(key) + return out + + def run(self, **kwargs): + """ + This method is used to run the analysis. + + :param kwargs: + :type kwargs: dict + + :Keyword Arguments: + * *hostname* (``str``) -- Hostname to be analyzed. + * *port* (``str``) -- Port to be analyzed. + * *force* (``bool``) -- Force the analysis. + * *keys* (``list``) -- List of keys to be analyzed. + + :return: The results of the analysis. + :rtype: dict + """ + self.input(**kwargs) + + if "hostname" not in kwargs: + raise AssertionError("Hostname is missing!") + Validator([(self._input_dict["hostname"], str)]) + self._input_dict["hostname"] = url_domain(self._input_dict["hostname"]) + if "port" in self._input_dict: + self._input_dict[ + "hostname" + ] = f'{self._input_dict["hostname"]}:{port_parse(self._input_dict["port"])}' + + logging.debug( + f"Executing analysis in {self._input_dict['hostname']} with args {self._arguments}" + ) + self._output_dict = self._worker( + self._instance.run( + hostname=self._input_dict["hostname"], + args=self._arguments, + force=self._input_dict.get("force", False), + ) + ) + return self.output() + + def output(self): + """ + This method is used to output the results of the analysis. + + :return: The results of the analysis. + :rtype: dict + """ + return self._output_dict.copy() diff --git a/modules/server/wrappers/testssl.py b/modules/server/wrappers/testssl.py index cb976c0..9540759 100644 --- a/modules/server/wrappers/testssl.py +++ b/modules/server/wrappers/testssl.py @@ -46,6 +46,7 @@ def __parse(self): # parse method result.pop("id", None) # Remove ID from results result.pop("ip", None) # Remove IP from results self.__output[site][ip][id] = result # put the result + def output(self) -> (dict, dict): """ @@ -269,6 +270,7 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): for arg in args: cmd.append(arg) cmd.append(hostname) + print(' '.join(cmd)) try: subprocess.run( cmd, @@ -293,10 +295,15 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): f"dependencies{sep}{file_name}.json", "r" ) as file: # load temp file data = file.read() + print("###") + print(data) cache, ip_cache = Parser(json.loads(data)).output() + print(cache, ip_cache) + print("###") self.__update_cache(cache, ip_cache) remove(f"dependencies{sep}{file_name}.json") else: + print("Not forced", link_sep(hostname)[0], self.__cache) if not validate_ip( hostname ): # recursive: if force : false, check if in cache. if not, recursive call diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py new file mode 100644 index 0000000..b34301f --- /dev/null +++ b/modules/server/wrappers/tlsscanner.py @@ -0,0 +1,403 @@ +import json +import logging +import re +import subprocess +import sys +import uuid +from os import devnull, path, remove, sep +from collections import defaultdict + +from utils.urls import link_sep, url_strip, validate_ip +from utils.validation import Validator + + +class Parser: + """ + Class used to parse TLS-Scanner results. + """ + + def __init__(self, to_parse: dict): + """ + Init method. + :param to_parse: Raw JSON output of testssl.sh, given as a python dict. + :type to_parse: dict + """ + self.__results = to_parse + self.__output = {} + self.__ip_output = {} + self.__parse() + + def __parse(self): # parse method + self.__output = self.__results + + + def output(self) -> (dict, dict): + """ + Output. + :return: returns parsed cache dicts. + :rtype: tuple of dict + """ + return self.__output, self.__ip_output + + +class TLS_Scanner: + """ + TLS-Scanner wrapper module. + """ + + __cache = {} + __ip_cache = {} + + def __init__(self): + """ + Loads TLS-Scanner variables. + """ + self.__tls_scanner = f"dependencies{sep}tls_scanner{sep}TLS-Server-Scanner.jar" + self.__input_dict = {} + + def input(self, **kwargs): + """ + Set the input for the modules + :param kwargs: See below + + :Keyword Arguments: + * *hostname* (``str``) -- + The hostname of the website to analyze. Can be an IP or a Name (DNS) + * *args* (``list of str``) -- + Raw arguments for testssl.sh executable + * *force* (``bool``) -- + Force rescan by ignoring cached results , Default *False* + * *one* (``bool``) -- + Add ``--IP=one`` to testssl.sh executable calls, default *True* + * *clean* (``bool``) -- + clear the cache, default *False* + """ + self.__input_dict = kwargs + + def output(self, **kwargs) -> dict: + """ + Output method of module + :param kwargs: See below + + :Keyword Arguments: + * *hostname* (``str``) -- + The hostname of the website analyzed. Can be an IP or a Name (DNS). + + :return: Empty dict if not found, results dict if found. + :rtype: dict + :raise AssertionError: If hostname parameter is not found. + """ + ''' + if "hostname" in kwargs: + kwargs["hostname"] = link_sep(kwargs["hostname"])[0] + ''' + if "hostname" not in kwargs: + raise AssertionError("Missing parameter hostname.") + elif kwargs["hostname"] not in self.__cache: + return {} # not found + else: + return ( + self.__cache[kwargs["hostname"]] # return cache value if + if not validate_ip(kwargs["hostname"]) # it's not an IP + else { # else return the IP value + kwargs["hostname"]: self.__cache[ + self.__ip_cache[kwargs["hostname"]] + ][kwargs["hostname"]] + } + ) + + def __merge(self, x, y) -> dict: + """ + Internal module, merge two dicts + :param x: source dict + :type x: dict + :param y: destination dict + :type y: dict + :return: merged dict + :rtype: dict + """ + z = x.copy() + z.update(y) + return z + + def __clean_cache(self) -> bool: + """ + Clear the cache + :return: True + :rtype: bool + """ + self.__cache = {} + self.__ip_cache = {} + return True + + def __update_cache(self, cache, ip_cache): + """ + Update the cache + :param cache: new results to add to the cache + :param ip_cache: new results of the reverse cache + """ + for site in cache: + if site not in self.__cache: + self.__cache[site] = cache[ + site + ] # for each site, update the cache if not in it + else: + for ip in cache[site]: + self.__cache[site][ip] = self.__merge( + self.__cache[site][ip], cache[site][ip] + ) # if present, merge + self.__ip_cache.update(ip_cache) + + def run(self, **kwargs) -> dict: + """ + + Set the input for the modules, processes the request and returns output. + :param kwargs: See below + + :Keyword Arguments: + * *hostname* (``str``) -- + The hostname of the website to analyze. Can be an IP or a Name (DNS) + * *args* (``list of str``) -- + Raw arguments for testssl.sh executable + * *force* (``bool``) -- + Force rescan by ignoring cached results , Default *False* + * *one* (``bool``) -- + Add ``--IP=one`` to testssl.sh executable calls, default *True* + * *clean* (``bool``) -- + clear the cache, default *False* + + :return: Parsed results. + :rtype: dict + :raise AssertionError: If hostname parameter is not found. + """ + self.input(**kwargs) + if "hostname" not in self.__input_dict: + raise AssertionError("IP or hostname args not found.") + else: # initialization of parameters + self.__input_dict["hostname"] = url_strip( + self.__input_dict["hostname"], strip_www=True + ) + args = self.__input_dict["args"] if "args" in self.__input_dict else [] + force = ( + self.__input_dict["force"] if "force" in self.__input_dict else False + ) + one = self.__input_dict["one"] if "one" in self.__input_dict else True + clean = ( + self.__input_dict["clean"] if "clean" in self.__input_dict else False + ) + Validator( + [ + (self.__input_dict["hostname"], str), + (args, list), + (force, bool), + (one, bool), + (clean, bool), + ] + ) + self.__scan( + str(self.__input_dict["hostname"]), + args=args, + force=force, + one=one, + clean=clean, + ) + return self.output(hostname=self.__input_dict["hostname"]) + + def __scan(self, hostname: str, args: [str], force: bool, one: bool, clean: bool): + """ + Scan internal module + :param hostname: Hostname or IP + :type hostname: str + :param args: Raw args for testssl.sh + :type args: list of str + :param force: Force the rescan, ignore the cached result. + :type force: bool + :param one: Add '--IP=one' to testssl.sh calls. + :type one: bool + :param clean: Clear the cache. + :type clean: bool + + """ + if clean: + self.__clean_cache() + self.__scan_hostname(hostname, args, force, one) + + + def __escape_output(self, output): + # Thanks to Stack Overflow + # https://stackoverflow.com/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + output = ansi_escape.sub('', output) + return output + + def __parse_output(self, output: str): + output = self.__escape_output(output) + output = output.split("\n") + ''' + report = { + "ALPACA":{ + "Result" : "", + "Details" : {}, + }, + "Padding Oracle":{ + "Result" : "", + "Details" : {} + }, + "Raccoon" : { + "Result" : "" + }, + "Direct Raccoon" : { + "Result" : "", + "Details" : {} + }, + "TLS Poodle" : { + "Result" : "" + } + }''' + report = defaultdict(dict) + + alpaca_details = {} + padding_oracle_details = {} + direct_raccoon_details = {} + + for i in range(0,len(output)): + if "Attack Vulnerabilities" in output[i]: + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + vulns = output[i+2:j-1] + + for vuln in vulns: + vuln,res = vuln.split(" : ",1) + vuln = vuln.replace("\t","").strip() + report[vuln]["Result"] = res + i = j # Skip lines + + elif "Alpaca Details" in output[i]: + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + alpaca_details_temp = output[i+2:j-1] + for detail in alpaca_details_temp: + detail,res = detail.split(" : ",1) + detail = detail.replace("\t","") + if "Strict ALPN" == detail: + alpaca_details["Strict ALPN"] = res + elif "Strict SNI" == detail: + alpaca_details["Strict SNI"] = res + elif "ALPACA Mitigation" == detail: + alpaca_details["ALPACA Mitigation"] = res + i = j # Skip lines + + elif "Padding Oracle Details" in output[i] and report["Padding Oracle"]["Result"] == "vulnerable": + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + padding_oracle_details_temp = output[i+2:j-1] + for detail in padding_oracle_details_temp: + detail = detail.split("|") + name = ('-'.join(detail[0].split("\t")[2:])).strip() + behaviour_difference = detail[1].strip() + result = detail[2].strip() + + if "<" in detail[3].strip(): + P = float(detail[3].strip()[4:]) + else: + P = float(detail[3].strip()[3:]) + + padding_oracle_details[name] = { + 'Behaviour' : behaviour_difference, + 'Result': result, + 'Confidence' : P + } + i = j # Skip lines + + elif "Direct Raccoon Results" in output[i] and report["Direct Raccoon"]["Result"] == "vulnerable": + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + direct_raccoon_details_temp = output[i+2:j-1] + for detail in direct_raccoon_details_temp: + detail = detail.split("|") + name = detail[0].replace("\t","-").strip() + behaviour_difference = detail[1].strip() + result = detail[2].strip() + if "<" in detail[3].strip(): + P = float(detail[3].strip()[4:]) + else: + P = float(detail[3].strip()[3:]) + direct_raccoon_details[name] = { + 'Behaviour' : behaviour_difference, + 'Result': result, + 'Confidence' : P + } + i = j # Skip lines + + report["Padding Oracle"]["Details"] = padding_oracle_details + report["ALPACA"]["Details"] = alpaca_details + report["Direct Raccoon"]["Details"] = direct_raccoon_details + return report + + def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): + """ + Internal module of scan + :param hostname: Hostname or IP + :type hostname: str + :param args: Raw args for testssl.sh + :type args: list of str + :param force: Force the rescan, ignore the cached result. + :type force: bool + :param one: Add '--IP=one' to testssl.sh calls. + :type one: bool + """ + # TODO: Add multihost support + if force: + logging.debug("Starting testssl analysis") + file_name = uuid.uuid4().hex + logging.debug( + f"Scanning {hostname}, saving result to temp file {file_name}" + ) + with open(devnull, "w") as null: + cmd = [ + "java", + "-jar", + self.__tls_scanner, + "-connect", + f"{hostname}", + "-scanDetail", + "QUICK" + ] + + if args: + logging.debug(f"Scanning with personalized args: {args}") + cmd.append("-vulns") + cmd.append(",".join(set(args))) + + output = "" + try: + print(' '.join(cmd)) + output = subprocess.check_output( + cmd, + stderr=sys.stderr, + #check=True, # check call equivalent + text=True, # text as an input + ) + if logging.getLogger().isEnabledFor(logging.DEBUG): + print(output) + + except subprocess.CalledProcessError as c: + logging.debug(c) + + + data = self.__parse_output(output) + data = {hostname : data} + + cache, ip_cache = Parser(data).output() + print(cache) + self.__update_cache(cache, ip_cache) + + else: + if hostname not in self.__cache: + self.__scan_hostname( + hostname, args=args, force=True, one=one + ) # with force = True From 1793d959ddfd574d76c38856b92ae45b540eace8 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Wed, 13 Apr 2022 10:59:43 +0200 Subject: [PATCH 017/209] Mitigations are loaded --- modules/server/alpaca.py | 10 +++--- modules/server/padding_oracle.py | 52 +++++++++++++++++++++++++++ modules/server/tlspoodle.py | 5 ++- modules/server/tlsscanner_base.py | 16 +++++++++ modules/server/wrappers/tlsscanner.py | 10 +----- 5 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 modules/server/padding_oracle.py diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 932552b..7ef9d27 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -10,7 +10,7 @@ class Alpaca(TLS_Scanner_base): """ conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + stix = Bundled(mitigation_object=load_mitigation("ALPACA")) # FIX def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ @@ -25,10 +25,10 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: :return: the result with the mitigations :rtype: dict """ - print("Loaded mitigation TLS Poodle") - condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") + + condition = condition and key == "ALPACA" if condition: - result["mitigation"] = load_mitigation("TLS POODLE") + result["mitigation"] = load_mitigation("ALPACA") return result if condition else {} # to override @@ -48,4 +48,4 @@ def _worker(self, results): :rtype: dict """ print("Alpaca output",results) - return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) + return self._obtain_results(results, ["ALPACA"]) diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py new file mode 100644 index 0000000..f12330c --- /dev/null +++ b/modules/server/padding_oracle.py @@ -0,0 +1,52 @@ +from modules.configuration.configuration_base import Parse_configuration_protocols +from modules.server.tlsscanner_base import TLS_Scanner_base +from modules.stix.stix_base import Bundled +from utils.mitigations import load_mitigation + + +class PaddngOracle(TLS_Scanner_base): + """ + Analysis of the Padding Oracle testssl results + """ + + conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? + stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + Sets the mitigations for the poodle results + + :param result: the result to set the mitigations in + :type result: dict + :param key: the key to set the mitigations for + :type key: str + :param condition: the condition to set the mitigations for + :type condition: bool + :return: the result with the mitigations + :rtype: dict + """ + condition = condition and key == "Padding Oracle" + if condition: + result["mitigation"] = load_mitigation("Padding Oracle") + + # Add vulnerable ciphers to the mitigation + + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Sets the arguments for the testssl command + """ + self._arguments = ["Sni","ProtocolVersion","CipherSuite","TlsPoodle","PaddingOracle","PaddingOracleIdentificationAfter"] + + # to override + def _worker(self, results): + """ + The worker method, which runs the testssl command + + :param results: dict + :return: dict + :rtype: dict + """ + return self._obtain_results(results, ["Padding Oracle"]) diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index c8a7621..59644e7 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -25,8 +25,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: :return: the result with the mitigations :rtype: dict """ - print("Loaded mitigation TLS Poodle") - condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") + condition = condition and key == "TLS Poodle" if condition: result["mitigation"] = load_mitigation("TLS POODLE") return result if condition else {} @@ -47,4 +46,4 @@ def _worker(self, results): :return: dict :rtype: dict """ - return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) + return self._obtain_results(results, ["TLS Poodle"]) diff --git a/modules/server/tlsscanner_base.py b/modules/server/tlsscanner_base.py index 9e543c2..0c7b3a4 100644 --- a/modules/server/tlsscanner_base.py +++ b/modules/server/tlsscanner_base.py @@ -93,6 +93,21 @@ def _obtain_results(self, results: dict, keys: list): """ val = Validator([(results, dict), (keys, list)]) out = {} + print() + print("tlsscanner_base.py._obtain_results",results, keys) + + for hostname in results: + for key in keys: + print(results[hostname][key], hostname, key) + condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" + + conditioned_result = self._set_mitigations( + results[hostname][key], key, condition + ) + + print(results[hostname][key]) + + ''' for ip in results: for key in keys: val.string(key) @@ -117,6 +132,7 @@ def _obtain_results(self, results: dict, keys: list): out["key"] = [] if key not in out["key"]: out["key"].append(key) + ''' return out def run(self, **kwargs): diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index b34301f..53b6268 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -96,15 +96,7 @@ def output(self, **kwargs) -> dict: elif kwargs["hostname"] not in self.__cache: return {} # not found else: - return ( - self.__cache[kwargs["hostname"]] # return cache value if - if not validate_ip(kwargs["hostname"]) # it's not an IP - else { # else return the IP value - kwargs["hostname"]: self.__cache[ - self.__ip_cache[kwargs["hostname"]] - ][kwargs["hostname"]] - } - ) + return self.__cache def __merge(self, x, y) -> dict: """ From 6fff164c2d0e9fd61c2c0db94f6db54a889c3cfb Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 15 Apr 2022 08:02:00 +0200 Subject: [PATCH 018/209] Some other changes --- configs/mitigations/ALPACA.json | 11 +++---- configs/mitigations/PADDING_ORACLE.json | 19 +++++++++++ configs/mitigations/TLS_POODLE.json | 16 ++++------ configs/modules/server/padding_oracle.json | 20 ++++++++++++ default_server.json | 7 ++-- modules/server/alpaca.py | 1 + modules/server/padding_oracle.py | 27 ++++++++++++---- modules/server/testssl_base.py | 6 ++++ modules/server/tlspoodle.py | 3 +- modules/server/tlsscanner_base.py | 37 +++------------------- utils/iana2openssl.py | 7 ++++ utils/mitigations.py | 1 + 12 files changed, 98 insertions(+), 57 deletions(-) create mode 100644 configs/mitigations/PADDING_ORACLE.json create mode 100644 configs/modules/server/padding_oracle.json create mode 100644 utils/iana2openssl.py diff --git a/configs/mitigations/ALPACA.json b/configs/mitigations/ALPACA.json index e578e98..89eacf0 100644 --- a/configs/mitigations/ALPACA.json +++ b/configs/mitigations/ALPACA.json @@ -2,17 +2,16 @@ "Entry": { "Name": "ALPACA", "ExtendedName": "Application Layer Protocol Confusion-Analyzing and Mitigating Cracks in TLS Authentication", - "CVE": "", - "CVSS3": "", - "#comment": "", + "CVE": "2021-3618", + "CVSS3": "7.4 (High)", + "#comment": "AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N", "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", "Mitigation": { - "Textual": "Enable the TLS Extensions SNI and ALPN.", - "Apache": "", - "Nginx": "" + "Textual": "Enable the TLS Extensions SNI and ALPN." } }, "#comment": " https://alpaca-attack.com/ ", + "#comment1": " https://nvd.nist.gov/vuln/detail/CVE-2021-3618 ", "#omit-xml-declaration": "yes" } \ No newline at end of file diff --git a/configs/mitigations/PADDING_ORACLE.json b/configs/mitigations/PADDING_ORACLE.json new file mode 100644 index 0000000..827caa5 --- /dev/null +++ b/configs/mitigations/PADDING_ORACLE.json @@ -0,0 +1,19 @@ +{ + "Entry": { + "Name": "Padding Oracle", + "ExtendedName": "Padding Oracle", + "CVE": "2014-3566", + "CVSS3": "3.4 (Low)", + "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", + "Description": "During the evaluation of CBC ciphersuites supported by the server, some were found vulnerable to padding oracle attack, meaning that the attacker could with specifcally crafted tls records make the server behave in different ways and observe the response, which could allow to decrypt the communication.", + "Mitigation": { + "Textual": "Ciphersuite that use CBC mode of operation should be avoided. If for some retrocompatibility reason not every CBC cipher can be disabled the following vulnerable ciphers should be disabled: ", + "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3. add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384.

N.B. restart the server by typing: sudo service apache2 restart.", + "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384


N.B. restart the server by typing: sudo service nginx restart.
" + + } + }, + "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", + "#comment1": " https://www.imperialviolet.org/2014/12/08/poodleagain.html ", + "#omit-xml-declaration": "yes" + } \ No newline at end of file diff --git a/configs/mitigations/TLS_POODLE.json b/configs/mitigations/TLS_POODLE.json index 5b8e69c..b9a3ede 100644 --- a/configs/mitigations/TLS_POODLE.json +++ b/configs/mitigations/TLS_POODLE.json @@ -2,17 +2,15 @@ "Entry": { "Name": "TLS Poodle", "ExtendedName": "TLS Padding Oracle On Downgraded Legacy Encryption", - "CVE": "", - "CVSS3": "", - "#comment": "", - "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", + "CVE": "2014-3566", + "CVSS3": "3.4 (Low)", + "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", + "Description": "Non-determinism during the decryption of malformed TLS records can induce the server to respond with different behaviours. If an attacker (MITM) can observe these different behaviours, then is able to decrypt the communication.", "Mitigation": { - "Textual": "Enable the TLS Extensions SNI and ALPN.", - "Apache": "", - "Nginx": "" - + "Textual": "Ciphersuite that use CBC mode of operation should be avoided. If for some retrocompatibility reason not every CBC cipher can be disabled the following vulnerable ciphers should be disabled: " } }, - "#comment": " https://alpaca-attack.com/ ", + "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", + "#comment1": " https://www.imperialviolet.org/2014/12/08/poodleagain.html ", "#omit-xml-declaration": "yes" } \ No newline at end of file diff --git a/configs/modules/server/padding_oracle.json b/configs/modules/server/padding_oracle.json new file mode 100644 index 0000000..1ba494d --- /dev/null +++ b/configs/modules/server/padding_oracle.json @@ -0,0 +1,20 @@ +{ + "input":[ + { + "name":"hostname", + "type":"str", + "description":"Hostname to analyze", + "required":"True" + } + ], + "description":"This modules runs the vulnerability checks for the Padding Oracle vulnerability.", + "path":"modules/server/padding_oracle.py", + "class_name":"PaddingOracle", + "output":[ + { + "name":"results", + "type":"dict", + "description":"results of the scan" + } + ] + } \ No newline at end of file diff --git a/default_server.json b/default_server.json index d9f67bc..eea40cb 100644 --- a/default_server.json +++ b/default_server.json @@ -3,6 +3,7 @@ "description": "Default server configuration", "modules": [ "3shake", + "alpaca", "beast", "breach", "ccs_injection", @@ -18,12 +19,14 @@ "lucky13", "mitzvah", "nomore", + "padding_oracle", "pfs", - "sslpoodle", "renegotiation", "robot", "sloth", + "sslpoodle", "sweet32", - "ticketbleed" + "ticketbleed", + "tlspoodle" ] } \ No newline at end of file diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 7ef9d27..d59c4aa 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -29,6 +29,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: condition = condition and key == "ALPACA" if condition: result["mitigation"] = load_mitigation("ALPACA") + # TODO: If partially mitigated, just say enable ALPN or SNI... return result if condition else {} # to override diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py index f12330c..68499fa 100644 --- a/modules/server/padding_oracle.py +++ b/modules/server/padding_oracle.py @@ -2,15 +2,15 @@ from modules.server.tlsscanner_base import TLS_Scanner_base from modules.stix.stix_base import Bundled from utils.mitigations import load_mitigation +from utils.iana2openssl import iana2openssl - -class PaddngOracle(TLS_Scanner_base): +class PaddingOracle(TLS_Scanner_base): """ - Analysis of the Padding Oracle testssl results + Analysis of the Padding Oracle TLS Scanner results """ conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + stix = Bundled(mitigation_object=load_mitigation("PADDING ORACLE")) # FIX def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ @@ -27,10 +27,23 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ condition = condition and key == "Padding Oracle" if condition: - result["mitigation"] = load_mitigation("Padding Oracle") - + result["mitigation"] = load_mitigation("PADDING ORACLE") # Should we use a different mitigation? # Add vulnerable ciphers to the mitigation + details = result['Details'] + vulnerable_ciphers = [] + for cipher in details: + if details[cipher]['Result'] != "NOT VULNERABLE": + vulnerable_ciphers.append(cipher) + + vulnerable_ciphers = list(set(vulnerable_ciphers)) # Remove duplicates + + vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] + ciphers = ":!".join(vulnerable_ciphers) + # TODO: Check for key error + result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) + result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) + return result if condition else {} # to override @@ -38,7 +51,7 @@ def _set_arguments(self): """ Sets the arguments for the testssl command """ - self._arguments = ["Sni","ProtocolVersion","CipherSuite","TlsPoodle","PaddingOracle","PaddingOracleIdentificationAfter"] + self._arguments = ["Sni","ProtocolVersion","CipherSuite","PaddingOracle","PaddingOracleIdentificationAfter"] # to override def _worker(self, results): diff --git a/modules/server/testssl_base.py b/modules/server/testssl_base.py index 6596938..262f8c1 100644 --- a/modules/server/testssl_base.py +++ b/modules/server/testssl_base.py @@ -90,6 +90,9 @@ def _obtain_results(self, results: dict, keys: list): """ val = Validator([(results, dict), (keys, list)]) out = {} + print("###") + print(results) + print("###") for ip in results: for key in keys: val.string(key) @@ -114,6 +117,9 @@ def _obtain_results(self, results: dict, keys: list): out["key"] = [] if key not in out["key"]: out["key"].append(key) + print("###") + print(out) + print("###") return out def run(self, **kwargs): diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index 59644e7..e53b130 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -6,7 +6,7 @@ class TLSPoodle(TLS_Scanner_base): """ - Analysis of the poodle testssl results + Analysis of the poodle TLS Scanner results """ conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? @@ -28,6 +28,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: condition = condition and key == "TLS Poodle" if condition: result["mitigation"] = load_mitigation("TLS POODLE") + return result if condition else {} # to override diff --git a/modules/server/tlsscanner_base.py b/modules/server/tlsscanner_base.py index 0c7b3a4..6feb24f 100644 --- a/modules/server/tlsscanner_base.py +++ b/modules/server/tlsscanner_base.py @@ -1,4 +1,5 @@ import logging +from collections import defaultdict from modules.server.wrappers.tlsscanner import TLS_Scanner from utils.mitigations import load_mitigation @@ -92,47 +93,19 @@ def _obtain_results(self, results: dict, keys: list): :rtype: dict """ val = Validator([(results, dict), (keys, list)]) - out = {} - print() - print("tlsscanner_base.py._obtain_results",results, keys) + out = defaultdict(dict) for hostname in results: for key in keys: - print(results[hostname][key], hostname, key) - condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" + condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" and results[hostname][key]['Result'] != "could not test (not vulnerable)" conditioned_result = self._set_mitigations( results[hostname][key], key, condition ) - print(results[hostname][key]) - - ''' - for ip in results: - for key in keys: - val.string(key) - if key not in results[ip]: - results[ip][key] = {"finding": "ERROR_NOT_FOUND"} - # check for severity != OK or info or warn - condition = "severity" in results[ip][key] and ( - results[ip][key]["severity"] != "OK" - and results[ip][key]["severity"] != "INFO" - and results[ip][key]["severity"] != "WARN" - ) - conditioned_result = self._set_mitigations( - results[ip][key], key, condition - ) if conditioned_result: - out = conditioned_result - if "ip" not in out: - out["ip"] = [] - if ip not in out["ip"]: - out["ip"].append(ip) - if "key" not in out: - out["key"] = [] - if key not in out["key"]: - out["key"].append(key) - ''' + out[hostname][key] = conditioned_result + return out def run(self, **kwargs): diff --git a/utils/iana2openssl.py b/utils/iana2openssl.py new file mode 100644 index 0000000..61c9a8c --- /dev/null +++ b/utils/iana2openssl.py @@ -0,0 +1,7 @@ +# This class converts IANA Ciphersuite names to OpenSSL Ciphersuite names + +mapping = {'TLS_NULL_WITH_NULL_NULL': '', 'TLS_RSA_WITH_NULL_MD5': 'NULL-MD5', 'TLS_RSA_WITH_NULL_SHA': 'NULL-SHA', 'TLS_RSA_EXPORT_WITH_RC4_40_MD5': 'EXP-RC4-MD5', 'TLS_RSA_WITH_RC4_128_MD5': 'RC4-MD5', 'TLS_RSA_WITH_RC4_128_SHA': 'RC4-SHA', 'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5': 'EXP-RC2-CBC-MD5', 'TLS_RSA_WITH_IDEA_CBC_SHA': 'IDEA-CBC-SHA', 'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DES-CBC-SHA', 'TLS_RSA_WITH_DES_CBC_SHA': 'DES-CBC-SHA', 'TLS_RSA_WITH_3DES_EDE_CBC_SHA': 'DES-CBC3-SHA', 'TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DH-DSS-DES-CBC-SHA', 'TLS_DH_DSS_WITH_DES_CBC_SHA': 'DH-DSS-DES-CBC-SHA', 'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA': 'DH-DSS-DES-CBC3-SHA', 'TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DH-RSA-DES-CBC-SHA', 'TLS_DH_RSA_WITH_DES_CBC_SHA': 'DH-RSA-DES-CBC-SHA', 'TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA': 'DH-RSA-DES-CBC3-SHA', 'TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA': 'EXP-EDH-DSS-DES-CBC-SHA', 'TLS_DHE_DSS_WITH_DES_CBC_SHA': 'EDH-DSS-DES-CBC-SHA', 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA': 'EDH-DSS-DES-CBC3-SHA', 'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-EDH-RSA-DES-CBC-SHA', 'TLS_DHE_RSA_WITH_DES_CBC_SHA': 'EDH-RSA-DES-CBC-SHA', 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA': 'EDH-RSA-DES-CBC3-SHA', 'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5': 'EXP-ADH-RC4-MD5', 'TLS_DH_anon_WITH_RC4_128_MD5': 'ADH-RC4-MD5', 'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA': 'EXP-ADH-DES-CBC-SHA', 'TLS_DH_anon_WITH_DES_CBC_SHA': 'ADH-DES-CBC-SHA', 'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA': 'ADH-DES-CBC3-SHA', 'SSL_FORTEZZA_KEA_WITH_NULL_SHA': '', 'SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA': '', 'SSL_FORTEZZA_KEA_WITH_RC4_128_SHA': '', 'TLS_KRB5_WITH_DES_CBC_SHA': 'KRB5-DES-CBC-SHA', 'TLS_KRB5_WITH_3DES_EDE_CBC_SHA': 'KRB5-DES-CBC3-SHA', 'TLS_KRB5_WITH_RC4_128_SHA': 'KRB5-RC4-SHA', 'TLS_KRB5_WITH_IDEA_CBC_SHA': 'KRB5-IDEA-CBC-SHA', 'TLS_KRB5_WITH_DES_CBC_MD5': 'KRB5-DES-CBC-MD5', 'TLS_KRB5_WITH_3DES_EDE_CBC_MD5': 'KRB5-DES-CBC3-MD5', 'TLS_KRB5_WITH_RC4_128_MD5': 'KRB5-RC4-MD5', 'TLS_KRB5_WITH_IDEA_CBC_MD5': 'KRB5-IDEA-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA': 'EXP-KRB5-DES-CBC-SHA', 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA': 'EXP-KRB5-RC2-CBC-SHA', 'TLS_KRB5_EXPORT_WITH_RC4_40_SHA': 'EXP-KRB5-RC4-SHA', 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5': 'EXP-KRB5-DES-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5': 'EXP-KRB5-RC2-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_RC4_40_MD5': 'EXP-KRB5-RC4-MD5', 'TLS_PSK_WITH_NULL_SHA': 'PSK-NULL-SHA', 'TLS_DHE_PSK_WITH_NULL_SHA': 'DHE-PSK-NULL-SHA', 'TLS_RSA_PSK_WITH_NULL_SHA': 'RSA-PSK-NULL-SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA': 'AES128-SHA', 'TLS_DH_DSS_WITH_AES_128_CBC_SHA': 'DH-DSS-AES128-SHA', 'TLS_DH_RSA_WITH_AES_128_CBC_SHA': 'DH-RSA-AES128-SHA', 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA': 'DHE-DSS-AES128-SHA', 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA': 'DHE-RSA-AES128-SHA', 'TLS_DH_anon_WITH_AES_128_CBC_SHA': 'ADH-AES128-SHA', 'TLS_RSA_WITH_AES_256_CBC_SHA': 'AES256-SHA', 'TLS_DH_DSS_WITH_AES_256_CBC_SHA': 'DH-DSS-AES256-SHA', 'TLS_DH_RSA_WITH_AES_256_CBC_SHA': 'DH-RSA-AES256-SHA', 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA': 'DHE-DSS-AES256-SHA', 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA': 'DHE-RSA-AES256-SHA', 'TLS_DH_anon_WITH_AES_256_CBC_SHA': 'ADH-AES256-SHA', 'TLS_RSA_WITH_NULL_SHA256': 'NULL-SHA256', 'TLS_RSA_WITH_AES_128_CBC_SHA256': 'AES128-SHA256', 'TLS_RSA_WITH_AES_256_CBC_SHA256': 'AES256-SHA256', 'TLS_DH_DSS_WITH_AES_128_CBC_SHA256': 'DH-DSS-AES128-SHA256', 'TLS_DH_RSA_WITH_AES_128_CBC_SHA256': 'DH-RSA-AES128-SHA256', 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256': 'DHE-DSS-AES128-SHA256', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA': 'CAMELLIA128-SHA', 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA': 'DH-DSS-CAMELLIA128-SHA', 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA': 'DH-RSA-CAMELLIA128-SHA', 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA': 'DHE-DSS-CAMELLIA128-SHA', 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA': 'DHE-RSA-CAMELLIA128-SHA', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA': 'ADH-CAMELLIA128-SHA', 'TLS_RSA_EXPORT1024_WITH_RC4_56_MD5': 'EXP1024-RC4-MD5', 'TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5': 'EXP1024-RC2-CBC-MD5', 'TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA': 'EXP1024-DES-CBC-SHA', 'TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA': 'EXP1024-DHE-DSS-DES-CBC-SHA', 'TLS_RSA_EXPORT1024_WITH_RC4_56_SHA': 'EXP1024-RC4-SHA', 'TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA': 'EXP1024-DHE-DSS-RC4-SHA', 'TLS_DHE_DSS_WITH_RC4_128_SHA': 'DHE-DSS-RC4-SHA', 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256': 'DHE-RSA-AES128-SHA256', 'TLS_DH_DSS_WITH_AES_256_CBC_SHA256': 'DH-DSS-AES256-SHA256', 'TLS_DH_RSA_WITH_AES_256_CBC_SHA256': 'DH-RSA-AES256-SHA256', 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256': 'DHE-DSS-AES256-SHA256', 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256': 'DHE-RSA-AES256-SHA256', 'TLS_DH_anon_WITH_AES_128_CBC_SHA256': 'ADH-AES128-SHA256', 'TLS_DH_anon_WITH_AES_256_CBC_SHA256': 'ADH-AES256-SHA256', 'TLS_GOSTR341094_WITH_28147_CNT_IMIT': 'GOST94-GOST89-GOST89', 'TLS_GOSTR341001_WITH_28147_CNT_IMIT': 'GOST2001-GOST89-GOST89', 'TLS_GOSTR341001_WITH_NULL_GOSTR3411': 'GOST94-NULL-GOST94', 'TLS_GOSTR341094_WITH_NULL_GOSTR3411': 'GOST2001-GOST89-GOST89', 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA': 'CAMELLIA256-SHA', 'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA': 'DH-DSS-CAMELLIA256-SHA', 'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA': 'DH-RSA-CAMELLIA256-SHA', 'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA': 'DHE-DSS-CAMELLIA256-SHA', 'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA': 'DHE-RSA-CAMELLIA256-SHA', 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA': 'ADH-CAMELLIA256-SHA', 'TLS_PSK_WITH_RC4_128_SHA': 'PSK-RC4-SHA', 'TLS_PSK_WITH_3DES_EDE_CBC_SHA': 'PSK-3DES-EDE-CBC-SHA', 'TLS_PSK_WITH_AES_128_CBC_SHA': 'PSK-AES128-CBC-SHA', 'TLS_PSK_WITH_AES_256_CBC_SHA': 'PSK-AES256-CBC-SHA', 'TLS_DHE_PSK_WITH_RC4_128_SHA': '', 'TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA': '', 'TLS_DHE_PSK_WITH_AES_128_CBC_SHA': '', 'TLS_DHE_PSK_WITH_AES_256_CBC_SHA': '', 'TLS_RSA_PSK_WITH_RC4_128_SHA': '', 'TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA': '', 'TLS_RSA_PSK_WITH_AES_128_CBC_SHA': '', 'TLS_RSA_PSK_WITH_AES_256_CBC_SHA': '', 'TLS_RSA_WITH_SEED_CBC_SHA': 'SEED-SHA', 'TLS_DH_DSS_WITH_SEED_CBC_SHA': 'DH-DSS-SEED-SHA', 'TLS_DH_RSA_WITH_SEED_CBC_SHA': 'DH-RSA-SEED-SHA', 'TLS_DHE_DSS_WITH_SEED_CBC_SHA': 'DHE-DSS-SEED-SHA', 'TLS_DHE_RSA_WITH_SEED_CBC_SHA': 'DHE-RSA-SEED-SHA', 'TLS_DH_anon_WITH_SEED_CBC_SHA': 'ADH-SEED-SHA', 'TLS_RSA_WITH_AES_128_GCM_SHA256': 'AES128-GCM-SHA256', 'TLS_RSA_WITH_AES_256_GCM_SHA384': 'AES256-GCM-SHA384', 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256': 'DHE-RSA-AES128-GCM-SHA256', 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384': 'DHE-RSA-AES256-GCM-SHA384', 'TLS_DH_RSA_WITH_AES_128_GCM_SHA256': 'DH-RSA-AES128-GCM-SHA256', 'TLS_DH_RSA_WITH_AES_256_GCM_SHA384': 'DH-RSA-AES256-GCM-SHA384', 'TLS_DHE_DSS_WITH_AES_128_GCM_SHA256': 'DHE-DSS-AES128-GCM-SHA256', 'TLS_DHE_DSS_WITH_AES_256_GCM_SHA384': 'DHE-DSS-AES256-GCM-SHA384', 'TLS_DH_DSS_WITH_AES_128_GCM_SHA256': 'DH-DSS-AES128-GCM-SHA256', 'TLS_DH_DSS_WITH_AES_256_GCM_SHA384': 'DH-DSS-AES256-GCM-SHA384', 'TLS_DH_anon_WITH_AES_128_GCM_SHA256': 'ADH-AES128-GCM-SHA256', 'TLS_DH_anon_WITH_AES_256_GCM_SHA384': 'ADH-AES256-GCM-SHA384', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'CAMELLIA128-SHA256', 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256': 'DH-DSS-CAMELLIA128-SHA256', 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'DH-RSA-CAMELLIA128-SHA256', 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-DSS-CAMELLIA128-SHA256', 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-RSA-CAMELLIA128-SHA256', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256': 'ADH-CAMELLIA128-SHA256', 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV': 'TLS_FALLBACK_SCSV', 'TLS_AES_128_GCM_SHA256': 'TLS_AES_128_GCM_SHA256', 'TLS_AES_256_GCM_SHA384': 'TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256': 'TLS_CHACHA20_POLY1305_SHA256', 'TLS_AES_128_CCM_SHA256': 'TLS_AES_128_CCM_SHA256', 'TLS_AES_128_CCM_8_SHA256': 'TLS_AES_128_CCM_8_SHA256', 'TLS_ECDH_ECDSA_WITH_NULL_SHA': 'ECDH-ECDSA-NULL-SHA', 'TLS_ECDH_ECDSA_WITH_RC4_128_SHA': 'ECDH-ECDSA-RC4-SHA', 'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA': 'ECDH-ECDSA-DES-CBC3-SHA', 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA': 'ECDH-ECDSA-AES128-SHA', 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA': 'ECDH-ECDSA-AES256-SHA', 'TLS_ECDHE_ECDSA_WITH_NULL_SHA': 'ECDHE-ECDSA-NULL-SHA', 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA': 'ECDHE-ECDSA-RC4-SHA', 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA': 'ECDHE-ECDSA-DES-CBC3-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA': 'ECDHE-ECDSA-AES128-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA': 'ECDHE-ECDSA-AES256-SHA', 'TLS_ECDH_RSA_WITH_NULL_SHA': 'ECDH-RSA-NULL-SHA', 'TLS_ECDH_RSA_WITH_RC4_128_SHA': 'ECDH-RSA-RC4-SHA', 'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA': 'ECDH-RSA-DES-CBC3-SHA', 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA': 'ECDH-RSA-AES128-SHA', 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA': 'ECDH-RSA-AES256-SHA', 'TLS_ECDHE_RSA_WITH_NULL_SHA': 'ECDHE-RSA-NULL-SHA', 'TLS_ECDHE_RSA_WITH_RC4_128_SHA': 'ECDHE-RSA-RC4-SHA', 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA': 'ECDHE-RSA-DES-CBC3-SHA', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'ECDHE-RSA-AES128-SHA', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'ECDHE-RSA-AES256-SHA', 'TLS_ECDH_anon_WITH_NULL_SHA': 'AECDH-NULL-SHA', 'TLS_ECDH_anon_WITH_RC4_128_SHA': 'AECDH-RC4-SHA', 'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA': 'AECDH-DES-CBC3-SHA', 'TLS_ECDH_anon_WITH_AES_128_CBC_SHA': 'AECDH-AES128-SHA', 'TLS_ECDH_anon_WITH_AES_256_CBC_SHA': 'AECDH-AES256-SHA', 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA': 'SRP-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA': 'SRP-RSA-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA': 'SRP-DSS-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA': 'SRP-AES-128-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA': 'SRP-RSA-AES-128-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA': 'SRP-DSS-AES-128-CBC-SHA', 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA': 'SRP-AES-256-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA': 'SRP-RSA-AES-256-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA': 'SRP-DSS-AES-256-CBC-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256': 'ECDHE-ECDSA-AES128-SHA256', 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384': 'ECDHE-ECDSA-AES256-SHA384', 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256': 'ECDH-ECDSA-AES128-SHA256', 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384': 'ECDH-ECDSA-AES256-SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256': 'ECDHE-RSA-AES128-SHA256', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384': 'ECDHE-RSA-AES256-SHA384', 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256': 'ECDH-RSA-AES128-SHA256', 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384': 'ECDH-RSA-AES256-SHA384', 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256': 'ECDHE-ECDSA-AES128-GCM-SHA256', 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384': 'ECDHE-ECDSA-AES256-GCM-SHA384', 'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256': 'ECDH-ECDSA-AES128-GCM-SHA256', 'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384': 'ECDH-ECDSA-AES256-GCM-SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256': 'ECDHE-RSA-AES128-GCM-SHA256', 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384': 'ECDHE-RSA-AES256-GCM-SHA384', 'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256': 'ECDH-RSA-AES128-GCM-SHA256', 'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384': 'ECDH-RSA-AES256-GCM-SHA384', 'TLS_ECDHE_PSK_WITH_RC4_128_SHA': 'ECDHE-PSK-RC4-SHA', 'TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA': 'ECDHE-PSK-3DES-EDE-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA': 'ECDHE-PSK-AES128-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA': 'ECDHE-PSK-AES256-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256': 'ECDHE-PSK-AES128-CBC-SHA256', 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384': 'ECDHE-PSK-AES256-CBC-SHA384', 'TLS_ECDHE_PSK_WITH_NULL_SHA': 'ECDHE-PSK-NULL-SHA', 'TLS_ECDHE_PSK_WITH_NULL_SHA256': 'ECDHE-PSK-NULL-SHA256', 'TLS_ECDHE_PSK_WITH_NULL_SHA384': 'ECDHE-PSK-NULL-SHA384', 'TLS_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_anon_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_anon_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_anon_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_anon_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-ECDSA-CAMELLIA128-SHA256', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-ECDSA-CAMELLIA256-SHA38', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDH-ECDSA-CAMELLIA128-SHA256', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDH-ECDSA-CAMELLIA256-SHA384', 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-RSA-CAMELLIA128-SHA256', 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-RSA-CAMELLIA256-SHA384', 'TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDH-RSA-CAMELLIA128-SHA256', 'TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDH-RSA-CAMELLIA256-SHA384', 'TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'PSK-CAMELLIA128-SHA256', 'TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'PSK-CAMELLIA256-SHA384', 'TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-PSK-CAMELLIA128-SHA256', 'TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'DHE-PSK-CAMELLIA256-SHA384', 'TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'RSA-PSK-CAMELLIA128-SHA256', 'TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'RSA-PSK-CAMELLIA256-SHA384', 'TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-PSK-CAMELLIA128-SHA256', 'TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-PSK-CAMELLIA256-SHA384', 'TLS_RSA_WITH_AES_128_CCM': 'AES128-CCM', 'TLS_RSA_WITH_AES_256_CCM': 'AES256-CCM', 'TLS_DHE_RSA_WITH_AES_128_CCM': 'DHE-RSA-AES128-CCM', 'TLS_DHE_RSA_WITH_AES_256_CCM': 'DHE-RSA-AES256-CCM', 'TLS_RSA_WITH_AES_128_CCM_8': 'AES128-CCM8', 'TLS_RSA_WITH_AES_256_CCM_8': 'AES256-CCM8', 'TLS_DHE_RSA_WITH_AES_128_CCM_8': 'DHE-RSA-AES128-CCM8', 'TLS_DHE_RSA_WITH_AES_256_CCM_8': 'DHE-RSA-AES256-CCM8', 'TLS_PSK_WITH_AES_128_CCM': 'PSK-AES128-CCM', 'TLS_PSK_WITH_AES_256_CCM': 'PSK-AES256-CCM', 'TLS_DHE_PSK_WITH_AES_128_CCM': 'DHE-PSK-AES128-CCM', 'TLS_DHE_PSK_WITH_AES_256_CCM': 'DHE-PSK-AES256-CCM', 'TLS_PSK_WITH_AES_128_CCM_8': 'PSK-AES128-CCM8', 'TLS_PSK_WITH_AES_256_CCM_8': 'PSK-AES256-CCM8', 'TLS_PSK_DHE_WITH_AES_128_CCM_8': 'DHE-PSK-AES128-CCM8', 'TLS_PSK_DHE_WITH_AES_256_CCM_8': 'DHE-PSK-AES256-CCM8', 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM': 'ECDHE-ECDSA-AES128-CCM', 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM': 'ECDHE-ECDSA-AES256-CCM', 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8': 'ECDHE-ECDSA-AES128-CCM8', 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8': 'ECDHE-ECDSA-AES256-CCM8', 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'ECDHE-RSA-CHACHA20-POLY1305-OLD', 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'ECDHE-ECDSA-CHACHA20-POLY1305-OLD', 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'DHE-RSA-CHACHA20-POLY1305-OLD', 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-RSA-CHACHA20-POLY1305', 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-ECDSA-CHACHA20-POLY1305', 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256': 'DHE-RSA-CHACHA20-POLY1305', 'TLS_PSK_WITH_CHACHA20_POLY1305_SHA256': 'PSK-CHACHA20-POLY1305', 'TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-PSK-CHACHA20-POLY1305', 'TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256': 'DHE-PSK-CHACHA20-POLY1305', 'TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256': 'RSA-PSK-CHACHA20-POLY1305', 'TLS_GOSTR341094_RSA_WITH_28147_CNT_MD5': 'GOST-MD5', 'TLS_RSA_WITH_28147_CNT_GOST94': 'GOST-GOST94', 'SSL_RSA_FIPS_WITH_DES_CBC_SHA': '', 'SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA': '', 'SSL_CK_RC4_128_WITH_MD5': 'RC4-MD5', 'SSL_CK_RC4_128_EXPORT40_WITH_MD5': 'EXP-RC4-MD5', 'SSL_CK_RC2_128_CBC_WITH_MD5': 'RC2-CBC-MD5', 'SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5': 'EXP-RC2-CBC-MD5', 'SSL_CK_IDEA_128_CBC_WITH_MD5': 'IDEA-CBC-MD5', 'SSL_CK_DES_64_CBC_WITH_MD5': 'DES-CBC-MD5', 'SSL_CK_DES_64_CBC_WITH_SHA': 'DES-CBC-SHA', 'SSL_CK_DES_192_EDE3_CBC_WITH_MD5': 'DES-CBC3-MD5', 'SSL_CK_DES_192_EDE3_CBC_WITH_SHA': 'DES-CBC3-SHA', 'SSL_CK_RC4_64_WITH_MD5': 'RC4-64-MD5', 'SSL_CK_DES_64_CFB64_WITH_MD5_1': 'DES-CFB-M1', 'SSL_CK_NULL': 'NULL'} + +# TODO: Check for key error +def iana2openssl(name: str) -> str: + return mapping[name] diff --git a/utils/mitigations.py b/utils/mitigations.py index 55931ee..847ee73 100644 --- a/utils/mitigations.py +++ b/utils/mitigations.py @@ -41,6 +41,7 @@ def load_mitigation( mitigation_name = mitigation_name.replace(" ", "_") mitigation_name = mitigation_name.upper() mitigation_path = Path(f"configs{sep}mitigations{sep}{mitigation_name}.json") + print(mitigation_path) if not mitigation_path.exists(): if raise_error: raise FileNotFoundError( From 6dbf0962f905148077d3e54bc3f0f205899c3d72 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 15 Apr 2022 09:38:22 +0200 Subject: [PATCH 019/209] Preliminary Raccoon --- configs/mitigations/PADDING_ORACLE.json | 6 +-- configs/mitigations/RACCOON.json | 5 ++- configs/mitigations/SSL_POODLE.json | 4 +- configs/modules/server/raccoon.json | 20 ++++++++++ default_server.json | 1 + modules/server/alpaca.py | 5 +-- modules/server/padding_oracle.py | 6 +-- modules/server/raccoon.py | 51 +++++++++++++++++++++++++ modules/server/tlspoodle.py | 4 +- utils/mitigations.py | 2 +- 10 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 configs/modules/server/raccoon.json create mode 100644 modules/server/raccoon.py diff --git a/configs/mitigations/PADDING_ORACLE.json b/configs/mitigations/PADDING_ORACLE.json index 827caa5..85063bf 100644 --- a/configs/mitigations/PADDING_ORACLE.json +++ b/configs/mitigations/PADDING_ORACLE.json @@ -8,12 +8,12 @@ "Description": "During the evaluation of CBC ciphersuites supported by the server, some were found vulnerable to padding oracle attack, meaning that the attacker could with specifcally crafted tls records make the server behave in different ways and observe the response, which could allow to decrypt the communication.", "Mitigation": { "Textual": "Ciphersuite that use CBC mode of operation should be avoided. If for some retrocompatibility reason not every CBC cipher can be disabled the following vulnerable ciphers should be disabled: ", - "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3. add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384.

N.B. restart the server by typing: sudo service apache2 restart.", - "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384


N.B. restart the server by typing: sudo service nginx restart.
" + "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3.Add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384.

N.B. restart the server by typing: sudo service apache2 restart.", + "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384


N.B. restart the server by typing: sudo service nginx restart.
" } }, "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", - "#comment1": " https://www.imperialviolet.org/2014/12/08/poodleagain.html ", + "#comment1": " https://blog.qualys.com/product-tech/2019/04/22/zombie-poodle-and-goldendoodle-vulnerabilities ", "#omit-xml-declaration": "yes" } \ No newline at end of file diff --git a/configs/mitigations/RACCOON.json b/configs/mitigations/RACCOON.json index 8f09167..b81d4fc 100644 --- a/configs/mitigations/RACCOON.json +++ b/configs/mitigations/RACCOON.json @@ -8,9 +8,10 @@ "Description": "The Raccoon attack exploits a flaw in the TLS specification which can lead to an attacker being able to compute the pre-master secret in connections which have used a Diffie-Hellman (DH) based ciphersuite. In such a case this would result in the attacker being able to eavesdrop on all encrypted communications sent over that TLS connection.", "Mitigation": { "Textual": "Using of 'static' DH ciphersuites should be completly avoided. Also reuising the same DHE secret for multiple connections should be avoided.", - "Apache": "", - "Nginx": "" + "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3.Add the string :!kDHr:!kDHd:!kDH at the end..

N.B. restart the server by typing: sudo service apache2 restart.", + "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add :!!kDHr:!kDHd:!kDH at the end.


N.B. restart the server by typing: sudo service nginx restart.
" + } }, "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2020-1968 ", diff --git a/configs/mitigations/SSL_POODLE.json b/configs/mitigations/SSL_POODLE.json index 5fea2ed..c3a9367 100644 --- a/configs/mitigations/SSL_POODLE.json +++ b/configs/mitigations/SSL_POODLE.json @@ -1,7 +1,7 @@ { "Entry": { - "Name": "POODLE", - "ExtendedName": "Padding Oracle On Downgraded Legacy Encryption", + "Name": "SSL POODLE", + "ExtendedName": "SSL Padding Oracle On Downgraded Legacy Encryption", "CVE": "2014-3566", "CVSS3": "3.4 (Low)", "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", diff --git a/configs/modules/server/raccoon.json b/configs/modules/server/raccoon.json new file mode 100644 index 0000000..799d361 --- /dev/null +++ b/configs/modules/server/raccoon.json @@ -0,0 +1,20 @@ +{ + "input":[ + { + "name":"hostname", + "type":"str", + "description":"Hostname to analyze", + "required":"True" + } + ], + "description":"This modules runs the vulnerability check for Raccoon.", + "path":"modules/server/raccoon.py", + "class_name":"Raccoon", + "output":[ + { + "name":"results", + "type":"dict", + "description":"results of the scan" + } + ] + } \ No newline at end of file diff --git a/default_server.json b/default_server.json index eea40cb..8fcbd0b 100644 --- a/default_server.json +++ b/default_server.json @@ -21,6 +21,7 @@ "nomore", "padding_oracle", "pfs", + "raccoon", "renegotiation", "robot", "sloth", diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index d59c4aa..9e1c776 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -35,18 +35,17 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: # to override def _set_arguments(self): """ - Sets the arguments for the testssl command + Sets the arguments for the TLS-Scanner command """ self._arguments = ["Sni","Alpn","Extension","ProtocolVersion","CipherSuite","Alpaca"] # to override def _worker(self, results): """ - The worker method, which runs the testssl command + The worker method, which runs the TLS-Scanner command :param results: dict :return: dict :rtype: dict """ - print("Alpaca output",results) return self._obtain_results(results, ["ALPACA"]) diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py index 68499fa..2b0c7db 100644 --- a/modules/server/padding_oracle.py +++ b/modules/server/padding_oracle.py @@ -27,7 +27,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ condition = condition and key == "Padding Oracle" if condition: - result["mitigation"] = load_mitigation("PADDING ORACLE") # Should we use a different mitigation? + result["mitigation"] = load_mitigation("PADDING ORACLE") # Add vulnerable ciphers to the mitigation details = result['Details'] @@ -41,8 +41,8 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] ciphers = ":!".join(vulnerable_ciphers) # TODO: Check for key error - result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) - result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) + result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) + result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) return result if condition else {} diff --git a/modules/server/raccoon.py b/modules/server/raccoon.py new file mode 100644 index 0000000..c417d76 --- /dev/null +++ b/modules/server/raccoon.py @@ -0,0 +1,51 @@ +from modules.configuration.configuration_base import Parse_configuration_protocols +from modules.server.tlsscanner_base import TLS_Scanner_base +from modules.stix.stix_base import Bundled +from utils.mitigations import load_mitigation + + +class Raccoon(TLS_Scanner_base): + """ + Analysis of the poodle testssl results + """ + + conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? + stix = Bundled(mitigation_object=load_mitigation("ALPACA")) # FIX + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + Sets the mitigations for the poodle results + + :param result: the result to set the mitigations in + :type result: dict + :param key: the key to set the mitigations for + :type key: str + :param condition: the condition to set the mitigations for + :type condition: bool + :return: the result with the mitigations + :rtype: dict + """ + + condition = condition and key == "Raccoon" + if condition: + result["mitigation"] = load_mitigation("RACCOON") + # TODO: If partially mitigated, just say enable ALPN or SNI... + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Sets the arguments for the TLS-Scanner command + """ + self._arguments = ["Sni","Alpn","ProtocolVersion","CipherSuite","DirectRaccoon","RaccoonAttackAfter"] + + # to override + def _worker(self, results): + """ + The worker method, which runs the TLS-Scanner command + + :param results: dict + :return: dict + :rtype: dict + """ + return self._obtain_results(results, ["Raccoon"]) diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index e53b130..a95e82c 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -34,14 +34,14 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: # to override def _set_arguments(self): """ - Sets the arguments for the testssl command + Sets the arguments for the TLS-Scanner command """ self._arguments = ["Sni","ProtocolVersion","CipherSuite","TlsPoodle"] # to override def _worker(self, results): """ - The worker method, which runs the testssl command + The worker method, which runs the TLS-Scanner command :param results: dict :return: dict diff --git a/utils/mitigations.py b/utils/mitigations.py index 847ee73..11736cb 100644 --- a/utils/mitigations.py +++ b/utils/mitigations.py @@ -41,7 +41,7 @@ def load_mitigation( mitigation_name = mitigation_name.replace(" ", "_") mitigation_name = mitigation_name.upper() mitigation_path = Path(f"configs{sep}mitigations{sep}{mitigation_name}.json") - print(mitigation_path) + #print(mitigation_path) if not mitigation_path.exists(): if raise_error: raise FileNotFoundError( From 763929bf8745aa1668e17350485279c2425269eb Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 15 Apr 2022 09:50:20 +0200 Subject: [PATCH 020/209] Update padding_oracle.py --- modules/server/padding_oracle.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py index 2b0c7db..715f4a6 100644 --- a/modules/server/padding_oracle.py +++ b/modules/server/padding_oracle.py @@ -28,22 +28,22 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: condition = condition and key == "Padding Oracle" if condition: result["mitigation"] = load_mitigation("PADDING ORACLE") - # Add vulnerable ciphers to the mitigation - - details = result['Details'] - vulnerable_ciphers = [] - for cipher in details: - if details[cipher]['Result'] != "NOT VULNERABLE": - vulnerable_ciphers.append(cipher) - - vulnerable_ciphers = list(set(vulnerable_ciphers)) # Remove duplicates + # Add vulnerable ciphers to the mitigation + + details = result['Details'] + vulnerable_ciphers = [] + for cipher in details: + if details[cipher]['Result'] != "NOT VULNERABLE": + vulnerable_ciphers.append(cipher) + + vulnerable_ciphers = list(set(vulnerable_ciphers)) # Remove duplicates - vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] - ciphers = ":!".join(vulnerable_ciphers) - # TODO: Check for key error - result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) - result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) - + vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] + ciphers = ":!".join(vulnerable_ciphers) + # TODO: Check for key error + result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) + result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) + return result if condition else {} # to override From 5c65f3f620e2a9aa07692e759ab9e174a5472682 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Tue, 19 Apr 2022 11:37:52 +0200 Subject: [PATCH 021/209] Save output nginx configuration --- modules/configuration/configuration.py | 181 ++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 17 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index e471fcb..93cbbac 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -3,8 +3,9 @@ from utils.logger import Logger from utils.type import WebserverType from utils.validation import Validator -from crossplane import parse as nginx_parse from apacheconfig import make_loader +from crossplane import parse as nginx_parse, build as nginx_build +import ast class Configuration: @@ -432,25 +433,171 @@ def fix(self, modules: dict, openssl=None, ignore_openssl=False, online=False): online=online, ) - def save(self, file_name: str = None): + def __rebuild(self, struct, my_payload): + """ + Funzione ricorsiva per generare struttura dati utilizzata dalla libreria `crossplane` + dalla nostra struttura custom + + :param struct: struttura dati custom creata dalla funzione `structure` + :type struct: dict + :param my_payload: output con modifica della reference a questa list + :type my_payload: list + """ + for key, val in struct.items(): + if type(val) == list: + my_payload.append({}) + index = len(my_payload) - 1 + + if len(val) > 0 and type(val[0]) == str: # str degli args + my_payload[index]['directive'] = key + my_payload[index]['args'] = val + else: # primo inizio di sottoblocco + max = len(val) - 1 + for cont, v in enumerate(val): + if type(v) == list: + # Caso nel cui fosse una lista di liste come direttive multiple con stessa chiave e valori diversi + if len(v) > 0: + my_payload[index]['directive'] = key + my_payload[index]['args'] = v + else: + my_payload[index]['block'] = [] + my_payload[index]['directive'] = key + # TODO: Valutare utilizzo di eval + my_payload[index]['args'] = ast.literal_eval(*v) if any(isinstance(el, dict) for el in v.values()) else [] + self.__rebuild(v, my_payload[index]['block']) + + if cont < max: # Se questo è l'ultimo elemento del sottoblocco, non aggiungo nuovo dict vuoto + my_payload.append({}) + index = len(my_payload) - 1 + + else: # caso speciale args con sottoblocco: type(val) == dict + # ogni entry corrisponde ad un nuovo blocco distinto (vedi location) + for k, v in val.items(): + if any(isinstance(el, list) for el in v): + for entry in v: + my_payload.append({}) + i = len(my_payload)-1 + + my_payload[i]['directive'] = k + my_payload[i]['args'] = entry + else: + my_payload.append({}) + i = len(my_payload)-1 + + my_payload[i]['directive'] = k + my_payload[i]['args'] = v + + def __rebuild_wrapper(self, struct, my_payload): + """ + Funzione wrapper per ritornare alla struttura della libreria 'crossplane' + dalla struttura personalizzata. + + :param struct: struttura dati custom creata dalla funzione `structure` + :type struct: dict + :param my_payload: output con modifica della reference a questa list + :type my_payload: list + """ + for key, val in struct.items(): + for entry in val: + my_payload.append({}) + index = len(my_payload) - 1 + my_payload[index]['directive'] = key + my_payload[index]['args'] = [] + if type(entry) == dict: # sottoblocco incoming + my_payload[index]['block'] = [] + self.__rebuild(entry, my_payload[index]['block']) + elif type(entry) == list: + my_payload[index]['args'] = entry + else: + my_payload[index]['args'] = val + break + + def save(self, file_path: str = None): """ Saves the configuration. - :param file_name: file name to save, if None, the input file name is used - :type file_name: str - :default file_name: None + :param file_path: file name to save, if None, the input file name is used + :type file_path: str + :default file_path: None """ self.__logging.info("Saving config file...") - if not file_name: - path = self.__path + + if self.__type == WebserverType.APACHE: + if not file_path: + path = self.__path + else: + path = file_path + file = Path(path) + file.touch() + + options = { + 'namedblocks': False + } + with make_loader(**options) as loader: + loader.dump(filepath=str(file.absolute()), dct=self.__loaded_conf) + self.__logging.info(f"Saved configuration in file {file.absolute()}") + + elif self.__type == WebserverType.NGINX: + cwd = Path.cwd() + + self_path = Path(self.__path).resolve() # main file path + file_path_resolved = None # output file path + output_folder = None # output base folder + if file_path: + file_path_resolved = Path(file_path).resolve() + if file_path_resolved == cwd.resolve(): + # Check that --apply-fix argument is not the current directory + # TODO: Doesn't check ../* path + file_path_resolved = Path('./nginx.conf').resolve() + output_folder = file_path_resolved.parent + if output_folder == cwd: + # -f arg is directly a "single" path (ex: -f output) + # -> folder and main file will have this name -> ./output/output is the ex-"nginx.conf" + output_folder = file_path_resolved + + if output_folder.exists(): + if output_folder.is_dir(): + self.__logging.warning(f"Folder '{output_folder.absolute()}/' already exists, overwriting files...") + elif output_folder.is_file(): + self.__logging.error(f"{output_folder.absolute()} is a file, cannot overwrite it to folder...") + raise NotADirectoryError(f"{output_folder.absolute()} is a file, cannot overwrite it to folder...") + else: + self.__logging.debug(f"Folder '{output_folder}/' is not here, creating at {output_folder.absolute()}/") + output_folder.mkdir(parents=True, exist_ok=True) + + + for path, val in self.__loaded_conf.items(): + this_path = Path(path).resolve() + file = this_path + + if file_path: + if len(self.__loaded_conf) == 1: + # only one output file, so filename is exactly file_path + file = file_path_resolved + else: + file_name_extension = this_path.stem + ''.join(this_path.suffixes) + + if self_path == this_path: + # main file needs to be renamed + file_name_extension = file_path_resolved.name + file = output_folder / file_name_extension + else: + sub_folder = this_path.parent.relative_to(self_path.parent) # subtree relative from main file folder + file = output_folder / sub_folder / file_name_extension + + if not file.parent.exists(): + self.__logging.debug(f"Folder '{file.parent}/' is not here, creating at {file.parent.absolute()}/") + file.parent.mkdir(parents=True, exist_ok=True) # Also here to create 'sub_folder' + + file.touch() # Create the file + + my_payload = [] + self.__rebuild_wrapper(val, my_payload) + config = nginx_build(my_payload) + # print(config) + file.write_text(config) + + self.__logging.info(f"Saved configuration in file {file.absolute()}") + else: - path = file_name - file = Path(path) - file.touch() - - options = { - 'namedblocks': False - } - with make_loader(**options) as loader: - loader.dump(filepath=str(file.absolute()), dct=self.__loaded_conf) - self.__logging.info(f"Saved configuration in file {file.absolute()}") + raise NotImplementedError From 0cd21ee14bfb0ed9ba79a5c09e4892cc88fb26a4 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Wed, 20 Apr 2022 11:00:06 +0200 Subject: [PATCH 022/209] Strip colons from report filename to avoid Windows backup errors --- modules/core.py | 2 +- modules/report.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index 885a584..19e2ca5 100644 --- a/modules/core.py +++ b/modules/core.py @@ -158,7 +158,7 @@ def input(self, **kwargs): kwargs["to_exclude"] = list(map(str.lower, kwargs["to_exclude"])) # set outputfilename if not already set if "output" not in kwargs or not kwargs["output"]: # if not output - file_name = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") + file_name = datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S") fl = str(Path(file_name).absolute()).lower() else: fl = Path(kwargs["output"]) diff --git a/modules/report.py b/modules/report.py index f0e8170..d848a7a 100644 --- a/modules/report.py +++ b/modules/report.py @@ -234,7 +234,7 @@ def run(self, **kwargs): mode=self.__input_dict["mode"], modules=list(modules.keys()), results=results, - date=datetime.now().replace(microsecond=0), + date=datetime.now().replace(microsecond=0).strftime("%Y-%m-%d_%H%M%S"), ) ) self.__logging.debug("Checking if needs pdf...") From 140a98dbb91e341cb46206bdde20bf67da8c7702 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 22 Apr 2022 11:03:21 +0200 Subject: [PATCH 023/209] Fixed some mitigations and raccon is finished --- configs/mitigations/ALPACA.json | 2 +- configs/mitigations/RACCOON.json | 6 ++--- modules/server/alpaca.py | 16 +++++++++++- modules/server/padding_oracle.py | 9 ++++++- modules/server/raccoon.py | 35 +++++++++++++++++++++++---- modules/server/wrappers/tlsscanner.py | 20 +++++++++------ utils/iana2openssl.py | 3 ++- 7 files changed, 71 insertions(+), 20 deletions(-) diff --git a/configs/mitigations/ALPACA.json b/configs/mitigations/ALPACA.json index 89eacf0..8276886 100644 --- a/configs/mitigations/ALPACA.json +++ b/configs/mitigations/ALPACA.json @@ -7,7 +7,7 @@ "#comment": "AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N", "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", "Mitigation": { - "Textual": "Enable the TLS Extensions SNI and ALPN." + "Textual": "Enable the TLS Extensions {extensions}." } }, diff --git a/configs/mitigations/RACCOON.json b/configs/mitigations/RACCOON.json index b81d4fc..b03092a 100644 --- a/configs/mitigations/RACCOON.json +++ b/configs/mitigations/RACCOON.json @@ -7,9 +7,9 @@ "#comment": " AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N ", "Description": "The Raccoon attack exploits a flaw in the TLS specification which can lead to an attacker being able to compute the pre-master secret in connections which have used a Diffie-Hellman (DH) based ciphersuite. In such a case this would result in the attacker being able to eavesdrop on all encrypted communications sent over that TLS connection.", "Mitigation": { - "Textual": "Using of 'static' DH ciphersuites should be completly avoided. Also reuising the same DHE secret for multiple connections should be avoided.", - "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3.Add the string :!kDHr:!kDHd:!kDH at the end..

N.B. restart the server by typing: sudo service apache2 restart.", - "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add :!!kDHr:!kDHd:!kDH at the end.


N.B. restart the server by typing: sudo service nginx restart.
" + "Textual": "Using of 'static' DH ciphersuites should be completly avoided. Also reusing ephemeral Diffie-Hellman keys for multiple connections should be avoided. Configure your server to always use fresh Diffie-Hellman keys", + "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3.Add the string :!{vuln_ciphersuites} at the end.

N.B. restart the server by typing: sudo service apache2 restart.", + "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add :!{vuln_ciphersuites} at the end.


N.B. restart the server by typing: sudo service nginx restart.
" } diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 9e1c776..d6a50fd 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -29,7 +29,21 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: condition = condition and key == "ALPACA" if condition: result["mitigation"] = load_mitigation("ALPACA") - # TODO: If partially mitigated, just say enable ALPN or SNI... + # Handle the case if the the vulnerability is partially mitigated + details = result["Details"] + ext = "" + if details["Strict SNI"] == "false": + ext = "SNI" + + if details["Strict ALPN"] == "false": + if ext != "": + ext += " and ALPN" + else: + ext = "ALPN" + result['mitigation']['Entry']['Mitigation']['Textual'] = result['mitigation']['Entry']['Mitigation']['Textual'].format(extensions = ext) + + print("Result", result) + return result if condition else {} # to override diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py index 715f4a6..594803b 100644 --- a/modules/server/padding_oracle.py +++ b/modules/server/padding_oracle.py @@ -39,7 +39,14 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: vulnerable_ciphers = list(set(vulnerable_ciphers)) # Remove duplicates vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] - ciphers = ":!".join(vulnerable_ciphers) + + ciphers = [] + for cipher in vulnerable_ciphers: + if cipher != "": + ciphers.append(cipher) + + ciphers = ":!".join(ciphers) + # TODO: Check for key error result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) diff --git a/modules/server/raccoon.py b/modules/server/raccoon.py index c417d76..db7fac5 100644 --- a/modules/server/raccoon.py +++ b/modules/server/raccoon.py @@ -2,7 +2,7 @@ from modules.server.tlsscanner_base import TLS_Scanner_base from modules.stix.stix_base import Bundled from utils.mitigations import load_mitigation - +from utils.iana2openssl import iana2openssl class Raccoon(TLS_Scanner_base): """ @@ -10,7 +10,7 @@ class Raccoon(TLS_Scanner_base): """ conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("ALPACA")) # FIX + stix = Bundled(mitigation_object=load_mitigation("RACCOON")) # FIX def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ @@ -25,11 +25,36 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: :return: the result with the mitigations :rtype: dict """ - condition = condition and key == "Raccoon" if condition: result["mitigation"] = load_mitigation("RACCOON") - # TODO: If partially mitigated, just say enable ALPN or SNI... + ciphers = [] + if result["vulnToDirectRaccoon"] == 'vulnerable': + details = result["Details"] + + vulnerable_ciphers = [] + + for cipher in details: + if details[cipher]['Result'] != "NOT VULNERABLE": + vulnerable_ciphers.append(cipher) + + vulnerable_ciphers = list(set(vulnerable_ciphers)) # Remove duplicates + vulnerable_ciphers = [iana2openssl(cipher.split("-",1)[1]) for cipher in vulnerable_ciphers] + + for cipher in vulnerable_ciphers: + if cipher != "": + ciphers.append(cipher) + + if result["vulnToRaccoon"] == 'vulnerable': + ciphers.append("kDHr") + ciphers.append("kDHd") + ciphers.append("kDH") + + ciphers = ":!".join(ciphers) + result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) + result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) + + return result if condition else {} # to override @@ -37,7 +62,7 @@ def _set_arguments(self): """ Sets the arguments for the TLS-Scanner command """ - self._arguments = ["Sni","Alpn","ProtocolVersion","CipherSuite","DirectRaccoon","RaccoonAttackAfter"] + self._arguments = ["Sni","Alpn","ProtocolVersion","CipherSuite","DirectRaccoon","DhValueAfter","RaccoonAttackAfter"] # to override def _worker(self, results): diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 53b6268..756a25d 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -238,13 +238,7 @@ def __parse_output(self, output: str): "Raccoon" : { "Result" : "" }, - "Direct Raccoon" : { - "Result" : "", - "Details" : {} - }, - "TLS Poodle" : { - "Result" : "" - } + [...] }''' report = defaultdict(dict) @@ -328,6 +322,11 @@ def __parse_output(self, output: str): report["Padding Oracle"]["Details"] = padding_oracle_details report["ALPACA"]["Details"] = alpaca_details report["Direct Raccoon"]["Details"] = direct_raccoon_details + report["Raccoon"]["vulnToRaccoon"] = report["Raccoon"]["Result"] + report["Raccoon"]["Result"] = 'vulnerable' if (report["Raccoon"]["Result"] == 'vulnerable' or report["Direct Raccoon"]["Result"] == 'vulnerable') else 'not vulnerable' + report["Raccoon"]["vulnToDirectRaccoon"] = report["Direct Raccoon"]["Result"] + report["Raccoon"]["Details"] = report["Direct Raccoon"]["Details"] + report.pop("Direct Raccoon",None) return report def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): @@ -363,7 +362,12 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): if args: logging.debug(f"Scanning with personalized args: {args}") cmd.append("-vulns") - cmd.append(",".join(set(args))) + aargs = [] + # Some vulnerabilities resuse some command arguments, so eliminate duplicates + for a in args: + if a not in aargs: + aargs.append(a) + cmd.append(",".join(args)) output = "" try: diff --git a/utils/iana2openssl.py b/utils/iana2openssl.py index 61c9a8c..33e676a 100644 --- a/utils/iana2openssl.py +++ b/utils/iana2openssl.py @@ -2,6 +2,7 @@ mapping = {'TLS_NULL_WITH_NULL_NULL': '', 'TLS_RSA_WITH_NULL_MD5': 'NULL-MD5', 'TLS_RSA_WITH_NULL_SHA': 'NULL-SHA', 'TLS_RSA_EXPORT_WITH_RC4_40_MD5': 'EXP-RC4-MD5', 'TLS_RSA_WITH_RC4_128_MD5': 'RC4-MD5', 'TLS_RSA_WITH_RC4_128_SHA': 'RC4-SHA', 'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5': 'EXP-RC2-CBC-MD5', 'TLS_RSA_WITH_IDEA_CBC_SHA': 'IDEA-CBC-SHA', 'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DES-CBC-SHA', 'TLS_RSA_WITH_DES_CBC_SHA': 'DES-CBC-SHA', 'TLS_RSA_WITH_3DES_EDE_CBC_SHA': 'DES-CBC3-SHA', 'TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DH-DSS-DES-CBC-SHA', 'TLS_DH_DSS_WITH_DES_CBC_SHA': 'DH-DSS-DES-CBC-SHA', 'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA': 'DH-DSS-DES-CBC3-SHA', 'TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-DH-RSA-DES-CBC-SHA', 'TLS_DH_RSA_WITH_DES_CBC_SHA': 'DH-RSA-DES-CBC-SHA', 'TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA': 'DH-RSA-DES-CBC3-SHA', 'TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA': 'EXP-EDH-DSS-DES-CBC-SHA', 'TLS_DHE_DSS_WITH_DES_CBC_SHA': 'EDH-DSS-DES-CBC-SHA', 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA': 'EDH-DSS-DES-CBC3-SHA', 'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA': 'EXP-EDH-RSA-DES-CBC-SHA', 'TLS_DHE_RSA_WITH_DES_CBC_SHA': 'EDH-RSA-DES-CBC-SHA', 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA': 'EDH-RSA-DES-CBC3-SHA', 'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5': 'EXP-ADH-RC4-MD5', 'TLS_DH_anon_WITH_RC4_128_MD5': 'ADH-RC4-MD5', 'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA': 'EXP-ADH-DES-CBC-SHA', 'TLS_DH_anon_WITH_DES_CBC_SHA': 'ADH-DES-CBC-SHA', 'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA': 'ADH-DES-CBC3-SHA', 'SSL_FORTEZZA_KEA_WITH_NULL_SHA': '', 'SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA': '', 'SSL_FORTEZZA_KEA_WITH_RC4_128_SHA': '', 'TLS_KRB5_WITH_DES_CBC_SHA': 'KRB5-DES-CBC-SHA', 'TLS_KRB5_WITH_3DES_EDE_CBC_SHA': 'KRB5-DES-CBC3-SHA', 'TLS_KRB5_WITH_RC4_128_SHA': 'KRB5-RC4-SHA', 'TLS_KRB5_WITH_IDEA_CBC_SHA': 'KRB5-IDEA-CBC-SHA', 'TLS_KRB5_WITH_DES_CBC_MD5': 'KRB5-DES-CBC-MD5', 'TLS_KRB5_WITH_3DES_EDE_CBC_MD5': 'KRB5-DES-CBC3-MD5', 'TLS_KRB5_WITH_RC4_128_MD5': 'KRB5-RC4-MD5', 'TLS_KRB5_WITH_IDEA_CBC_MD5': 'KRB5-IDEA-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA': 'EXP-KRB5-DES-CBC-SHA', 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA': 'EXP-KRB5-RC2-CBC-SHA', 'TLS_KRB5_EXPORT_WITH_RC4_40_SHA': 'EXP-KRB5-RC4-SHA', 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5': 'EXP-KRB5-DES-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5': 'EXP-KRB5-RC2-CBC-MD5', 'TLS_KRB5_EXPORT_WITH_RC4_40_MD5': 'EXP-KRB5-RC4-MD5', 'TLS_PSK_WITH_NULL_SHA': 'PSK-NULL-SHA', 'TLS_DHE_PSK_WITH_NULL_SHA': 'DHE-PSK-NULL-SHA', 'TLS_RSA_PSK_WITH_NULL_SHA': 'RSA-PSK-NULL-SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA': 'AES128-SHA', 'TLS_DH_DSS_WITH_AES_128_CBC_SHA': 'DH-DSS-AES128-SHA', 'TLS_DH_RSA_WITH_AES_128_CBC_SHA': 'DH-RSA-AES128-SHA', 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA': 'DHE-DSS-AES128-SHA', 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA': 'DHE-RSA-AES128-SHA', 'TLS_DH_anon_WITH_AES_128_CBC_SHA': 'ADH-AES128-SHA', 'TLS_RSA_WITH_AES_256_CBC_SHA': 'AES256-SHA', 'TLS_DH_DSS_WITH_AES_256_CBC_SHA': 'DH-DSS-AES256-SHA', 'TLS_DH_RSA_WITH_AES_256_CBC_SHA': 'DH-RSA-AES256-SHA', 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA': 'DHE-DSS-AES256-SHA', 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA': 'DHE-RSA-AES256-SHA', 'TLS_DH_anon_WITH_AES_256_CBC_SHA': 'ADH-AES256-SHA', 'TLS_RSA_WITH_NULL_SHA256': 'NULL-SHA256', 'TLS_RSA_WITH_AES_128_CBC_SHA256': 'AES128-SHA256', 'TLS_RSA_WITH_AES_256_CBC_SHA256': 'AES256-SHA256', 'TLS_DH_DSS_WITH_AES_128_CBC_SHA256': 'DH-DSS-AES128-SHA256', 'TLS_DH_RSA_WITH_AES_128_CBC_SHA256': 'DH-RSA-AES128-SHA256', 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256': 'DHE-DSS-AES128-SHA256', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA': 'CAMELLIA128-SHA', 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA': 'DH-DSS-CAMELLIA128-SHA', 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA': 'DH-RSA-CAMELLIA128-SHA', 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA': 'DHE-DSS-CAMELLIA128-SHA', 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA': 'DHE-RSA-CAMELLIA128-SHA', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA': 'ADH-CAMELLIA128-SHA', 'TLS_RSA_EXPORT1024_WITH_RC4_56_MD5': 'EXP1024-RC4-MD5', 'TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5': 'EXP1024-RC2-CBC-MD5', 'TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA': 'EXP1024-DES-CBC-SHA', 'TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA': 'EXP1024-DHE-DSS-DES-CBC-SHA', 'TLS_RSA_EXPORT1024_WITH_RC4_56_SHA': 'EXP1024-RC4-SHA', 'TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA': 'EXP1024-DHE-DSS-RC4-SHA', 'TLS_DHE_DSS_WITH_RC4_128_SHA': 'DHE-DSS-RC4-SHA', 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256': 'DHE-RSA-AES128-SHA256', 'TLS_DH_DSS_WITH_AES_256_CBC_SHA256': 'DH-DSS-AES256-SHA256', 'TLS_DH_RSA_WITH_AES_256_CBC_SHA256': 'DH-RSA-AES256-SHA256', 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256': 'DHE-DSS-AES256-SHA256', 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256': 'DHE-RSA-AES256-SHA256', 'TLS_DH_anon_WITH_AES_128_CBC_SHA256': 'ADH-AES128-SHA256', 'TLS_DH_anon_WITH_AES_256_CBC_SHA256': 'ADH-AES256-SHA256', 'TLS_GOSTR341094_WITH_28147_CNT_IMIT': 'GOST94-GOST89-GOST89', 'TLS_GOSTR341001_WITH_28147_CNT_IMIT': 'GOST2001-GOST89-GOST89', 'TLS_GOSTR341001_WITH_NULL_GOSTR3411': 'GOST94-NULL-GOST94', 'TLS_GOSTR341094_WITH_NULL_GOSTR3411': 'GOST2001-GOST89-GOST89', 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA': 'CAMELLIA256-SHA', 'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA': 'DH-DSS-CAMELLIA256-SHA', 'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA': 'DH-RSA-CAMELLIA256-SHA', 'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA': 'DHE-DSS-CAMELLIA256-SHA', 'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA': 'DHE-RSA-CAMELLIA256-SHA', 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA': 'ADH-CAMELLIA256-SHA', 'TLS_PSK_WITH_RC4_128_SHA': 'PSK-RC4-SHA', 'TLS_PSK_WITH_3DES_EDE_CBC_SHA': 'PSK-3DES-EDE-CBC-SHA', 'TLS_PSK_WITH_AES_128_CBC_SHA': 'PSK-AES128-CBC-SHA', 'TLS_PSK_WITH_AES_256_CBC_SHA': 'PSK-AES256-CBC-SHA', 'TLS_DHE_PSK_WITH_RC4_128_SHA': '', 'TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA': '', 'TLS_DHE_PSK_WITH_AES_128_CBC_SHA': '', 'TLS_DHE_PSK_WITH_AES_256_CBC_SHA': '', 'TLS_RSA_PSK_WITH_RC4_128_SHA': '', 'TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA': '', 'TLS_RSA_PSK_WITH_AES_128_CBC_SHA': '', 'TLS_RSA_PSK_WITH_AES_256_CBC_SHA': '', 'TLS_RSA_WITH_SEED_CBC_SHA': 'SEED-SHA', 'TLS_DH_DSS_WITH_SEED_CBC_SHA': 'DH-DSS-SEED-SHA', 'TLS_DH_RSA_WITH_SEED_CBC_SHA': 'DH-RSA-SEED-SHA', 'TLS_DHE_DSS_WITH_SEED_CBC_SHA': 'DHE-DSS-SEED-SHA', 'TLS_DHE_RSA_WITH_SEED_CBC_SHA': 'DHE-RSA-SEED-SHA', 'TLS_DH_anon_WITH_SEED_CBC_SHA': 'ADH-SEED-SHA', 'TLS_RSA_WITH_AES_128_GCM_SHA256': 'AES128-GCM-SHA256', 'TLS_RSA_WITH_AES_256_GCM_SHA384': 'AES256-GCM-SHA384', 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256': 'DHE-RSA-AES128-GCM-SHA256', 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384': 'DHE-RSA-AES256-GCM-SHA384', 'TLS_DH_RSA_WITH_AES_128_GCM_SHA256': 'DH-RSA-AES128-GCM-SHA256', 'TLS_DH_RSA_WITH_AES_256_GCM_SHA384': 'DH-RSA-AES256-GCM-SHA384', 'TLS_DHE_DSS_WITH_AES_128_GCM_SHA256': 'DHE-DSS-AES128-GCM-SHA256', 'TLS_DHE_DSS_WITH_AES_256_GCM_SHA384': 'DHE-DSS-AES256-GCM-SHA384', 'TLS_DH_DSS_WITH_AES_128_GCM_SHA256': 'DH-DSS-AES128-GCM-SHA256', 'TLS_DH_DSS_WITH_AES_256_GCM_SHA384': 'DH-DSS-AES256-GCM-SHA384', 'TLS_DH_anon_WITH_AES_128_GCM_SHA256': 'ADH-AES128-GCM-SHA256', 'TLS_DH_anon_WITH_AES_256_GCM_SHA384': 'ADH-AES256-GCM-SHA384', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'CAMELLIA128-SHA256', 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256': 'DH-DSS-CAMELLIA128-SHA256', 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'DH-RSA-CAMELLIA128-SHA256', 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-DSS-CAMELLIA128-SHA256', 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-RSA-CAMELLIA128-SHA256', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256': 'ADH-CAMELLIA128-SHA256', 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV': 'TLS_FALLBACK_SCSV', 'TLS_AES_128_GCM_SHA256': 'TLS_AES_128_GCM_SHA256', 'TLS_AES_256_GCM_SHA384': 'TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256': 'TLS_CHACHA20_POLY1305_SHA256', 'TLS_AES_128_CCM_SHA256': 'TLS_AES_128_CCM_SHA256', 'TLS_AES_128_CCM_8_SHA256': 'TLS_AES_128_CCM_8_SHA256', 'TLS_ECDH_ECDSA_WITH_NULL_SHA': 'ECDH-ECDSA-NULL-SHA', 'TLS_ECDH_ECDSA_WITH_RC4_128_SHA': 'ECDH-ECDSA-RC4-SHA', 'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA': 'ECDH-ECDSA-DES-CBC3-SHA', 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA': 'ECDH-ECDSA-AES128-SHA', 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA': 'ECDH-ECDSA-AES256-SHA', 'TLS_ECDHE_ECDSA_WITH_NULL_SHA': 'ECDHE-ECDSA-NULL-SHA', 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA': 'ECDHE-ECDSA-RC4-SHA', 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA': 'ECDHE-ECDSA-DES-CBC3-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA': 'ECDHE-ECDSA-AES128-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA': 'ECDHE-ECDSA-AES256-SHA', 'TLS_ECDH_RSA_WITH_NULL_SHA': 'ECDH-RSA-NULL-SHA', 'TLS_ECDH_RSA_WITH_RC4_128_SHA': 'ECDH-RSA-RC4-SHA', 'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA': 'ECDH-RSA-DES-CBC3-SHA', 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA': 'ECDH-RSA-AES128-SHA', 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA': 'ECDH-RSA-AES256-SHA', 'TLS_ECDHE_RSA_WITH_NULL_SHA': 'ECDHE-RSA-NULL-SHA', 'TLS_ECDHE_RSA_WITH_RC4_128_SHA': 'ECDHE-RSA-RC4-SHA', 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA': 'ECDHE-RSA-DES-CBC3-SHA', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'ECDHE-RSA-AES128-SHA', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'ECDHE-RSA-AES256-SHA', 'TLS_ECDH_anon_WITH_NULL_SHA': 'AECDH-NULL-SHA', 'TLS_ECDH_anon_WITH_RC4_128_SHA': 'AECDH-RC4-SHA', 'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA': 'AECDH-DES-CBC3-SHA', 'TLS_ECDH_anon_WITH_AES_128_CBC_SHA': 'AECDH-AES128-SHA', 'TLS_ECDH_anon_WITH_AES_256_CBC_SHA': 'AECDH-AES256-SHA', 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA': 'SRP-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA': 'SRP-RSA-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA': 'SRP-DSS-3DES-EDE-CBC-SHA', 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA': 'SRP-AES-128-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA': 'SRP-RSA-AES-128-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA': 'SRP-DSS-AES-128-CBC-SHA', 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA': 'SRP-AES-256-CBC-SHA', 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA': 'SRP-RSA-AES-256-CBC-SHA', 'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA': 'SRP-DSS-AES-256-CBC-SHA', 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256': 'ECDHE-ECDSA-AES128-SHA256', 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384': 'ECDHE-ECDSA-AES256-SHA384', 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256': 'ECDH-ECDSA-AES128-SHA256', 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384': 'ECDH-ECDSA-AES256-SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256': 'ECDHE-RSA-AES128-SHA256', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384': 'ECDHE-RSA-AES256-SHA384', 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256': 'ECDH-RSA-AES128-SHA256', 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384': 'ECDH-RSA-AES256-SHA384', 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256': 'ECDHE-ECDSA-AES128-GCM-SHA256', 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384': 'ECDHE-ECDSA-AES256-GCM-SHA384', 'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256': 'ECDH-ECDSA-AES128-GCM-SHA256', 'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384': 'ECDH-ECDSA-AES256-GCM-SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256': 'ECDHE-RSA-AES128-GCM-SHA256', 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384': 'ECDHE-RSA-AES256-GCM-SHA384', 'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256': 'ECDH-RSA-AES128-GCM-SHA256', 'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384': 'ECDH-RSA-AES256-GCM-SHA384', 'TLS_ECDHE_PSK_WITH_RC4_128_SHA': 'ECDHE-PSK-RC4-SHA', 'TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA': 'ECDHE-PSK-3DES-EDE-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA': 'ECDHE-PSK-AES128-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA': 'ECDHE-PSK-AES256-CBC-SHA', 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256': 'ECDHE-PSK-AES128-CBC-SHA256', 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384': 'ECDHE-PSK-AES256-CBC-SHA384', 'TLS_ECDHE_PSK_WITH_NULL_SHA': 'ECDHE-PSK-NULL-SHA', 'TLS_ECDHE_PSK_WITH_NULL_SHA256': 'ECDHE-PSK-NULL-SHA256', 'TLS_ECDHE_PSK_WITH_NULL_SHA384': 'ECDHE-PSK-NULL-SHA384', 'TLS_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DH_anon_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DH_anon_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384': '', 'TLS_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DH_anon_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DH_anon_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256': '', 'TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256': '', 'TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384': '', 'TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256': '', 'TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-ECDSA-CAMELLIA128-SHA256', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-ECDSA-CAMELLIA256-SHA38', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDH-ECDSA-CAMELLIA128-SHA256', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDH-ECDSA-CAMELLIA256-SHA384', 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-RSA-CAMELLIA128-SHA256', 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-RSA-CAMELLIA256-SHA384', 'TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256': 'ECDH-RSA-CAMELLIA128-SHA256', 'TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384': 'ECDH-RSA-CAMELLIA256-SHA384', 'TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256': '', 'TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384': '', 'TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'PSK-CAMELLIA128-SHA256', 'TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'PSK-CAMELLIA256-SHA384', 'TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'DHE-PSK-CAMELLIA128-SHA256', 'TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'DHE-PSK-CAMELLIA256-SHA384', 'TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'RSA-PSK-CAMELLIA128-SHA256', 'TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'RSA-PSK-CAMELLIA256-SHA384', 'TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256': 'ECDHE-PSK-CAMELLIA128-SHA256', 'TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384': 'ECDHE-PSK-CAMELLIA256-SHA384', 'TLS_RSA_WITH_AES_128_CCM': 'AES128-CCM', 'TLS_RSA_WITH_AES_256_CCM': 'AES256-CCM', 'TLS_DHE_RSA_WITH_AES_128_CCM': 'DHE-RSA-AES128-CCM', 'TLS_DHE_RSA_WITH_AES_256_CCM': 'DHE-RSA-AES256-CCM', 'TLS_RSA_WITH_AES_128_CCM_8': 'AES128-CCM8', 'TLS_RSA_WITH_AES_256_CCM_8': 'AES256-CCM8', 'TLS_DHE_RSA_WITH_AES_128_CCM_8': 'DHE-RSA-AES128-CCM8', 'TLS_DHE_RSA_WITH_AES_256_CCM_8': 'DHE-RSA-AES256-CCM8', 'TLS_PSK_WITH_AES_128_CCM': 'PSK-AES128-CCM', 'TLS_PSK_WITH_AES_256_CCM': 'PSK-AES256-CCM', 'TLS_DHE_PSK_WITH_AES_128_CCM': 'DHE-PSK-AES128-CCM', 'TLS_DHE_PSK_WITH_AES_256_CCM': 'DHE-PSK-AES256-CCM', 'TLS_PSK_WITH_AES_128_CCM_8': 'PSK-AES128-CCM8', 'TLS_PSK_WITH_AES_256_CCM_8': 'PSK-AES256-CCM8', 'TLS_PSK_DHE_WITH_AES_128_CCM_8': 'DHE-PSK-AES128-CCM8', 'TLS_PSK_DHE_WITH_AES_256_CCM_8': 'DHE-PSK-AES256-CCM8', 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM': 'ECDHE-ECDSA-AES128-CCM', 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM': 'ECDHE-ECDSA-AES256-CCM', 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8': 'ECDHE-ECDSA-AES128-CCM8', 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8': 'ECDHE-ECDSA-AES256-CCM8', 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'ECDHE-RSA-CHACHA20-POLY1305-OLD', 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'ECDHE-ECDSA-CHACHA20-POLY1305-OLD', 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD': 'DHE-RSA-CHACHA20-POLY1305-OLD', 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-RSA-CHACHA20-POLY1305', 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-ECDSA-CHACHA20-POLY1305', 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256': 'DHE-RSA-CHACHA20-POLY1305', 'TLS_PSK_WITH_CHACHA20_POLY1305_SHA256': 'PSK-CHACHA20-POLY1305', 'TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256': 'ECDHE-PSK-CHACHA20-POLY1305', 'TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256': 'DHE-PSK-CHACHA20-POLY1305', 'TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256': 'RSA-PSK-CHACHA20-POLY1305', 'TLS_GOSTR341094_RSA_WITH_28147_CNT_MD5': 'GOST-MD5', 'TLS_RSA_WITH_28147_CNT_GOST94': 'GOST-GOST94', 'SSL_RSA_FIPS_WITH_DES_CBC_SHA': '', 'SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA': '', 'SSL_CK_RC4_128_WITH_MD5': 'RC4-MD5', 'SSL_CK_RC4_128_EXPORT40_WITH_MD5': 'EXP-RC4-MD5', 'SSL_CK_RC2_128_CBC_WITH_MD5': 'RC2-CBC-MD5', 'SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5': 'EXP-RC2-CBC-MD5', 'SSL_CK_IDEA_128_CBC_WITH_MD5': 'IDEA-CBC-MD5', 'SSL_CK_DES_64_CBC_WITH_MD5': 'DES-CBC-MD5', 'SSL_CK_DES_64_CBC_WITH_SHA': 'DES-CBC-SHA', 'SSL_CK_DES_192_EDE3_CBC_WITH_MD5': 'DES-CBC3-MD5', 'SSL_CK_DES_192_EDE3_CBC_WITH_SHA': 'DES-CBC3-SHA', 'SSL_CK_RC4_64_WITH_MD5': 'RC4-64-MD5', 'SSL_CK_DES_64_CFB64_WITH_MD5_1': 'DES-CFB-M1', 'SSL_CK_NULL': 'NULL'} -# TODO: Check for key error def iana2openssl(name: str) -> str: + if name not in mapping: + return "" return mapping[name] From a7369d6cbf8395284aa308cabef63aee6fe9b674 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:03:13 +0200 Subject: [PATCH 024/209] Fix documentation --- modules/configuration/configuration_base.py | 44 ++++++++++--------- .../nginx/nginx_configuration_base.py | 18 +++++++- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/modules/configuration/configuration_base.py b/modules/configuration/configuration_base.py index d2f83c1..bd5e815 100644 --- a/modules/configuration/configuration_base.py +++ b/modules/configuration/configuration_base.py @@ -108,7 +108,7 @@ def condition(self, vhost): Dummy condition method. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost is vulnerable. :rtype: bool :raise: NotImplementedError if method is not implemented. @@ -120,7 +120,7 @@ def fix(self, vhost): Dummy fix method. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :raise: NotImplementedError if method is not implemented. """ raise NotImplementedError @@ -130,7 +130,7 @@ def is_empty(self, vhost): Dummy empty method. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the contextual VirtualHost directive. :rtype: bool :raise: NotImplementedError if method is not implemented. @@ -167,7 +167,7 @@ def is_empty(self, vhost): Check if vhost doesn't have the contextual directive. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ @@ -179,7 +179,7 @@ def is_tls(self, vhost, version=3): Check if vhost is using only the TLS version x. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param version: TLS version to check. :type version: int :returns: True if vhost is using ONLY the TLS version x. @@ -193,7 +193,7 @@ def fix(self, vhost): Fix TLS/SSL protocol bad configuration. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict """ assert self.__execution_class is not None, "Webserver type not set." return self.__execution_class.fix(vhost) @@ -203,7 +203,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): Check if vhost is vulnerable to TLS SSLProtocol bad configuration. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -220,6 +220,8 @@ class Parse_configuration_ciphers(Config_base): Check if vhost is vulnerable to misconfigured TLS cipher. """ + VHOST_USE = PortType.SSL + def __init__(self, openssl: str, ciphers: list): self.__openssl = openssl self.__ciphers = ciphers @@ -239,7 +241,7 @@ def is_tls(self, vhost, version=3): Check if vhost is using ONLY the TLS version x. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param version: TLS version to check. :type version: int :returns: True if vhost is using ONLY the TLS version x. @@ -253,7 +255,7 @@ def is_empty(self, vhost): Check if vhost doesn't have the contextual directive. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the contextual directive. :rtype: bool """ @@ -269,7 +271,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): Check if vhost is vulnerable to misconfigured TLS cipher. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -305,7 +307,7 @@ def is_empty(self, vhost): Check if vhost doesn't have the header directive. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the header directive. :rtype: bool """ @@ -317,7 +319,7 @@ def fix(self, vhost): Fix misconfigured TLS strict security in vhost. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict """ assert self.__execution_class is not None, "Webserver type not set." return self.__execution_class.fix(vhost) @@ -327,7 +329,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): Check if vhost is vulnerable to misconfigured TLS strict security. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -344,7 +346,7 @@ class Parse_configuration_checks_compression(Config_base): Check if vhost is vulnerable to misconfigured TLS compression. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict """ VHOST_USE = PortType.NONE @@ -368,7 +370,7 @@ def is_tls(self, vhost, version=3): Check if vhost is using only a specific version of TLS. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param version: TLS version. :type version: int :returns: True if vhost is using only a specific version of TLS. @@ -382,7 +384,7 @@ def is_empty(self, vhost): Check if vhost doesn't have the SSLCompression directive. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the SSLCompression directive. :rtype: bool """ @@ -394,7 +396,7 @@ def fix(self, vhost): Fix misconfigured TLS compression in vhost. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict """ assert self.__execution_class is not None, "Webserver type not set." return self.__execution_class.fix(vhost) @@ -404,7 +406,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): Check if vhost is vulnerable to misconfigured TLS compression. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. @@ -438,7 +440,7 @@ def is_empty(self, vhost): Check if vhost doesn't have the RewriteEngine and RewriteRule directives. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :returns: True if vhost doesn't have the RewriteEngine and RewriteRule directives. :rtype: bool """ @@ -450,7 +452,7 @@ def fix(self, vhost): Fix misconfigured TLS redirect in vhost. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict """ assert self.__execution_class is not None, "Webserver type not set." return self.__execution_class.fix(vhost) @@ -460,7 +462,7 @@ def condition(self, vhost, openssl=None, ignore_openssl=False): Check if vhost is vulnerable to misconfigured TLS redirect. :param vhost: VirtualHost object. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: dict :param openssl: OpenSSL version. :type openssl: str :param ignore_openssl: Ignore OpenSSL version. diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index 167d31d..d84a1a2 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -13,6 +13,8 @@ def __init__(self, openssl: str, protocols: dict, openssl_class): :type openssl: str :param protocols: TLS/SSL protocols to check. :type protocols: dict + :param openssl_class: OpenSSL class from "super". + :type openssl_class: OpenSSL """ self.__openssl = openssl self.__protocols = protocols @@ -126,6 +128,14 @@ class Nginx_parse_configuration_ciphers(): """ def __init__(self, openssl: str, ciphers: list, openssl_class): + """ + :param openssl: OpenSSL version. + :type openssl: str + :param ciphers: ciphers to check. + :type ciphers: dict + :param openssl_class: OpenSSL class from "super". + :type openssl_class: OpenSSL + """ self.__openssl = openssl self.__ciphers = ciphers self.openSSL = openssl_class @@ -292,6 +302,12 @@ class Nginx_parse_configuration_checks_compression(): """ def __init__(self, openssl: str, openssl_class): + """ + :param openssl: OpenSSL version. + :type openssl: str + :param openssl_class: OpenSSL class from "super". + :type openssl_class: OpenSSL + """ self.__openssl = openssl self.__key = "ssl_compression" self.openSSL = openssl_class @@ -402,7 +418,7 @@ def fix(self, vhost): if key not in vhost: vhost[key] = ['301', 'https://$host$request_uri'] else: - # TODO: Verificare quale altro c'è già? + # TODO: Check which one is already here? pass return { From e57613b623e5d45c2a3ed33445bb7e6e387dc010 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:04:01 +0200 Subject: [PATCH 025/209] Eng comments --- modules/configuration/configuration.py | 48 ++++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 93cbbac..fa1abc7 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -46,7 +46,7 @@ def __obtain_vhost(self, port=None): if "VirtualHost" not in self.__loaded_conf: self.__loaded_conf["VirtualHost"] = [] loaded_vhost = self.__loaded_conf["VirtualHost"] - # loaded_vhost è dict se solo uno presente, lista di dict altrimenti + # loaded_vhost is dict if there is only one vhost, list of dict otherwise if isinstance(loaded_vhost, list): for vhost in loaded_vhost: if not port or port in list(vhost.keys())[0]: @@ -57,11 +57,11 @@ def __obtain_vhost(self, port=None): elif self.__type == WebserverType.NGINX: def __gen(conf_server): for server in conf_server: - # Nella struttura custom, se lista di lista allora il blocco server - # contiene più di una direttiva 'listen' + # In our custom structure, if list of lists then server block + # contains more than one 'listen' directive if any(isinstance(el, list) for el in server['listen']): for _port in server['listen']: - # Assumo che il primo elemento della (sotto)lista sia la porta + # I assume that the first element of (sub)list is the port if not port or port in _port[0]: yield {_port[0]: server} else: @@ -148,20 +148,19 @@ def __structure(payload, struct): if directive_key not in struct: struct[directive_key] = [] elif 'block' not in directive: - # se esiste già questa chiave ma non è un inizio di sottoblocco, - # allora è una lista di lista, ad indicare più direttive con uguale chiave - # ma distinto valore. - # Esempio: + # if this key already exists, but it's not the start of a subblock, + # then it's a list of lists, to indicate many directive with same key + # but distinct values + # Example: # { # listen 80; # listen 443 ssl; # } - # diventa + # will become # {'listen': [['80'], ['443', 'ssl']]} if any(isinstance(el, str) for el in struct[directive_key]): - # prima volta che scopro che ci sono più chiavi uguali, - # quindi modifico il valore della chiave in array, e aggiungo - # ciò che ho attualmente nel loop... + # first time I discover that there are more directive with same key, + # so I change the value of key to an array, and then I add what I have now in the loop struct[directive_key] = [struct[directive_key], directive['args']] special = True elif any(isinstance(el, list) for el in struct[directive_key]): @@ -173,17 +172,18 @@ def __structure(payload, struct): index = len(struct[directive_key]) - 1 if len(directive['args']) != 0: - arg = repr(directive['args']) # repr della lista per argomento di un blocco - struct[directive_key][index][arg] = {} # Sottoblocco con chiave gli argomenti di un blocco, esempio location >>> = /50x.html <<< {...} + arg = repr(directive['args']) # list repr as argument of a block + struct[directive_key][index][arg] = {} # Subblock with key the arguments of a block, for example: location >>> = /50x.html <<< {...} __structure(directive['block'], struct[directive_key][index][arg]) else: __structure(directive['block'], struct[directive_key][index]) - elif not special: # se non è un inizio di sottoblocco e non è già stato elaborato in precedenza + elif not special: # if it's not a subblock and has not been already handled before struct[directive_key] = directive['args'] payload = nginx_parse(str(file.absolute())) if payload['status'] != 'ok' or len(payload['errors']) > 0: + self.__logging.error(f"Error parsing nginx config: {payload['errors']}") raise Exception(f"Error parsing nginx config: {payload['errors']}") struct = {} @@ -192,6 +192,8 @@ def __structure(payload, struct): __structure(file['parsed'], struct[file['file']]) # Remove file if it doesn't have any 'http' or 'server' block + # TODO: not robust enough, an include could be expanded with useful directive for us but not included at this point + # ie: include snippet/directive/ssl.conf from https://github.com/risan/nginx-config if 'http' not in struct[file['file']] and 'server' not in struct[file['file']]: del struct[file['file']] @@ -448,30 +450,30 @@ def __rebuild(self, struct, my_payload): my_payload.append({}) index = len(my_payload) - 1 - if len(val) > 0 and type(val[0]) == str: # str degli args + if len(val) > 0 and type(val[0]) == str: # args str my_payload[index]['directive'] = key my_payload[index]['args'] = val - else: # primo inizio di sottoblocco + else: # first stage of subblock max = len(val) - 1 for cont, v in enumerate(val): if type(v) == list: - # Caso nel cui fosse una lista di liste come direttive multiple con stessa chiave e valori diversi + # Case where it's a list of lists as multiple directives with same key and different values if len(v) > 0: my_payload[index]['directive'] = key my_payload[index]['args'] = v else: my_payload[index]['block'] = [] my_payload[index]['directive'] = key - # TODO: Valutare utilizzo di eval + # TODO: Evaluate use of eval to bring back args as list from a string my_payload[index]['args'] = ast.literal_eval(*v) if any(isinstance(el, dict) for el in v.values()) else [] self.__rebuild(v, my_payload[index]['block']) - if cont < max: # Se questo è l'ultimo elemento del sottoblocco, non aggiungo nuovo dict vuoto + if cont < max: # If this is the last element of the subblock, don't add a new empty dict my_payload.append({}) index = len(my_payload) - 1 - else: # caso speciale args con sottoblocco: type(val) == dict - # ogni entry corrisponde ad un nuovo blocco distinto (vedi location) + else: # special case where arg has a subblock: type(val) == dict + # every entry is a new distinct block (see 'location' for reference) for k, v in val.items(): if any(isinstance(el, list) for el in v): for entry in v: @@ -503,7 +505,7 @@ def __rebuild_wrapper(self, struct, my_payload): index = len(my_payload) - 1 my_payload[index]['directive'] = key my_payload[index]['args'] = [] - if type(entry) == dict: # sottoblocco incoming + if type(entry) == dict: # subblock incoming my_payload[index]['block'] = [] self.__rebuild(entry, my_payload[index]['block']) elif type(entry) == list: From 71d2c3d95772b2e6c3eb4d2deb772c8c42bed15e Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:04:26 +0200 Subject: [PATCH 026/209] Fix 'if' directive throwed error during rebuild --- modules/configuration/configuration.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index fa1abc7..f3dbf13 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -482,6 +482,15 @@ def __rebuild(self, struct, my_payload): my_payload[i]['directive'] = k my_payload[i]['args'] = entry + elif any(isinstance(el, dict) for el in v): + # this could be an 'if' directive, so let's start again with subblock + my_payload.append({}) + i = len(my_payload)-1 + + my_payload[i]['directive'] = k + my_payload[i]['args'] = ast.literal_eval(*v[0].keys()) + my_payload[i]['block'] = [] + self.__rebuild(v[0], my_payload[i]['block']) else: my_payload.append({}) i = len(my_payload)-1 From 417ee9fd038aa39f5e728b183b7815da5f47c51a Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:04:50 +0200 Subject: [PATCH 027/209] Fix save with only one loaded_conf --- modules/configuration/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index f3dbf13..351a3fb 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -572,7 +572,7 @@ def save(self, file_path: str = None): elif output_folder.is_file(): self.__logging.error(f"{output_folder.absolute()} is a file, cannot overwrite it to folder...") raise NotADirectoryError(f"{output_folder.absolute()} is a file, cannot overwrite it to folder...") - else: + elif len(self.__loaded_conf) > 1: self.__logging.debug(f"Folder '{output_folder}/' is not here, creating at {output_folder.absolute()}/") output_folder.mkdir(parents=True, exist_ok=True) From 81af38098e8c51c0da9f5fd817e9077156ccf5bf Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:51:27 +0200 Subject: [PATCH 028/209] Added method `set_class_name` to Logger --- modules/configuration/configuration.py | 5 +++++ utils/logger.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 351a3fb..8cf09f9 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -108,6 +108,11 @@ def __load_conf(self, path) -> dict: else: results = self.__load_nginx_conf(file) + if self.__type == WebserverType.APACHE: + self.__logging.set_class_name("Configuration APACHE") + elif self.__type == WebserverType.NGINX: + self.__logging.set_class_name("Configuration NGINX") + return results def __load_apache_conf(self, file: Path) -> dict: diff --git a/utils/logger.py b/utils/logger.py index eefeacd..c112a06 100644 --- a/utils/logger.py +++ b/utils/logger.py @@ -8,6 +8,17 @@ def __init__(self, obj): """ Logger to log errors and other messages + :param obj: Obj (automatically gets type name) or name as a string. + :type obj: str or obj + :raise TypeError: If string or obj different + """ + self.__class_name = None + self.set_class_name(obj) + + def set_class_name(self, obj): + """ + Set the class name. + :param obj: Obj (automatically gets type name) or name as a string. :type obj: str or obj :raise TypeError: If string or obj different From 86af8bfc06a03a34145345f8767bd025551444b3 Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:51:45 +0200 Subject: [PATCH 029/209] Fix ciphers_default --- modules/configuration/configuration.py | 1 - modules/configuration/nginx/nginx_configuration_base.py | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index 8cf09f9..f558c20 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -610,7 +610,6 @@ def save(self, file_path: str = None): my_payload = [] self.__rebuild_wrapper(val, my_payload) config = nginx_build(my_payload) - # print(config) file.write_text(config) self.__logging.info(f"Saved configuration in file {file.absolute()}") diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index d84a1a2..7391a35 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -179,6 +179,7 @@ def fix(self, vhost): """ key = self.__key v = Validator() + ciphers_default = 'HIGH:!aNULL:!MD5' backup = vhost[key].copy() if key in vhost else [] for cipher in self.__ciphers: v.string(cipher) @@ -186,10 +187,10 @@ def fix(self, vhost): if len(vhost[key]) == 1: # ssl_ciphers directive has only one argument vhost[key][0] += f":!{cipher.upper()}" - else: - vhost[key] = [f'HIGH:!aNULL:!MD5:!{cipher.upper()}'] + else: # len could be only 0 + vhost[key] = [f'{ciphers_default}:!{cipher.upper()}'] else: - vhost[key] = [f'HIGH:!aNULL:!MD5:!{cipher.upper()}'] + vhost[key] = [f'{ciphers_default}:!{cipher.upper()}'] return { "before": f"{key} {backup}" if backup else "", From 082fdef3e8aaedb613124bc8f2a71e2bc17f671a Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Mon, 2 May 2022 12:19:31 +0200 Subject: [PATCH 030/209] Prints removed --- modules/core.py | 3 --- modules/server/testssl_base.py | 6 ------ modules/server/wrappers/testssl.py | 5 ----- modules/server/wrappers/tlsscanner.py | 1 - 4 files changed, 15 deletions(-) diff --git a/modules/core.py b/modules/core.py index 25ee6fe..6d0be83 100644 --- a/modules/core.py +++ b/modules/core.py @@ -105,8 +105,6 @@ def __init__( stix=stix, ) self.__cache[configuration] = self.__load_configuration(modules) - print(self.__cache[configuration]) - '''''' self.__exec( type_of_analysis=self.__input_dict["type_of_analysis"], hostname_or_path=self.__input_dict["hostname_or_path"], @@ -627,7 +625,6 @@ def __exec_anaylsis( port=port, ) self.__logging.info(f"Analysis of {hostname_or_path} done.") - print("Results", results) return loaded_modules, results # todo add output attack trees diff --git a/modules/server/testssl_base.py b/modules/server/testssl_base.py index 262f8c1..6596938 100644 --- a/modules/server/testssl_base.py +++ b/modules/server/testssl_base.py @@ -90,9 +90,6 @@ def _obtain_results(self, results: dict, keys: list): """ val = Validator([(results, dict), (keys, list)]) out = {} - print("###") - print(results) - print("###") for ip in results: for key in keys: val.string(key) @@ -117,9 +114,6 @@ def _obtain_results(self, results: dict, keys: list): out["key"] = [] if key not in out["key"]: out["key"].append(key) - print("###") - print(out) - print("###") return out def run(self, **kwargs): diff --git a/modules/server/wrappers/testssl.py b/modules/server/wrappers/testssl.py index 9540759..4518e2b 100644 --- a/modules/server/wrappers/testssl.py +++ b/modules/server/wrappers/testssl.py @@ -295,15 +295,10 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): f"dependencies{sep}{file_name}.json", "r" ) as file: # load temp file data = file.read() - print("###") - print(data) cache, ip_cache = Parser(json.loads(data)).output() - print(cache, ip_cache) - print("###") self.__update_cache(cache, ip_cache) remove(f"dependencies{sep}{file_name}.json") else: - print("Not forced", link_sep(hostname)[0], self.__cache) if not validate_ip( hostname ): # recursive: if force : false, check if in cache. if not, recursive call diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 756a25d..2d8978f 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -389,7 +389,6 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): data = {hostname : data} cache, ip_cache = Parser(data).output() - print(cache) self.__update_cache(cache, ip_cache) else: From 6f426dc63944d6afb5c2855847f97361a6d725d4 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 8 May 2022 16:14:29 +0200 Subject: [PATCH 031/209] Updated mitigations with better messages --- configs/mitigations/PADDING_ORACLE.json | 2 +- configs/mitigations/TLS_POODLE.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/mitigations/PADDING_ORACLE.json b/configs/mitigations/PADDING_ORACLE.json index 85063bf..0375834 100644 --- a/configs/mitigations/PADDING_ORACLE.json +++ b/configs/mitigations/PADDING_ORACLE.json @@ -7,7 +7,7 @@ "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", "Description": "During the evaluation of CBC ciphersuites supported by the server, some were found vulnerable to padding oracle attack, meaning that the attacker could with specifcally crafted tls records make the server behave in different ways and observe the response, which could allow to decrypt the communication.", "Mitigation": { - "Textual": "Ciphersuite that use CBC mode of operation should be avoided. If for some retrocompatibility reason not every CBC cipher can be disabled the following vulnerable ciphers should be disabled: ", + "Textual": "Ciphersuite that use CBC mode of operation should be avoided.", "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. find the line starting with: SSLCipherSuite;
3.Add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384.

N.B. restart the server by typing: sudo service apache2 restart.", "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {{...}} brackets configuration, find ssl_ciphers;
3. Add the string :!{vuln_ciphersuites} at the end. Instead if you wish to disable all CBC mode ciphersuites you can add: :!SHA1:!SHA256:!SHA384


N.B. restart the server by typing: sudo service nginx restart.
" diff --git a/configs/mitigations/TLS_POODLE.json b/configs/mitigations/TLS_POODLE.json index b9a3ede..75ce8fb 100644 --- a/configs/mitigations/TLS_POODLE.json +++ b/configs/mitigations/TLS_POODLE.json @@ -7,7 +7,7 @@ "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", "Description": "Non-determinism during the decryption of malformed TLS records can induce the server to respond with different behaviours. If an attacker (MITM) can observe these different behaviours, then is able to decrypt the communication.", "Mitigation": { - "Textual": "Ciphersuite that use CBC mode of operation should be avoided. If for some retrocompatibility reason not every CBC cipher can be disabled the following vulnerable ciphers should be disabled: " + "Textual": "Ciphersuite that use CBC mode of operation should be avoided. Run the padding_oracle module to find out what ciphers where found to be vulnerable." } }, "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", From 4179c06883e703b009802835e76a041e55aa6f83 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 8 May 2022 16:18:06 +0200 Subject: [PATCH 032/209] Added module aliases This allows to run the specify the module poodle and run both tlspoodle and sslpoodle modules --- modules/parse_input_conf.py | 13 ++++++++++++- utils/configuration.py | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/parse_input_conf.py b/modules/parse_input_conf.py index f157085..bf7d1f4 100644 --- a/modules/parse_input_conf.py +++ b/modules/parse_input_conf.py @@ -4,7 +4,7 @@ from utils.loader import load_class, load_configuration from os.path import sep from utils.configuration import merge - +from utils.configuration import get_aliases class Parser: """ @@ -152,7 +152,18 @@ def __get_modules(self, data: dict): """ v = Validator([(data["modules"], list)]) + aliases = get_aliases() + modules = [] for module in data["modules"]: + m = module + if module in aliases: + for alias in aliases[module]: + if alias not in modules: + modules.append(alias) + else: + if module not in modules: + modules.append(module) + for module in modules: mod_data = load_configuration(module) mod_path = Path(mod_data["path"]) self.__cache[mod_path.stem] = ( diff --git a/utils/configuration.py b/utils/configuration.py index 46066d2..af50977 100644 --- a/utils/configuration.py +++ b/utils/configuration.py @@ -56,3 +56,9 @@ def __print_pretty_value(value, indent): pretty(value, indent + 1, is_list=True) else: print("\t" * (indent + 1) + f"{value}") + +def get_aliases(): + aliases = { + "poodle" : ["tlspoodle","sslpoodle"] + } + return aliases \ No newline at end of file From 56338274117f0836161388a4cfe7ba8dd80d6c17 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 8 May 2022 16:18:23 +0200 Subject: [PATCH 033/209] Update setup.py to install TLS-Scanner --- dependencies.json | 18 +++++++++++++- install.py | 34 ++++++++++++++++++++++++++- modules/server/wrappers/tlsscanner.py | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/dependencies.json b/dependencies.json index 407343b..9b2b1c6 100644 --- a/dependencies.json +++ b/dependencies.json @@ -23,9 +23,25 @@ "type": "zip", "url": "https://github.com/drwetter/testssl.sh/archive/3.0.4.zip" }, - { + { "type": "apt", "url": "wkhtmltopdf" + }, + { + "type": "git", + "url": "https://github.com/IvanValentini/TLS-Scanner" + }, + { + "type": "apt", + "url": "default-jre" + }, + { + "type": "apt", + "url": "maven" + }, + { + "type": "compile_maven", + "path": "TLS-Scanner" } ] \ No newline at end of file diff --git a/install.py b/install.py index f9910e0..72ad8d5 100644 --- a/install.py +++ b/install.py @@ -1,4 +1,5 @@ import asyncio +from compileall import compile_path import json import sys @@ -36,6 +37,7 @@ def __init__(self, dependencies): # constructor zips = [] cfgs = [] apts = [] + maven_paths = [] logger.info("Loading dependencies...") for dependency in dependencies: # for each dependency if dependency["type"] == "git": # if it's git @@ -53,6 +55,9 @@ def __init__(self, dependencies): # constructor elif dependency["type"] == "cfg": # if it's cfg cfgs.append(dependency["url"]) # append it's url to the cfg array logger.debug(f"Added dependency cfg {dependency['url']}") + elif dependency["type"] == "compile_maven": # if it's cfg + maven_paths.append(dependency["path"]) # append it's url to the cfg array + logger.debug(f"Added dependency compile {dependency['path']}") else: # if not found, throw warning logger.warning( f"Ignoring dependency {dependency['url']}, type {dependency['type']} is not recognized." @@ -89,8 +94,10 @@ def __init__(self, dependencies): # constructor self.install_dependencies("apts", results_apts) # install the dependencies pkg logger.info("Unzipping dependencies...") self.install_dependencies("zips", results_zips) # unzips the zips + logger.info("Compiling dependencies...") + self.compile_maven_dependencies(maven_paths) # unzips the zips logger.info("Generating Certificates...") - self.generate_cert() + self.generate_cert() logger.info("All done!") def generate_cert(self): @@ -188,6 +195,31 @@ def install_dependencies(self, type, results): logger.debug(f"Removing file dependencies{sep}{file}") remove(f"dependencies{sep}{file}") + def compile_maven_dependencies(self, paths): + + for path in paths: + logger.info(f"Compiling dependencies{sep}{path}...") + f_path = f"./dependencies{sep}{path}" + with open(devnull, "w") as null: + subprocess.check_call( + [ + "mvn", + "clean", + "install", + "-DskipTests=true", + ], + stderr=sys.stderr, + stdout=( + sys.stdout + if logging.getLogger().isEnabledFor( + logging.DEBUG + ) # if the user asked for debug mode, let him see the output. + else null # else /dev/null + ), + cwd=f_path + ) + + def git_clone(self, url, path=None): file_name = self.get_filename(url) with open(devnull, "w") as null: diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 2d8978f..bd3c330 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -52,7 +52,7 @@ def __init__(self): """ Loads TLS-Scanner variables. """ - self.__tls_scanner = f"dependencies{sep}tls_scanner{sep}TLS-Server-Scanner.jar" + self.__tls_scanner = f"dependencies{sep}TLS-Scanner{sep}apps{sep}TLS-Server-Scanner.jar" self.__input_dict = {} def input(self, **kwargs): From d53236fe2c42e05bdb27cfd48e29c7e73b0d1cbb Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 8 May 2022 16:18:06 +0200 Subject: [PATCH 034/209] Added module aliases This allows to run the specify the module poodle and run both tlspoodle and sslpoodle modules --- modules/parse_input_conf.py | 12 +++++++++++- utils/configuration.py | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/parse_input_conf.py b/modules/parse_input_conf.py index f157085..e96eeed 100644 --- a/modules/parse_input_conf.py +++ b/modules/parse_input_conf.py @@ -4,7 +4,7 @@ from utils.loader import load_class, load_configuration from os.path import sep from utils.configuration import merge - +from utils.configuration import get_aliases class Parser: """ @@ -152,7 +152,17 @@ def __get_modules(self, data: dict): """ v = Validator([(data["modules"], list)]) + aliases = get_aliases() + modules = [] for module in data["modules"]: + if module in aliases: + for alias in aliases[module]: + if alias not in modules: + modules.append(alias) + else: + if module not in modules: + modules.append(module) + for module in modules: mod_data = load_configuration(module) mod_path = Path(mod_data["path"]) self.__cache[mod_path.stem] = ( diff --git a/utils/configuration.py b/utils/configuration.py index 46066d2..af50977 100644 --- a/utils/configuration.py +++ b/utils/configuration.py @@ -56,3 +56,9 @@ def __print_pretty_value(value, indent): pretty(value, indent + 1, is_list=True) else: print("\t" * (indent + 1) + f"{value}") + +def get_aliases(): + aliases = { + "poodle" : ["tlspoodle","sslpoodle"] + } + return aliases \ No newline at end of file From c016054ed85422206d361d1cfaebca507d13de45 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sun, 8 May 2022 16:18:23 +0200 Subject: [PATCH 035/209] Update setup.py to install TLS-Scanner --- dependencies.json | 18 +++++++++++++- install.py | 34 ++++++++++++++++++++++++++- modules/server/wrappers/tlsscanner.py | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/dependencies.json b/dependencies.json index 407343b..9b2b1c6 100644 --- a/dependencies.json +++ b/dependencies.json @@ -23,9 +23,25 @@ "type": "zip", "url": "https://github.com/drwetter/testssl.sh/archive/3.0.4.zip" }, - { + { "type": "apt", "url": "wkhtmltopdf" + }, + { + "type": "git", + "url": "https://github.com/IvanValentini/TLS-Scanner" + }, + { + "type": "apt", + "url": "default-jre" + }, + { + "type": "apt", + "url": "maven" + }, + { + "type": "compile_maven", + "path": "TLS-Scanner" } ] \ No newline at end of file diff --git a/install.py b/install.py index f9910e0..72ad8d5 100644 --- a/install.py +++ b/install.py @@ -1,4 +1,5 @@ import asyncio +from compileall import compile_path import json import sys @@ -36,6 +37,7 @@ def __init__(self, dependencies): # constructor zips = [] cfgs = [] apts = [] + maven_paths = [] logger.info("Loading dependencies...") for dependency in dependencies: # for each dependency if dependency["type"] == "git": # if it's git @@ -53,6 +55,9 @@ def __init__(self, dependencies): # constructor elif dependency["type"] == "cfg": # if it's cfg cfgs.append(dependency["url"]) # append it's url to the cfg array logger.debug(f"Added dependency cfg {dependency['url']}") + elif dependency["type"] == "compile_maven": # if it's cfg + maven_paths.append(dependency["path"]) # append it's url to the cfg array + logger.debug(f"Added dependency compile {dependency['path']}") else: # if not found, throw warning logger.warning( f"Ignoring dependency {dependency['url']}, type {dependency['type']} is not recognized." @@ -89,8 +94,10 @@ def __init__(self, dependencies): # constructor self.install_dependencies("apts", results_apts) # install the dependencies pkg logger.info("Unzipping dependencies...") self.install_dependencies("zips", results_zips) # unzips the zips + logger.info("Compiling dependencies...") + self.compile_maven_dependencies(maven_paths) # unzips the zips logger.info("Generating Certificates...") - self.generate_cert() + self.generate_cert() logger.info("All done!") def generate_cert(self): @@ -188,6 +195,31 @@ def install_dependencies(self, type, results): logger.debug(f"Removing file dependencies{sep}{file}") remove(f"dependencies{sep}{file}") + def compile_maven_dependencies(self, paths): + + for path in paths: + logger.info(f"Compiling dependencies{sep}{path}...") + f_path = f"./dependencies{sep}{path}" + with open(devnull, "w") as null: + subprocess.check_call( + [ + "mvn", + "clean", + "install", + "-DskipTests=true", + ], + stderr=sys.stderr, + stdout=( + sys.stdout + if logging.getLogger().isEnabledFor( + logging.DEBUG + ) # if the user asked for debug mode, let him see the output. + else null # else /dev/null + ), + cwd=f_path + ) + + def git_clone(self, url, path=None): file_name = self.get_filename(url) with open(devnull, "w") as null: diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 2d8978f..bd3c330 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -52,7 +52,7 @@ def __init__(self): """ Loads TLS-Scanner variables. """ - self.__tls_scanner = f"dependencies{sep}tls_scanner{sep}TLS-Server-Scanner.jar" + self.__tls_scanner = f"dependencies{sep}TLS-Scanner{sep}apps{sep}TLS-Server-Scanner.jar" self.__input_dict = {} def input(self, **kwargs): From 28b9c84654cf24848bca4e98db9861c988170dc7 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Tue, 10 May 2022 09:35:45 +0200 Subject: [PATCH 036/209] Removed configuration detection --- modules/server/alpaca.py | 7 ++----- modules/server/padding_oracle.py | 10 ++++------ modules/server/tlspoodle.py | 3 +-- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index d6a50fd..859b6c3 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -9,12 +9,11 @@ class Alpaca(TLS_Scanner_base): Analysis of the poodle testssl results """ - conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("ALPACA")) # FIX + stix = Bundled(mitigation_object=load_mitigation("ALPACA")) def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ - Sets the mitigations for the poodle results + Sets the mitigations for the ALPACA results :param result: the result to set the mitigations in :type result: dict @@ -41,8 +40,6 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: else: ext = "ALPN" result['mitigation']['Entry']['Mitigation']['Textual'] = result['mitigation']['Entry']['Mitigation']['Textual'].format(extensions = ext) - - print("Result", result) return result if condition else {} diff --git a/modules/server/padding_oracle.py b/modules/server/padding_oracle.py index 594803b..5f3ad5f 100644 --- a/modules/server/padding_oracle.py +++ b/modules/server/padding_oracle.py @@ -9,12 +9,11 @@ class PaddingOracle(TLS_Scanner_base): Analysis of the Padding Oracle TLS Scanner results """ - conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("PADDING ORACLE")) # FIX + stix = Bundled(mitigation_object=load_mitigation("PADDING ORACLE")) def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ - Sets the mitigations for the poodle results + Sets the mitigations for the padding oracle results :param result: the result to set the mitigations in :type result: dict @@ -47,7 +46,6 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: ciphers = ":!".join(ciphers) - # TODO: Check for key error result['mitigation']['Entry']['Mitigation']['Apache'] = result['mitigation']['Entry']['Mitigation']['Apache'].format(vuln_ciphersuites = ciphers) result['mitigation']['Entry']['Mitigation']['Nginx'] = result['mitigation']['Entry']['Mitigation']['Nginx'].format(vuln_ciphersuites = ciphers) @@ -56,14 +54,14 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: # to override def _set_arguments(self): """ - Sets the arguments for the testssl command + Sets the arguments for the TLS-Scanner command """ self._arguments = ["Sni","ProtocolVersion","CipherSuite","PaddingOracle","PaddingOracleIdentificationAfter"] # to override def _worker(self, results): """ - The worker method, which runs the testssl command + The worker method, which runs the TLS-Scanner command :param results: dict :return: dict diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index a95e82c..e987436 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -9,8 +9,7 @@ class TLSPoodle(TLS_Scanner_base): Analysis of the poodle TLS Scanner results """ - conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) # FIX + stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ From 3100d376345a9057310481213eca6a2d3c562fbc Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Mon, 16 May 2022 15:06:19 +0200 Subject: [PATCH 037/209] Update mitigations.py --- utils/mitigations.py | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/mitigations.py b/utils/mitigations.py index 11736cb..55931ee 100644 --- a/utils/mitigations.py +++ b/utils/mitigations.py @@ -41,7 +41,6 @@ def load_mitigation( mitigation_name = mitigation_name.replace(" ", "_") mitigation_name = mitigation_name.upper() mitigation_path = Path(f"configs{sep}mitigations{sep}{mitigation_name}.json") - #print(mitigation_path) if not mitigation_path.exists(): if raise_error: raise FileNotFoundError( From e1520a8dfc095149cdd16f2c34724b754e1681fd Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Thu, 16 Jun 2022 09:05:58 +0200 Subject: [PATCH 038/209] Remove unused import --- install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/install.py b/install.py index 72ad8d5..7f0bf76 100644 --- a/install.py +++ b/install.py @@ -1,5 +1,4 @@ import asyncio -from compileall import compile_path import json import sys From 1351b63ba276a22b26ad207f197e89c244079901 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Thu, 16 Jun 2022 09:12:07 +0200 Subject: [PATCH 039/209] Updated comments --- install.py | 2 +- modules/core.py | 2 -- modules/server/alpaca.py | 2 +- modules/server/raccoon.py | 5 ++--- modules/server/tlspoodle.py | 2 +- modules/server/wrappers/testssl.py | 2 -- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/install.py b/install.py index 7f0bf76..e22941d 100644 --- a/install.py +++ b/install.py @@ -96,7 +96,7 @@ def __init__(self, dependencies): # constructor logger.info("Compiling dependencies...") self.compile_maven_dependencies(maven_paths) # unzips the zips logger.info("Generating Certificates...") - self.generate_cert() + self.generate_cert() logger.info("All done!") def generate_cert(self): diff --git a/modules/core.py b/modules/core.py index 6d0be83..5f57f8d 100644 --- a/modules/core.py +++ b/modules/core.py @@ -110,8 +110,6 @@ def __init__( hostname_or_path=self.__input_dict["hostname_or_path"], configuration=self.__input_dict["configuration"], ) - - def __string_output_type(self, kwargs_type: Report) -> str: """ diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 859b6c3..2ed10db 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -6,7 +6,7 @@ class Alpaca(TLS_Scanner_base): """ - Analysis of the poodle testssl results + Analysis of the ALPACA TLS-Scanner results """ stix = Bundled(mitigation_object=load_mitigation("ALPACA")) diff --git a/modules/server/raccoon.py b/modules/server/raccoon.py index db7fac5..1d12d2d 100644 --- a/modules/server/raccoon.py +++ b/modules/server/raccoon.py @@ -6,11 +6,10 @@ class Raccoon(TLS_Scanner_base): """ - Analysis of the poodle testssl results + Analysis of the Raccoon TLS-Scanner results """ - conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) # FIXX? - stix = Bundled(mitigation_object=load_mitigation("RACCOON")) # FIX + stix = Bundled(mitigation_object=load_mitigation("RACCOON")) def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index e987436..f97f697 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -6,7 +6,7 @@ class TLSPoodle(TLS_Scanner_base): """ - Analysis of the poodle TLS Scanner results + Analysis of the Poodle TLS-Scanner results """ stix = Bundled(mitigation_object=load_mitigation("TLS POODLE")) diff --git a/modules/server/wrappers/testssl.py b/modules/server/wrappers/testssl.py index 4518e2b..cb976c0 100644 --- a/modules/server/wrappers/testssl.py +++ b/modules/server/wrappers/testssl.py @@ -46,7 +46,6 @@ def __parse(self): # parse method result.pop("id", None) # Remove ID from results result.pop("ip", None) # Remove IP from results self.__output[site][ip][id] = result # put the result - def output(self) -> (dict, dict): """ @@ -270,7 +269,6 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): for arg in args: cmd.append(arg) cmd.append(hostname) - print(' '.join(cmd)) try: subprocess.run( cmd, From b352e1602817d7485ebab03069fd503eccd35d76 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:28:25 +0200 Subject: [PATCH 040/209] Moved code around --- domains | 2 + modules/server/raccoon.py | 2 +- modules/server/tlspoodle.py | 2 +- modules/server/wrappers/tlsscanner.py | 262 ++++++++++++-------------- 4 files changed, 126 insertions(+), 142 deletions(-) create mode 100644 domains diff --git a/domains b/domains new file mode 100644 index 0000000..bb0d638 --- /dev/null +++ b/domains @@ -0,0 +1,2 @@ +google.com +facebook.com diff --git a/modules/server/raccoon.py b/modules/server/raccoon.py index 1d12d2d..6b71043 100644 --- a/modules/server/raccoon.py +++ b/modules/server/raccoon.py @@ -13,7 +13,7 @@ class Raccoon(TLS_Scanner_base): def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ - Sets the mitigations for the poodle results + Sets the mitigations for the Raccoon results :param result: the result to set the mitigations in :type result: dict diff --git a/modules/server/tlspoodle.py b/modules/server/tlspoodle.py index f97f697..c3713f4 100644 --- a/modules/server/tlspoodle.py +++ b/modules/server/tlspoodle.py @@ -13,7 +13,7 @@ class TLSPoodle(TLS_Scanner_base): def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: """ - Sets the mitigations for the poodle results + Sets the mitigations for the tls poodle results :param result: the result to set the mitigations in :type result: dict diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index bd3c330..d39d414 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -19,7 +19,7 @@ class Parser: def __init__(self, to_parse: dict): """ Init method. - :param to_parse: Raw JSON output of testssl.sh, given as a python dict. + :param to_parse: Raw JSON output of TLS-Scanner, given as a python dict. :type to_parse: dict """ self.__results = to_parse @@ -27,8 +27,122 @@ def __init__(self, to_parse: dict): self.__ip_output = {} self.__parse() + def __escape_output(self, output): + # Thanks to Stack Overflow + # https://stackoverflow.com/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python + ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + output = ansi_escape.sub('', output) + return output + def __parse(self): # parse method - self.__output = self.__results + output = self.__escape_output(self.__results) + output = output.split("\n") + ''' + report = { + "ALPACA":{ + "Result" : "", + "Details" : {}, + }, + "Padding Oracle":{ + "Result" : "", + "Details" : {} + }, + "Raccoon" : { + "Result" : "" + }, + [...] + }''' + report = defaultdict(dict) + + alpaca_details = {} + padding_oracle_details = {} + direct_raccoon_details = {} + hostname = "" + for i in range(0,len(output)): + if "Report for" in output[i]: + hostname = output[i].split(" ")[-1] + elif "Attack Vulnerabilities" in output[i]: + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + vulns = output[i+2:j-1] + + for vuln in vulns: + vuln,res = vuln.split(" : ",1) + vuln = vuln.replace("\t","").strip() + report[vuln]["Result"] = res + i = j # Skip lines + + elif "Alpaca Details" in output[i]: + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + alpaca_details_temp = output[i+2:j-1] + for detail in alpaca_details_temp: + detail,res = detail.split(" : ",1) + detail = detail.replace("\t","") + if "Strict ALPN" == detail: + alpaca_details["Strict ALPN"] = res + elif "Strict SNI" == detail: + alpaca_details["Strict SNI"] = res + elif "ALPACA Mitigation" == detail: + alpaca_details["ALPACA Mitigation"] = res + i = j # Skip lines + + elif "Padding Oracle Details" in output[i] and report["Padding Oracle"]["Result"] == "vulnerable": + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + padding_oracle_details_temp = output[i+2:j-1] + for detail in padding_oracle_details_temp: + detail = detail.split("|") + name = ('-'.join(detail[0].split("\t")[2:])).strip() + behaviour_difference = detail[1].strip() + result = detail[2].strip() + + if "<" in detail[3].strip(): + P = float(detail[3].strip()[4:]) + else: + P = float(detail[3].strip()[3:]) + + padding_oracle_details[name] = { + 'Behaviour' : behaviour_difference, + 'Result': result, + 'Confidence' : P + } + i = j # Skip lines + + elif "Direct Raccoon Results" in output[i] and report["Direct Raccoon"]["Result"] == "vulnerable": + j = i + 1 + while output[j] != "------------------------------------------------------------": + j += 1 + direct_raccoon_details_temp = output[i+2:j-1] + for detail in direct_raccoon_details_temp: + detail = detail.split("|") + name = detail[0].replace("\t","-").strip() + behaviour_difference = detail[1].strip() + result = detail[2].strip() + if "<" in detail[3].strip(): + P = float(detail[3].strip()[4:]) + else: + P = float(detail[3].strip()[3:]) + direct_raccoon_details[name] = { + 'Behaviour' : behaviour_difference, + 'Result': result, + 'Confidence' : P + } + i = j # Skip lines + + report["Padding Oracle"]["Details"] = padding_oracle_details + report["ALPACA"]["Details"] = alpaca_details + report["Direct Raccoon"]["Details"] = direct_raccoon_details + report["Raccoon"]["vulnToRaccoon"] = report["Raccoon"]["Result"] + report["Raccoon"]["Result"] = 'vulnerable' if (report["Raccoon"]["Result"] == 'vulnerable' or report["Direct Raccoon"]["Result"] == 'vulnerable') else 'not vulnerable' + report["Raccoon"]["vulnToDirectRaccoon"] = report["Direct Raccoon"]["Result"] + report["Raccoon"]["Details"] = report["Direct Raccoon"]["Details"] + report.pop("Direct Raccoon",None) + + self.__output = {hostname : report} def output(self) -> (dict, dict): @@ -64,11 +178,9 @@ def input(self, **kwargs): * *hostname* (``str``) -- The hostname of the website to analyze. Can be an IP or a Name (DNS) * *args* (``list of str``) -- - Raw arguments for testssl.sh executable + Raw arguments for TLS-Scanner executable * *force* (``bool``) -- Force rescan by ignoring cached results , Default *False* - * *one* (``bool``) -- - Add ``--IP=one`` to testssl.sh executable calls, default *True* * *clean* (``bool``) -- clear the cache, default *False* """ @@ -150,11 +262,9 @@ def run(self, **kwargs) -> dict: * *hostname* (``str``) -- The hostname of the website to analyze. Can be an IP or a Name (DNS) * *args* (``list of str``) -- - Raw arguments for testssl.sh executable + Raw arguments for TLS-Scanner executable * *force* (``bool``) -- Force rescan by ignoring cached results , Default *False* - * *one* (``bool``) -- - Add ``--IP=one`` to testssl.sh executable calls, default *True* * *clean* (``bool``) -- clear the cache, default *False* @@ -200,12 +310,10 @@ def __scan(self, hostname: str, args: [str], force: bool, one: bool, clean: bool Scan internal module :param hostname: Hostname or IP :type hostname: str - :param args: Raw args for testssl.sh + :param args: Raw args for TLS-Scanner :type args: list of str :param force: Force the rescan, ignore the cached result. :type force: bool - :param one: Add '--IP=one' to testssl.sh calls. - :type one: bool :param clean: Clear the cache. :type clean: bool @@ -215,139 +323,18 @@ def __scan(self, hostname: str, args: [str], force: bool, one: bool, clean: bool self.__scan_hostname(hostname, args, force, one) - def __escape_output(self, output): - # Thanks to Stack Overflow - # https://stackoverflow.com/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python - ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') - output = ansi_escape.sub('', output) - return output - - def __parse_output(self, output: str): - output = self.__escape_output(output) - output = output.split("\n") - ''' - report = { - "ALPACA":{ - "Result" : "", - "Details" : {}, - }, - "Padding Oracle":{ - "Result" : "", - "Details" : {} - }, - "Raccoon" : { - "Result" : "" - }, - [...] - }''' - report = defaultdict(dict) - - alpaca_details = {} - padding_oracle_details = {} - direct_raccoon_details = {} - - for i in range(0,len(output)): - if "Attack Vulnerabilities" in output[i]: - j = i + 1 - while output[j] != "------------------------------------------------------------": - j += 1 - vulns = output[i+2:j-1] - - for vuln in vulns: - vuln,res = vuln.split(" : ",1) - vuln = vuln.replace("\t","").strip() - report[vuln]["Result"] = res - i = j # Skip lines - - elif "Alpaca Details" in output[i]: - j = i + 1 - while output[j] != "------------------------------------------------------------": - j += 1 - alpaca_details_temp = output[i+2:j-1] - for detail in alpaca_details_temp: - detail,res = detail.split(" : ",1) - detail = detail.replace("\t","") - if "Strict ALPN" == detail: - alpaca_details["Strict ALPN"] = res - elif "Strict SNI" == detail: - alpaca_details["Strict SNI"] = res - elif "ALPACA Mitigation" == detail: - alpaca_details["ALPACA Mitigation"] = res - i = j # Skip lines - - elif "Padding Oracle Details" in output[i] and report["Padding Oracle"]["Result"] == "vulnerable": - j = i + 1 - while output[j] != "------------------------------------------------------------": - j += 1 - padding_oracle_details_temp = output[i+2:j-1] - for detail in padding_oracle_details_temp: - detail = detail.split("|") - name = ('-'.join(detail[0].split("\t")[2:])).strip() - behaviour_difference = detail[1].strip() - result = detail[2].strip() - - if "<" in detail[3].strip(): - P = float(detail[3].strip()[4:]) - else: - P = float(detail[3].strip()[3:]) - - padding_oracle_details[name] = { - 'Behaviour' : behaviour_difference, - 'Result': result, - 'Confidence' : P - } - i = j # Skip lines - - elif "Direct Raccoon Results" in output[i] and report["Direct Raccoon"]["Result"] == "vulnerable": - j = i + 1 - while output[j] != "------------------------------------------------------------": - j += 1 - direct_raccoon_details_temp = output[i+2:j-1] - for detail in direct_raccoon_details_temp: - detail = detail.split("|") - name = detail[0].replace("\t","-").strip() - behaviour_difference = detail[1].strip() - result = detail[2].strip() - if "<" in detail[3].strip(): - P = float(detail[3].strip()[4:]) - else: - P = float(detail[3].strip()[3:]) - direct_raccoon_details[name] = { - 'Behaviour' : behaviour_difference, - 'Result': result, - 'Confidence' : P - } - i = j # Skip lines - - report["Padding Oracle"]["Details"] = padding_oracle_details - report["ALPACA"]["Details"] = alpaca_details - report["Direct Raccoon"]["Details"] = direct_raccoon_details - report["Raccoon"]["vulnToRaccoon"] = report["Raccoon"]["Result"] - report["Raccoon"]["Result"] = 'vulnerable' if (report["Raccoon"]["Result"] == 'vulnerable' or report["Direct Raccoon"]["Result"] == 'vulnerable') else 'not vulnerable' - report["Raccoon"]["vulnToDirectRaccoon"] = report["Direct Raccoon"]["Result"] - report["Raccoon"]["Details"] = report["Direct Raccoon"]["Details"] - report.pop("Direct Raccoon",None) - return report - def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): """ Internal module of scan :param hostname: Hostname or IP :type hostname: str - :param args: Raw args for testssl.sh + :param args: Raw args for TLS-Scanner :type args: list of str :param force: Force the rescan, ignore the cached result. - :type force: bool - :param one: Add '--IP=one' to testssl.sh calls. :type one: bool """ - # TODO: Add multihost support if force: - logging.debug("Starting testssl analysis") - file_name = uuid.uuid4().hex - logging.debug( - f"Scanning {hostname}, saving result to temp file {file_name}" - ) + logging.debug("Starting TLS-Scanner analysis") with open(devnull, "w") as null: cmd = [ "java", @@ -371,7 +358,6 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): output = "" try: - print(' '.join(cmd)) output = subprocess.check_output( cmd, stderr=sys.stderr, @@ -384,11 +370,7 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): except subprocess.CalledProcessError as c: logging.debug(c) - - data = self.__parse_output(output) - data = {hostname : data} - - cache, ip_cache = Parser(data).output() + cache, ip_cache = Parser(output).output() self.__update_cache(cache, ip_cache) else: From 793d36f7b29a84d7bc11fcf7270107eb93ae0172 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:33:35 +0200 Subject: [PATCH 041/209] Remove unused imports --- modules/server/wrappers/tlsscanner.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index d39d414..aee1429 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -1,13 +1,11 @@ -import json import logging import re import subprocess import sys -import uuid -from os import devnull, path, remove, sep +from os import devnull, sep from collections import defaultdict -from utils.urls import link_sep, url_strip, validate_ip +from utils.urls import url_strip from utils.validation import Validator From 238618fcc0dff148c4b7343f4bbf66d759cfea81 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Thu, 16 Jun 2022 11:59:37 +0200 Subject: [PATCH 042/209] Enable aliasing in the excluded modules --- modules/core.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/core.py b/modules/core.py index 5f57f8d..1822848 100644 --- a/modules/core.py +++ b/modules/core.py @@ -14,6 +14,7 @@ import datetime from enum import Enum from modules.report import Report as Report_module +from utils.configuration import get_aliases from utils.urls import link_sep from utils.urls import has_wildcard, remove_wildcard from utils.subdomain_enumeration import enumerate @@ -158,6 +159,19 @@ def input(self, **kwargs): ] ) kwargs["to_exclude"] = list(map(str.lower, kwargs["to_exclude"])) + + tmp_to_exclude = [] + aliases = get_aliases() + for module in kwargs["to_exclude"]: + if module in aliases: + for alias in aliases[module]: + if alias not in tmp_to_exclude: + tmp_to_exclude.append(alias) + else: + if module not in tmp_to_exclude: + tmp_to_exclude.append(module) + kwargs["to_exclude"] = tmp_to_exclude + # set outputfilename if not already set if "output" not in kwargs or not kwargs["output"]: # if not output file_name = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") @@ -348,6 +362,7 @@ def __preanalysis_tls_scanner( :return: preanalysis :rtype: dict """ + self.__logging.info(tls_scanner_args) if tls_scanner_args and ( type_of_analysis == self.Analysis.HOST or type_of_analysis == self.Analysis.DOMAINS @@ -355,6 +370,7 @@ def __preanalysis_tls_scanner( self.__logging.debug( f"Starting preanalysis tls_scanner with args {tls_scanner_args}..." ) + self.__logging.info("Running tls-scanner") TLS_Scanner().run( hostname=f"{hostname}:{port}", args=tls_scanner_args, @@ -376,6 +392,7 @@ def __load_modules(self, parsed_configuration: dict) -> (dict, dict, list): loaded_arguments = {} testssl_args = [] tls_scanner_args = [] + self.__logging.info(self.__input_dict["to_exclude"]) for name, module_args in parsed_configuration.items(): if name not in self.__input_dict["to_exclude"]: Module, args = module_args @@ -585,6 +602,7 @@ def __exec_anaylsis( loaded_modules, loaded_arguments, testssl_args, tls_scanner_args = self.__load_modules( parsed_configuration ) + self.__logging.info(tls_scanner_args) # preanalysis if needed self.__logging.info(f"Running analysis..") if type_of_analysis == self.Analysis.CONFIGURATION: From 7e1bb2c883179e3c77a7473e9e23ee7242bc1d6f Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:25:49 +0200 Subject: [PATCH 043/209] Small update Remove "one" parameter Remove useless logging --- modules/core.py | 3 --- modules/server/wrappers/tlsscanner.py | 12 ++++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/modules/core.py b/modules/core.py index 1822848..78764d2 100644 --- a/modules/core.py +++ b/modules/core.py @@ -362,7 +362,6 @@ def __preanalysis_tls_scanner( :return: preanalysis :rtype: dict """ - self.__logging.info(tls_scanner_args) if tls_scanner_args and ( type_of_analysis == self.Analysis.HOST or type_of_analysis == self.Analysis.DOMAINS @@ -392,7 +391,6 @@ def __load_modules(self, parsed_configuration: dict) -> (dict, dict, list): loaded_arguments = {} testssl_args = [] tls_scanner_args = [] - self.__logging.info(self.__input_dict["to_exclude"]) for name, module_args in parsed_configuration.items(): if name not in self.__input_dict["to_exclude"]: Module, args = module_args @@ -602,7 +600,6 @@ def __exec_anaylsis( loaded_modules, loaded_arguments, testssl_args, tls_scanner_args = self.__load_modules( parsed_configuration ) - self.__logging.info(tls_scanner_args) # preanalysis if needed self.__logging.info(f"Running analysis..") if type_of_analysis == self.Analysis.CONFIGURATION: diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index aee1429..942ba3b 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -281,7 +281,6 @@ def run(self, **kwargs) -> dict: force = ( self.__input_dict["force"] if "force" in self.__input_dict else False ) - one = self.__input_dict["one"] if "one" in self.__input_dict else True clean = ( self.__input_dict["clean"] if "clean" in self.__input_dict else False ) @@ -290,7 +289,6 @@ def run(self, **kwargs) -> dict: (self.__input_dict["hostname"], str), (args, list), (force, bool), - (one, bool), (clean, bool), ] ) @@ -298,12 +296,11 @@ def run(self, **kwargs) -> dict: str(self.__input_dict["hostname"]), args=args, force=force, - one=one, clean=clean, ) return self.output(hostname=self.__input_dict["hostname"]) - def __scan(self, hostname: str, args: [str], force: bool, one: bool, clean: bool): + def __scan(self, hostname: str, args: [str], force: bool, clean: bool): """ Scan internal module :param hostname: Hostname or IP @@ -318,10 +315,10 @@ def __scan(self, hostname: str, args: [str], force: bool, one: bool, clean: bool """ if clean: self.__clean_cache() - self.__scan_hostname(hostname, args, force, one) + self.__scan_hostname(hostname, args, force) - def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): + def __scan_hostname(self, hostname: str, args: [str], force: bool): """ Internal module of scan :param hostname: Hostname or IP @@ -329,7 +326,6 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): :param args: Raw args for TLS-Scanner :type args: list of str :param force: Force the rescan, ignore the cached result. - :type one: bool """ if force: logging.debug("Starting TLS-Scanner analysis") @@ -374,5 +370,5 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool, one: bool): else: if hostname not in self.__cache: self.__scan_hostname( - hostname, args=args, force=True, one=one + hostname, args=args, force=True ) # with force = True From c63c265144f03a2e726dcbfbbd09713604f59424 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 17 Jun 2022 16:12:25 +0200 Subject: [PATCH 044/209] ALPACA Nginx Dynamic Mitigations --- configs/mitigations/ALPACA.json | 5 ++++- modules/server/alpaca.py | 24 +++++++++++++++++++++++- modules/server/tlsscanner_base.py | 2 +- modules/server/wrappers/tlsscanner.py | 4 +++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/configs/mitigations/ALPACA.json b/configs/mitigations/ALPACA.json index 8276886..d8dbf8d 100644 --- a/configs/mitigations/ALPACA.json +++ b/configs/mitigations/ALPACA.json @@ -7,11 +7,14 @@ "#comment": "AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N", "Description": "ALPACA is an application layer protocol content confusion attack, exploiting TLS servers implementing different protocols but using compatible certificates, such as multi-domain or wildcard certificates. Attackers can redirect traffic from one subdomain to another, resulting in a valid TLS session. This breaks the authentication of TLS and cross-protocol attacks may be possible where the behavior of one protocol service may compromise the other at the application layer.", "Mitigation": { - "Textual": "Enable the TLS Extensions {extensions}." + "Textual": "Enable the TLS Extensions {extensions}.", + "Apache": "No snippet available", + "Nginx": "No snippet available" } }, "#comment": " https://alpaca-attack.com/ ", "#comment1": " https://nvd.nist.gov/vuln/detail/CVE-2021-3618 ", + "#comment2": "https://blog.sion.moe/methods-to-prevent-leaking-websites-origin-server-ip-behind-cdn/", "#omit-xml-declaration": "yes" } \ No newline at end of file diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 2ed10db..923215f 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -28,7 +28,7 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: condition = condition and key == "ALPACA" if condition: result["mitigation"] = load_mitigation("ALPACA") - # Handle the case if the the vulnerability is partially mitigated + # Handle the case if the the vulnerability is partially mitigated and provide a dynamic mitigation details = result["Details"] ext = "" if details["Strict SNI"] == "false": @@ -41,6 +41,28 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: ext = "ALPN" result['mitigation']['Entry']['Mitigation']['Textual'] = result['mitigation']['Entry']['Mitigation']['Textual'].format(extensions = ext) + if "ALPN" or "SNI" in ext: + result['mitigation']['Entry']['Mitigation']['Nginx'] = "" + if "ALPN" in ext: + result['mitigation']['Entry']['Mitigation']['Nginx'] = "To enable Strict ALPN in Nginx upgrade to version >=1.21.4.
" + if "SNI" in ext: + result['mitigation']['Entry']['Mitigation']['Nginx'] += """ + To enable Strict SNI in Nginx:
+ 1. If you are using Nginx>=1.19.4 edit you configuration file usually located in /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION); to look like this:
+ server {
+ listen 443 ssl default_server;
+ ssl_reject_handshake on;
+ }
+
+ server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name example.com;
+ [...]
+ }
+

+ 2. If you are using nginx<1.19.4 follow this guide
+ """ return result if condition else {} # to override diff --git a/modules/server/tlsscanner_base.py b/modules/server/tlsscanner_base.py index 6feb24f..111498f 100644 --- a/modules/server/tlsscanner_base.py +++ b/modules/server/tlsscanner_base.py @@ -97,7 +97,7 @@ def _obtain_results(self, results: dict, keys: list): for hostname in results: for key in keys: - condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" and results[hostname][key]['Result'] != "could not test (not vulnerable)" + condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" and results[hostname][key]['Result'] != "could not test (not vulnerable)" and results[hostname][key]['Result'] != "true" conditioned_result = self._set_mitigations( results[hostname][key], key, condition diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 942ba3b..ccbc74d 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -337,7 +337,9 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool): "-connect", f"{hostname}", "-scanDetail", - "QUICK" + "QUICK", + "-server_name", + f"{hostname.split(':')[0]}", ] if args: From 6fe882c0c9d6498f2aacbb4a19ee21ffc294d72d Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sat, 18 Jun 2022 13:39:40 +0200 Subject: [PATCH 045/209] ALPACA Apache Dynamic Mitigations --- configs/mitigations/ALPACA.json | 1 + modules/server/alpaca.py | 56 +++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/configs/mitigations/ALPACA.json b/configs/mitigations/ALPACA.json index d8dbf8d..862deb1 100644 --- a/configs/mitigations/ALPACA.json +++ b/configs/mitigations/ALPACA.json @@ -16,5 +16,6 @@ "#comment": " https://alpaca-attack.com/ ", "#comment1": " https://nvd.nist.gov/vuln/detail/CVE-2021-3618 ", "#comment2": "https://blog.sion.moe/methods-to-prevent-leaking-websites-origin-server-ip-behind-cdn/", + "#comment3": "https://blog.des.no/2020/11/configuring-strict-sni-with-apache-httpd-2-4/", "#omit-xml-declaration": "yes" } \ No newline at end of file diff --git a/modules/server/alpaca.py b/modules/server/alpaca.py index 923215f..5dc126d 100644 --- a/modules/server/alpaca.py +++ b/modules/server/alpaca.py @@ -44,24 +44,48 @@ def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: if "ALPN" or "SNI" in ext: result['mitigation']['Entry']['Mitigation']['Nginx'] = "" if "ALPN" in ext: - result['mitigation']['Entry']['Mitigation']['Nginx'] = "To enable Strict ALPN in Nginx upgrade to version >=1.21.4.
" + result['mitigation']['Entry']['Mitigation']['Nginx'] = "To enable Strict ALPN in Nginx upgrade to version >=1.21.4.

" + result['mitigation']['Entry']['Mitigation']['Apache'] = "Currenlty there is no snippet available to enable Strict ALPN.

" if "SNI" in ext: result['mitigation']['Entry']['Mitigation']['Nginx'] += """ - To enable Strict SNI in Nginx:
- 1. If you are using Nginx>=1.19.4 edit you configuration file usually located in /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION); to look like this:
- server {
- listen 443 ssl default_server;
- ssl_reject_handshake on;
- }
-
- server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
- server_name example.com;
- [...]
- }
-

- 2. If you are using nginx<1.19.4 follow this guide
+ To enable Strict SNI in Nginx:
+ 1. If you are using Nginx>=1.19.4 edit you configuration file usually located in /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION); to look like this:
+ server {
+ listen 443 ssl default_server;
+ ssl_reject_handshake on;
+ }
+
+ server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+ server_name example.com;
+ [...]
+ }
+

+ 2. If you are using nginx<1.19.4 follow this guide
+ """ + result['mitigation']['Entry']['Mitigation']['Apache'] += """ + To enable Strict SNI in Apache:
+ 1. The first step is to create a dummy certificate for localhost with a 100-year lifespan (unless you feel like setting up a cron job that regenerates a 90-day certificate every 30 days or so):
+ + sudo openssl genrsa -out /etc/ssl/private/localhost.key 2048
+ sudo openssl req -new -key /etc/ssl/private/localhost.key -subj /CN=localhost -out /etc/ssl/certs/localhost.csr
+ echo subjectAltName=DNS:localhost | sudo openssl x509 -in /etc/ssl/certs/localhost.csr -out /etc/ssl/certs/localhost.crt -req -signkey /etc/ssl/private/localhost.key -days 36525 -extfile -
+

+ 2. Create the virtual host configuration which uses that certificate so that TLS connections without a server name will now be directed to your dummy virtual host and fail:
+ + <VirtualHost *:443>
+ ServerName localhost
+ SSLEngine On
+ SSLStrictSNIVHostCheck On
+ SSLCertificateKeyFile /etc/ssl/private/localhost.key
+ SSLCertificateFile /etc/ssl/certs/localhost.crt
+ <Location />
+ Require all denied
+ </Location>
+ </VirtualHost>
+

+ This must be placed in a location where it will be read before any other virtual host configuration, for example before all other virtual host configurations in: /etc/apache2/sites-available/example.com.conf. """ return result if condition else {} From 44cd71de6a34ddf88d8833c6f50c5cb1446d7768 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Sat, 18 Jun 2022 14:04:21 +0200 Subject: [PATCH 046/209] Initialize TLS-Scanner submodules during installation --- dependencies.json | 5 +++++ install.py | 32 ++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/dependencies.json b/dependencies.json index 9b2b1c6..37e05a6 100644 --- a/dependencies.json +++ b/dependencies.json @@ -42,6 +42,11 @@ { "type": "compile_maven", "path": "TLS-Scanner" + }, + { + "type": "git-submodule", + "cmd": "update --init --recursive", + "path": "TLS-Scanner" } ] \ No newline at end of file diff --git a/install.py b/install.py index e22941d..30cf97b 100644 --- a/install.py +++ b/install.py @@ -36,6 +36,7 @@ def __init__(self, dependencies): # constructor zips = [] cfgs = [] apts = [] + git_submodules = {} maven_paths = [] logger.info("Loading dependencies...") for dependency in dependencies: # for each dependency @@ -54,9 +55,12 @@ def __init__(self, dependencies): # constructor elif dependency["type"] == "cfg": # if it's cfg cfgs.append(dependency["url"]) # append it's url to the cfg array logger.debug(f"Added dependency cfg {dependency['url']}") - elif dependency["type"] == "compile_maven": # if it's cfg - maven_paths.append(dependency["path"]) # append it's url to the cfg array + elif dependency["type"] == "compile_maven": # if it's maven project + maven_paths.append(dependency["path"]) # append it's path to the maven array logger.debug(f"Added dependency compile {dependency['path']}") + elif dependency["type"] == "git-submodule": # if it's reporitory submodule + git_submodules[dependency["path"]] = dependency["cmd"] + logger.debug(f"Added git submodule of {dependency['path']} with command git submodule {dependency['cmd']}") else: # if not found, throw warning logger.warning( f"Ignoring dependency {dependency['url']}, type {dependency['type']} is not recognized." @@ -84,6 +88,10 @@ def __init__(self, dependencies): # constructor self.git_clone(git) # and clone it logger.info(f"{file_name} done.") + for path in git_submodules: # for each git submodule, + self.git_submodules_init(path,git_submodules[path]) # initialize submodules + logger.info(f"Submodules of {path} done.") + logger.info("Installing dependencies...") logger.warning( "This may take a while... Rerun the tool with -v to see the detailed installation." @@ -93,7 +101,7 @@ def __init__(self, dependencies): # constructor self.install_dependencies("apts", results_apts) # install the dependencies pkg logger.info("Unzipping dependencies...") self.install_dependencies("zips", results_zips) # unzips the zips - logger.info("Compiling dependencies...") + logger.info("Compiling maven dependencies...") self.compile_maven_dependencies(maven_paths) # unzips the zips logger.info("Generating Certificates...") self.generate_cert() @@ -217,7 +225,23 @@ def compile_maven_dependencies(self, paths): ), cwd=f_path ) - + + def git_submodules_init(self, path, cmd): + cmd = ["git", "submodule"] + cmd.split(" ") + with open(devnull, "w") as null: + subprocess.check_call( + cmd, + stderr=sys.stderr, + stdout=( + sys.stdout + if logging.getLogger().isEnabledFor( + logging.DEBUG + ) # if the user asked for debug mode, let him see the output. + else null # else /dev/null + ), + cwd="dependencies/"+path + ) + def git_clone(self, url, path=None): file_name = self.get_filename(url) From 36a0a92224f850f7e5fd4ec2b5f563f85a3edf44 Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Mon, 20 Jun 2022 11:55:51 +0200 Subject: [PATCH 047/209] Remove duplicate TLS-Scanner arguments Duplicate TLS-Scanner arguments were not correctly removed --- modules/core.py | 5 ++++- modules/server/wrappers/tlsscanner.py | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/core.py b/modules/core.py index 78764d2..ec969a9 100644 --- a/modules/core.py +++ b/modules/core.py @@ -266,7 +266,10 @@ def __add_tls_scanner_args(self, module: TLS_Scanner_base, tls_scanner_args: lis """ if self.__is_tls_scanner(module): - tls_scanner_args += module._arguments + for arg in module._arguments: + if arg not in tls_scanner_args: + tls_scanner_args.append(arg) + return tls_scanner_args def __conf_analysis( diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index ccbc74d..0d823aa 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -345,11 +345,6 @@ def __scan_hostname(self, hostname: str, args: [str], force: bool): if args: logging.debug(f"Scanning with personalized args: {args}") cmd.append("-vulns") - aargs = [] - # Some vulnerabilities resuse some command arguments, so eliminate duplicates - for a in args: - if a not in aargs: - aargs.append(a) cmd.append(",".join(args)) output = "" From cf1dfba219c1ce3675c9e175fa7bc1da941103ac Mon Sep 17 00:00:00 2001 From: IvanValentini <34308333+IvanValentini@users.noreply.github.com> Date: Fri, 29 Jul 2022 09:52:53 +0200 Subject: [PATCH 048/209] Delete domains file --- domains | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 domains diff --git a/domains b/domains deleted file mode 100644 index bb0d638..0000000 --- a/domains +++ /dev/null @@ -1,2 +0,0 @@ -google.com -facebook.com From 45eb9daeb8226c3d250d920fc38287611236357d Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 26 Aug 2022 16:31:37 +0200 Subject: [PATCH 049/209] Fix report with multiple 'add_header' directive in a server block --- modules/configuration/nginx/nginx_configuration_base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index 7391a35..016773b 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -289,9 +289,16 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): :returns: True if vhost is vulnerable to misconfigured TLS strict security. :rtype: bool """ + is_vulnerable = False + if self.__key in vhost: + if any(isinstance(el, list) for el in vhost[self.__key]): # more than one 'add_header' directive + is_vulnerable = all([("Strict-Transport-Security" not in vhost) for vhost in vhost[self.__key]]) + else: + is_vulnerable = "Strict-Transport-Security" not in vhost[self.__key] + return ( self.__key not in vhost - or "Strict-Transport-Security" not in vhost[self.__key] + or is_vulnerable ) # vulnerable if True class Nginx_parse_configuration_checks_compression(): From a1a5d7fcb70171737caee85602dfe822179b1fec Mon Sep 17 00:00:00 2001 From: FedeC <24813315+turbostar190@users.noreply.github.com> Date: Fri, 26 Aug 2022 16:41:57 +0200 Subject: [PATCH 050/209] Variable name disambiguation --- modules/configuration/nginx/nginx_configuration_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/configuration/nginx/nginx_configuration_base.py b/modules/configuration/nginx/nginx_configuration_base.py index 016773b..9bacf86 100644 --- a/modules/configuration/nginx/nginx_configuration_base.py +++ b/modules/configuration/nginx/nginx_configuration_base.py @@ -292,7 +292,7 @@ def condition(self, vhost, openssl: str = None, ignore_openssl=False): is_vulnerable = False if self.__key in vhost: if any(isinstance(el, list) for el in vhost[self.__key]): # more than one 'add_header' directive - is_vulnerable = all([("Strict-Transport-Security" not in vhost) for vhost in vhost[self.__key]]) + is_vulnerable = all([("Strict-Transport-Security" not in vh) for vh in vhost[self.__key]]) else: is_vulnerable = "Strict-Transport-Security" not in vhost[self.__key] From 9c261b672a9d16bcc252f21e9a6d30c71d62835d Mon Sep 17 00:00:00 2001 From: Salvatore Manfredi Date: Sat, 24 Dec 2022 17:28:57 +0100 Subject: [PATCH 051/209] Update CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json --- configs/mitigations/CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/mitigations/CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json b/configs/mitigations/CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json index 589a36c..70a7941 100644 --- a/configs/mitigations/CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json +++ b/configs/mitigations/CERTIFICATE_OR_KEYSTORE_DISCLOSURE.json @@ -4,7 +4,7 @@ "ExtendedName": "Certificate or Keystore disclosure", "Description": "
", "Mitigation": { - "Textual": "You probabily have an hardcoded certificate or keystore. You should obfuscate it and properly encrypt it so nobody can use it if a decompilation is done. A strong and secure password should be sufficient.
Another mitigation should be dynamically downloading the certificate while runtime, from a trusted source, using a secure channel.
", + "Textual": "You probably have an hardcoded certificate or keystore. You should obfuscate it and properly encrypt it so nobody can use it if a decompilation is done. A strong and secure password should be sufficient.
Another mitigation should be dynamically downloading the certificate while runtime, from a trusted source, using a secure channel.
", } }, "#omit-xml-declaration": "yes" From aaa32ced1f04c0378b165ccce9fcdbff1011383f Mon Sep 17 00:00:00 2001 From: matteounitn <32160291+matteounitn@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:12:27 +0200 Subject: [PATCH 052/209] Fix minor issue --- modules/server/tlsscanner_base.py | 4 +++- modules/server/wrappers/tlsscanner.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/server/tlsscanner_base.py b/modules/server/tlsscanner_base.py index 111498f..546c445 100644 --- a/modules/server/tlsscanner_base.py +++ b/modules/server/tlsscanner_base.py @@ -97,7 +97,9 @@ def _obtain_results(self, results: dict, keys: list): for hostname in results: for key in keys: - condition = results[hostname][key]['Result'] != "not vulnerable" and results[hostname][key]['Result'] != "error" and results[hostname][key]['Result'] != "not tested yet" and results[hostname][key]['Result'] != "could not test (not vulnerable)" and results[hostname][key]['Result'] != "true" + not_in_this=["error","not tested yet","could not test (not vulnerable)","true","not vulnerable"] + + condition = results[hostname][key]['Result'] not in not_in_this if "Result" in results[hostname][key] else False conditioned_result = self._set_mitigations( results[hostname][key], key, condition diff --git a/modules/server/wrappers/tlsscanner.py b/modules/server/wrappers/tlsscanner.py index 0d823aa..bc892c2 100644 --- a/modules/server/wrappers/tlsscanner.py +++ b/modules/server/wrappers/tlsscanner.py @@ -130,7 +130,18 @@ def __parse(self): # parse method 'Confidence' : P } i = j # Skip lines - + if "Raccoon" not in report: + report["Raccoon"]={} + if "Result" not in report["Raccoon"]: + report["Raccoon"]["Result"] = "not vulnerable" + if "Direct Raccoon" not in report: + report["Direct Raccoon"]={} + if "Result" not in report["Direct Raccoon"]: + report["Direct Raccoon"]["Result"] = "not vulnerable" + if "Details" not in report["Direct Raccoon"]: + report["Direct Raccoon"]["Details"] = {} + + report["Padding Oracle"]["Details"] = padding_oracle_details report["ALPACA"]["Details"] = alpaca_details report["Direct Raccoon"]["Details"] = direct_raccoon_details From f3bc05a18930f22ee53ff777c351b68b8efbfd1b Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 10 Mar 2023 12:32:20 +0100 Subject: [PATCH 053/209] added tls assistant to my work directory --- README.md | 1 + configs/mitigations/POODLE.json | 19 + .../compliance/configuration_mapping.json | 4 + .../compliance/evaluations_mapping.json | 18 + configs/modules/compliance/misc_fields.json | 3 + configs/modules/compliance/sheet_columns.json | 3 + configs/modules/compliance/sheet_mapping.json | 11 + configs/modules/server/poodle.json | 20 + dump.json | 1 + install.py | 2 +- modules/compliance/compliance_base.py | 191 ++++ modules/compliance/compliance_many.py | 53 + modules/compliance/compliance_one.py | 44 + modules/compliance/requirements.db | 0 modules/compliance/wrappers/db_reader.py | 85 ++ modules/core.py | 1 + modules/server/poodle.py | 49 + modules/server/tlsfuzzer_base.py | 28 +- requirements.db | Bin 0 -> 1044480 bytes test.config | 32 + testssl_dump.json | 971 ++++++++++++++++++ testssl_dump_old.json | 752 ++++++++++++++ utils/configuration.py | 2 +- utils/database.py | 22 + 24 files changed, 2296 insertions(+), 16 deletions(-) create mode 100644 configs/mitigations/POODLE.json create mode 100644 configs/modules/compliance/configuration_mapping.json create mode 100644 configs/modules/compliance/evaluations_mapping.json create mode 100644 configs/modules/compliance/misc_fields.json create mode 100644 configs/modules/compliance/sheet_columns.json create mode 100644 configs/modules/compliance/sheet_mapping.json create mode 100644 configs/modules/server/poodle.json create mode 100644 dump.json create mode 100644 modules/compliance/compliance_base.py create mode 100644 modules/compliance/compliance_many.py create mode 100644 modules/compliance/compliance_one.py create mode 100644 modules/compliance/requirements.db create mode 100644 modules/compliance/wrappers/db_reader.py create mode 100644 modules/server/poodle.py create mode 100644 requirements.db create mode 100644 test.config create mode 100644 testssl_dump.json create mode 100644 testssl_dump_old.json create mode 100644 utils/database.py diff --git a/README.md b/README.md index ec41ece..a4dbd60 100644 --- a/README.md +++ b/README.md @@ -365,3 +365,4 @@ limitations under the License. Developed within [Security & Trust](https://st.fbk.eu/) Research Unit at [Fondazione Bruno Kessler](https://www.fbk.eu/en/) (Italy) + diff --git a/configs/mitigations/POODLE.json b/configs/mitigations/POODLE.json new file mode 100644 index 0000000..5fea2ed --- /dev/null +++ b/configs/mitigations/POODLE.json @@ -0,0 +1,19 @@ +{ + "Entry": { + "Name": "POODLE", + "ExtendedName": "Padding Oracle On Downgraded Legacy Encryption", + "CVE": "2014-3566", + "CVSS3": "3.4 (Low)", + "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", + "Description": "By exploiting the missing validation of the padding bytes during decryption, an attacker is able to guess the session cookie. The attack is mounted by performing a MITM and requesting a SSLv3 connection between the client and the server. Once accepted, the attack is performed by modifying the padding in order to guess the cookie.", + "Mitigation": { + "Textual": "Disable SSLv3 protocol support.", + "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. search for the line starting with: SSLProtocol
- if it contains the substring +SSLv3, remove it;
- otherwise, add -SSLv3 at the end of the line.

N.B. restart the server by typing: sudo service apache2 restart.", + "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {...} brackets configuration, find ssl_protocols;
3. Remove SSLv3 (if any). Make sure you have atleast another TLS protocol. If you can't find ssl_protocols you should be fine if your NGINX is updated.


N.B. restart the server by typing: sudo service nginx restart.
" + + } + }, + "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", + "#comment1": " https://www.acunetix.com/vulnerabilities/web/the-poodle-attack-sslv3-supported/ ", + "#omit-xml-declaration": "yes" +} \ No newline at end of file diff --git a/configs/modules/compliance/configuration_mapping.json b/configs/modules/compliance/configuration_mapping.json new file mode 100644 index 0000000..9525fda --- /dev/null +++ b/configs/modules/compliance/configuration_mapping.json @@ -0,0 +1,4 @@ +{ + "SSLProtocol": "Protocols", + "SSLCipherSuite": "Cipher Suites" +} \ No newline at end of file diff --git a/configs/modules/compliance/evaluations_mapping.json b/configs/modules/compliance/evaluations_mapping.json new file mode 100644 index 0000000..6b6d714 --- /dev/null +++ b/configs/modules/compliance/evaluations_mapping.json @@ -0,0 +1,18 @@ +{ + "security": { + "must": 0, + "must not": 0, + "not recommended": 1, + "recommended": 2, + "optional": 3, + "": 4 + }, + "legacy": { + "must": 0, + "must not": 0, + "recommended": 1, + "not recommended": 2, + "optional": 3, + "": 4 + } +} \ No newline at end of file diff --git a/configs/modules/compliance/misc_fields.json b/configs/modules/compliance/misc_fields.json new file mode 100644 index 0000000..babb9bf --- /dev/null +++ b/configs/modules/compliance/misc_fields.json @@ -0,0 +1,3 @@ +{ + "secure_client_renego": "Client-initiated renegotiation" +} \ No newline at end of file diff --git a/configs/modules/compliance/sheet_columns.json b/configs/modules/compliance/sheet_columns.json new file mode 100644 index 0000000..c8b33a9 --- /dev/null +++ b/configs/modules/compliance/sheet_columns.json @@ -0,0 +1,3 @@ +{ + "KeyLength": ["name", "length", "evaluation", "condition"] +} \ No newline at end of file diff --git a/configs/modules/compliance/sheet_mapping.json b/configs/modules/compliance/sheet_mapping.json new file mode 100644 index 0000000..f0ff913 --- /dev/null +++ b/configs/modules/compliance/sheet_mapping.json @@ -0,0 +1,11 @@ +{ + "Protocols": "Protocol", + "Cipher Suites": "CipherSuite", + "TLS extensions": "Extension", + "Supported groups": "Groups", + "Signature algorithms": "Signature", + "Hash Algorithm": "Hash", + "Certificate Signature": "CertificateSignature", + "Key lengths": "KeyLengths", + "Misc": "Misc" +} \ No newline at end of file diff --git a/configs/modules/server/poodle.json b/configs/modules/server/poodle.json new file mode 100644 index 0000000..0a16a5f --- /dev/null +++ b/configs/modules/server/poodle.json @@ -0,0 +1,20 @@ +{ + "input":[ + { + "name":"hostname", + "type":"str", + "description":"Hostname to analyze", + "required":"True" + } + ], + "description":"This modules runs the vulnerability check for POODLE.", + "path":"modules/server/poodle.py", + "class_name":"Poodle", + "output":[ + { + "name":"results", + "type":"dict", + "description":"results of the scan" + } + ] +} \ No newline at end of file diff --git a/dump.json b/dump.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/dump.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/install.py b/install.py index 30cf97b..5362ea2 100644 --- a/install.py +++ b/install.py @@ -225,7 +225,7 @@ def compile_maven_dependencies(self, paths): ), cwd=f_path ) - + def git_submodules_init(self, path, cmd): cmd = ["git", "submodule"] + cmd.split(" ") with open(devnull, "w") as null: diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py new file mode 100644 index 0000000..b733123 --- /dev/null +++ b/modules/compliance/compliance_base.py @@ -0,0 +1,191 @@ +import json + +from modules.compliance.wrappers.db_reader import Database +from modules.server.wrappers.testssl import Testssl +from utils.loader import load_configuration +from utils.validation import Validator + + +class Compliance: + def __init__(self): + self._input_dict = {} + self._database_instance = Database() + self._last_data = {} + self._output_dict = {} + self._user_configuration = {} + self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/modules/compliance/") + self.sheet_columns = load_configuration("sheet_columns", "configs/modules/compliance/") + self.misc_fields = load_configuration("misc_fields", "configs/modules/compliance/") + self.test_ssl = Testssl() + + def evaluation_to_use(self, evaluations, security: bool = True): + """ + Given two evaluations returns true if the first one wins, false otherwise. + + :param evaluations: list of evaluations to be checked + :type evaluations: list + :param security: True if security wins false if legacy wins, default to true + :type security: bool + :return: the standard which wins + :rtype: bool + """ + # If an evaluation is not mapped it can be considered as a Not mentioned + security_mapping = "security" if security else "legacy" + if not evaluations: + raise IndexError("Evaluations list is empty") + first_value = self.evaluations_mapping.get(security_mapping, {}).get(evaluations[0].replace("°", "")) + best = 0 + for i, el in enumerate(evaluations[1:]): + evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(el.replace("°", "")) + if first_value > evaluation_value: + best = i + 1 + # if they have the same value first wins + return best + + def input(self, **kwargs): + """ + Set the input parameters + + :param kwargs: input parameters + :type kwargs: dict + + :Keyword Arguments: + * *standard* (``list``) -- Guidelines to check against + * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol + * *actual_configuration* (``dict``) -- The configuration to check, not needed if generating + * *test_ssl* (``bool``) -- If true the user_configuration gets generated using testssl data + """ + actual_configuration = kwargs.get("actual_configuration") + use_test_ssl = kwargs.get("test_ssl") + if actual_configuration and Validator([(actual_configuration, dict)]): + self.prepare_configuration(actual_configuration) + elif use_test_ssl: + # test_ssl_output = self.test_ssl.run(**{"hostname": "falconvendor.davita.com"}) + # with open("dump.json", "w") as f: + # json.dump(test_ssl_output, f, indent=4) + + # this is temporary + with open("testssl_dump.json", 'r') as f: + test_ssl_output = json.load(f) + self.prepare_testssl_output(test_ssl_output) + + self._input_dict = kwargs + + # To override + def _worker(self, sheets_to_check): + """ + :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} + :type sheets_to_check: dict + + :return: processed results + :rtype: dict + + :raise NotImplementedError: + """ + raise NotImplementedError("This method should be reimplemented") + + def run(self, **kwargs): + self.input(**kwargs) + sheets_to_check = kwargs.get("sheets_to_check") + val = Validator() + val.dict(sheets_to_check) + self._worker(sheets_to_check) + return self.output() + + def output(self): + return self._output_dict.copy() + + def prepare_configuration(self, actual_configuration): + for field in actual_configuration: + new_field = actual_configuration[field] + if isinstance(new_field, str): + if "Cipher" in field: + new_field = new_field.split(":") if ":" in new_field else new_field + elif "Protocol" in field and " " in new_field: + tmp_dict = {} + for version in new_field.split(" "): + accepted = False if version[0] == '-' else True + new_version_name = version.replace("-", "").replace("v", " ") + if new_version_name[-2] != '.' and new_version_name != "all": + new_version_name += ".0" + tmp_dict[new_version_name] = accepted + new_field = tmp_dict + field_name = self._database_instance.configuration_mapping.get(field, field) + self._user_configuration[field_name] = new_field + + def prepare_testssl_output(self, test_ssl_output): + + for site in test_ssl_output: + for field in test_ssl_output[site]: + actual_dict = test_ssl_output[site][field] + if (field.startswith("SSL") or field.startswith("TLS")) and field[3] != "_": + if not self._user_configuration.get("Protocol"): + self._user_configuration["Protocol"] = {} + protocol_dict = self._user_configuration.get("Protocol") + new_version_name = field.replace("_", ".").replace("v", " ").replace("TLS1", "TLS 1") + if new_version_name[-2] != '.': + new_version_name += ".0" + protocol_dict[new_version_name] = "not" not in actual_dict["finding"] + elif field.startswith("cipher_x"): + if not self._user_configuration.get("CipherSuite"): + self._user_configuration["CipherSuite"] = set() + value = actual_dict.get("finding", "") + if " " in value: + value = value.split(" ")[-1] + self._user_configuration["CipherSuite"].add(value) + elif field.startswith("cert_keySize"): + if not self._user_configuration.get("KeyLengths"): + self._user_configuration["KeyLengths"] = [] + self._user_configuration["KeyLengths"].append(actual_dict["finding"].split(" ")[:2]) + elif field == "TLS_extensions": + entry = actual_dict["finding"] + entry = entry.replace("' '", ",").replace("'", "") + extensions: list = entry.split(",") + extensions_pairs = [] + for ex in extensions: + # the [1] is the iana code + extensions_pairs.append(ex.split("/#")[0].lower().replace(" ", "_")) + self._user_configuration["Extension"] = extensions_pairs + elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): + # TODO make this part more stable, now it may crash + if not self._user_configuration.get("CertificateSignature"): + self._user_configuration["CertificateSignature"] = set() + if not self._user_configuration.get("Hash"): + self._user_configuration["Hash"] = set() + tokens = actual_dict["finding"].split(" ") + sig_alg = tokens[-1] + hash_alg = tokens[0] + if sig_alg.startswith("SHA"): + sig_alg, hash_alg = hash_alg, sig_alg + self._user_configuration["CertificateSignature"].add(sig_alg) + self._user_configuration["Hash"].add(hash_alg) + elif field.startswith("cert_keySize"): + if not self._user_configuration.get("KeyLengths"): + self._user_configuration["KeyLengths"] = set() + self._user_configuration["KeyLengths"].add(tuple(actual_dict["finding"].split(" ")[:2])) + elif field == "PFS_ECDHE_curves": + values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ + else actual_dict["finding"] + self._user_configuration["Groups"] = values + elif field in self.misc_fields: + if not self._user_configuration.get("Misc"): + self._user_configuration["Misc"] = {} + self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] + + def update_result(self, sheet, name, evaluation, is_enabled): + information_level = None + action = None + if evaluation == "must" and not is_enabled: + information_level = "ERROR" + action = "has to be enabled" + elif evaluation == "must not" and is_enabled: + information_level = "ERROR" + action = "has to be disabled" + elif evaluation == "recommended" and not is_enabled: + information_level = "ALERT" + action = "should be enabled" + elif evaluation == "not recommended" and is_enabled: + information_level = "ALERT" + action = "should be disabled" + if information_level: + self._output_dict[sheet][name] = f"{information_level}: {name} {action}" diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py new file mode 100644 index 0000000..6e21bee --- /dev/null +++ b/modules/compliance/compliance_many.py @@ -0,0 +1,53 @@ +from modules.compliance.compliance_base import Compliance + + +# TODO fix this +class ComplianceMany(Compliance): + def _worker(self, sheets_to_check): + """ + :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} + :type sheets_to_check: dict + + :return: processed results + :rtype: dict + """ + if not self._user_configuration: + raise ValueError("No configuration provided") + columns = ["name", "evaluation", "condition"] + name_index = columns.index("name") + evaluation_index = columns.index("evaluation") + entries = {} + tables = [] + for sheet in sheets_to_check: + for guideline in sheets_to_check[sheet]: + if not self._output_dict.get(sheet): + self._output_dict[sheet] = {} + table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + tables.append(table_name) + self._database_instance.input(tables, other_filter="ORDER BY name") + data = self._database_instance.output(columns) + entries[sheet] = data + tables = [] + actual_evaluation = "" + for sheet in sheets_to_check: + config_field = self._database_instance.sheet_mapping.get(sheet) + counter = 0 + for entry in entries[sheet]: + name = entry[name_index] + evaluation = entry[evaluation_index] + if evaluation != actual_evaluation: + evaluations = [actual_evaluation, evaluation] + best_evaluation = self.evaluation_to_use(evaluations) + actual_evaluation = evaluations[best_evaluation] + if config_field and counter % len(sheets_to_check[sheet]) == 0: + field_value = self._user_configuration[config_field] + enabled = False + if isinstance(field_value, dict): + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + elif isinstance(field_value, list): + enabled = name in field_value + self.update_result(sheet, name, actual_evaluation, enabled) + actual_evaluation = "" + counter += 1 diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py new file mode 100644 index 0000000..c2e86f2 --- /dev/null +++ b/modules/compliance/compliance_one.py @@ -0,0 +1,44 @@ +from modules.compliance.compliance_base import Compliance + + +kwargs = {"sheets_to_check": {"Extension":{"ANSSI":"", "BSI":""}, "KeyLengths":{"ANSSI":""}, "Misc":{"ANSSI":""}}, "test_ssl":True} + +class ComplianceOne(Compliance): + def _worker(self, sheets_to_check): + """ + :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} + :type sheets_to_check: dict + + :return: processed results + :rtype: dict + """ + if not self._user_configuration: + raise ValueError("No configuration provided") + columns = ["name", "evaluation", "condition"] + name_index = columns.index("name") + evaluation_index = columns.index("evaluation") + for sheet in sheets_to_check: + # If the sheet isn't in the dictionary then I can use the default value + columns = self.sheet_columns.get(sheet, columns) + guideline = list(sheets_to_check[sheet].keys())[0] + if not self._output_dict.get(sheet): + self._output_dict[sheet] = {} + table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + self._database_instance.input([table_name]) + data = self._database_instance.output(columns) + config_field = sheet + for entry in data: + if config_field: + name = entry[name_index] + evaluation = entry[evaluation_index] + field_value = self._user_configuration[config_field] + enabled = False + if isinstance(field_value, dict): + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + elif field_value and isinstance(field_value, list) and isinstance(field_value[0], tuple): + enabled = entry[:2] in field_value + elif isinstance(field_value, list): + enabled = name in field_value + self.update_result(sheet, name, evaluation, enabled) diff --git a/modules/compliance/requirements.db b/modules/compliance/requirements.db new file mode 100644 index 0000000..e69de29 diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py new file mode 100644 index 0000000..d20c481 --- /dev/null +++ b/modules/compliance/wrappers/db_reader.py @@ -0,0 +1,85 @@ +import sqlite3 + +import utils.database as db_utils +from utils.loader import load_configuration + + +class Database: + database_file = "requirements.db" + + def __init__(self, file: str = database_file): + self.database_file = file + self.connection = sqlite3.connect(self.database_file) + self.cursor = self.connection.cursor() + self.configuration_mapping = load_configuration("configuration_mapping", "configs/modules/compliance/") + self.sheet_mapping = load_configuration("sheet_mapping", "configs/modules/compliance/") + + # # Retrieve the list of tables from the database + # self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") + # self.table_names = [table[0] for table in self.cursor.fetchall()] + self.__input_dict = {} + + def get_table_name(self, sheet, standard_name, version=""): + """ + Given the sheet, standard_name and version returns the corresponding database table + :param: standard_name -- Standard whose data are needed + :type standard_name: str + :param: sheet -- Sheet whose data are needed + :type sheet: str + :param: version -- (Optional) Standard's version + :type version: str + :return: the name of the table + :rtype: str + """ + sheet_name = self.get_sheet_name(sheet, sheet) + standard_name = db_utils.get_standard_name_for_database(standard_name) + version_name = db_utils.get_version_name_for_database(version) + return sheet_name + standard_name + version_name + + def get_sheet_name(self, sheet, default_value = None): + """ + Returns the sheet_name for the sheet, returns None if there isn't a mapping available + """ + return self.sheet_mapping.get(sheet, default_value) + + def input(self, tables, evaluation="", other_filter=""): + """ + Set the input parameters + + :param: tables -- List of tables from which data should be retrieved + :type tables: list + :param: evaluation -- (Optional) To filter between evaluations + :type evaluation: str + :param: other_filter -- (Optional) A filter to add to the query the WHERE/AND part will be handled automatically + :type other_filter: str + """ + self.__input_dict = { + "tables": tables, + "evaluation": evaluation, + "other_filter": other_filter + } + + def output(self, columns="*"): + """ + Retrieve data from the database. + + :param: tables -- List of tables from which data should be retrieved + :type columns: list + :param: evaluation -- (Optional) To filter between evaluations + """ + query = "" + first = True + evaluation = self.__input_dict.get("evaluation") + other_filter = self.__input_dict.get("other_filter") + for table in self.__input_dict.get("tables", []): + if first: + first = False + else: + query += " UNION ALL " + query += f"SELECT {','.join(columns)} FROM {table}" + if evaluation: + query += f" where evaluation = {evaluation}" + if other_filter: + query += " " + other_filter + self.cursor.execute(query) + return self.cursor.fetchall() diff --git a/modules/core.py b/modules/core.py index ac45453..1284cc0 100644 --- a/modules/core.py +++ b/modules/core.py @@ -6,6 +6,7 @@ from modules.server.tlsscanner_base import TLS_Scanner_base from modules.server.wrappers.testssl import Testssl from modules.server.wrappers.tlsscanner import TLS_Scanner +from modules.server.wrappers.testssl import Testssl from utils.booleanize import boolean_results from utils.logger import Logger from utils.colors import Color diff --git a/modules/server/poodle.py b/modules/server/poodle.py new file mode 100644 index 0000000..7ec323c --- /dev/null +++ b/modules/server/poodle.py @@ -0,0 +1,49 @@ +from modules.configuration.configuration_base import Parse_configuration_protocols +from modules.server.testssl_base import Testssl_base +from modules.stix.stix_base import Bundled +from utils.mitigations import load_mitigation + + +class Poodle(Testssl_base): + """ + Analysis of the poodle testssl results + """ + + conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) + stix = Bundled(mitigation_object=load_mitigation("POODLE")) + + def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: + """ + Sets the mitigations for the poodle results + + :param result: the result to set the mitigations in + :type result: dict + :param key: the key to set the mitigations for + :type key: str + :param condition: the condition to set the mitigations for + :type condition: bool + :return: the result with the mitigations + :rtype: dict + """ + condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") + if condition: + result["mitigation"] = load_mitigation("POODLE") + return result if condition else {} + + # to override + def _set_arguments(self): + """ + Sets the arguments for the testssl command + """ + self._arguments = ["-O"] + + # to override + def _worker(self, results): + """ + The worker method, which runs the testssl command + + :param results: dict + :return: dict + :rtype: dict + """ + return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) diff --git a/modules/server/tlsfuzzer_base.py b/modules/server/tlsfuzzer_base.py index 840a371..0dd800e 100644 --- a/modules/server/tlsfuzzer_base.py +++ b/modules/server/tlsfuzzer_base.py @@ -70,20 +70,6 @@ def _set_arguments(self): raise NotImplementedError("This method should be reimplemented!") # to override - def _worker(self, results): - """ - Dummy method to be overridden - - :param results: results to be processed - :type results: dict - - :return: processed results - :rtype: dict - - :raise NotImplementedError: - """ - raise NotImplementedError("This method should be reimplemented!") - def _obtain_results(self, results: dict, keys: dict): """ Obtain results from the analysis @@ -127,6 +113,20 @@ def _obtain_results(self, results: dict, keys: dict): self.__logging.info(f"Ignoring {script} analysis.\n") return out + def _worker(self, results): + """ + Dummy method to be overridden + + :param results: results to be processed + :type results: dict + + :return: processed results + :rtype: dict + + :raise NotImplementedError: + """ + raise NotImplementedError("This method should be reimplemented!") + def run(self, **kwargs): """ Run the analysis diff --git a/requirements.db b/requirements.db new file mode 100644 index 0000000000000000000000000000000000000000..e594cf4cb841a473cbd0e0891848adeea04b8cea GIT binary patch literal 1044480 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 literal 0 HcmV?d00001 diff --git a/test.config b/test.config new file mode 100644 index 0000000..dab4782 --- /dev/null +++ b/test.config @@ -0,0 +1,32 @@ +# generated 2023-03-07, Mozilla Guideline v5.6, Apache 2.4.41, OpenSSL 1.1.1k, intermediate configuration +# https://ssl-config.mozilla.org/#server=apache&version=2.4.41&config=intermediate&openssl=1.1.1k&guideline=5.6 + +# this configuration requires mod_ssl, mod_socache_shmcb, mod_rewrite, and mod_headers + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/ + RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] + + + + SSLEngine on + + # curl https://ssl-config.mozilla.org/ffdhe2048.txt >> /path/to/signed_cert_and_intermediate_certs_and_dhparams + SSLCertificateFile /path/to/signed_cert_and_intermediate_certs_and_dhparams + SSLCertificateKeyFile /path/to/private_key + + # enable HTTP/2, if available + Protocols h2 http/1.1 + + # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds) + Header always set Strict-Transport-Security "max-age=63072000" + + +# intermediate configuration +SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 +SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +SSLHonorCipherOrder off +SSLSessionTickets off + +SSLUseStapling On +SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" diff --git a/testssl_dump.json b/testssl_dump.json new file mode 100644 index 0000000..0fc34df --- /dev/null +++ b/testssl_dump.json @@ -0,0 +1,971 @@ +{ + "172.67.71.17": { + "service": { + "port": "443", + "severity": "INFO", + "finding": "HTTP" + }, + "pre_128cipher": { + "port": "443", + "severity": "INFO", + "finding": "No 128 cipher limit bug" + }, + "SSLv2": { + "port": "443", + "severity": "OK", + "finding": "not offered" + }, + "SSLv3": { + "port": "443", + "severity": "OK", + "finding": "not offered" + }, + "TLS1": { + "port": "443", + "severity": "INFO", + "finding": "not offered" + }, + "TLS1_1": { + "port": "443", + "severity": "INFO", + "finding": "is not offered" + }, + "TLS1_2": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "TLS1_3": { + "port": "443", + "severity": "OK", + "finding": "offered with final" + }, + "NPN": { + "port": "443", + "severity": "INFO", + "finding": "not offered" + }, + "ALPN_HTTP2": { + "port": "443", + "severity": "OK", + "finding": "h2" + }, + "ALPN": { + "port": "443", + "severity": "INFO", + "finding": "http/1.1" + }, + "cipherlist_NULL": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_aNULL": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_EXPORT": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_LOW": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_3DES_IDEA": { + "port": "443", + "severity": "INFO", + "cwe": "CWE-310", + "finding": "not offered" + }, + "cipherlist_AVERAGE": { + "port": "443", + "severity": "LOW", + "cwe": "CWE-310", + "finding": "offered" + }, + "cipherlist_STRONG": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "PFS": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "PFS_ciphers": { + "port": "443", + "severity": "INFO", + "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDHE-RSA-CHACHA20-POLY1305-OLD ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES128-SHA" + }, + "PFS_ECDHE_curves": { + "port": "443", + "severity": "OK", + "finding": "prime256v1 secp384r1 secp521r1 X25519" + }, + "cipher_order": { + "port": "443", + "severity": "OK", + "finding": "server -- TLS 1.3 client determined" + }, + "protocol_negotiated": { + "port": "443", + "severity": "OK", + "finding": "Default protocol TLS1.3" + }, + "cipher_negotiated": { + "port": "443", + "severity": "OK", + "finding": "TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519)" + }, + "cipherorder_TLSv1_2": { + "port": "443", + "severity": "INFO", + "finding": "ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-CHACHA20-POLY1305-OLD ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA AES128-GCM-SHA256 AES128-SHA ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA AES256-GCM-SHA384 AES256-SHA ECDHE-RSA-AES128-SHA256 AES128-SHA256 ECDHE-RSA-AES256-SHA384 AES256-SHA256" + }, + "TLS_extensions": { + "port": "443", + "severity": "INFO", + "finding": "'server name/#0' 'renegotiation info/#65281' 'EC point formats/#11' 'session ticket/#35' 'status request/#5' 'next protocol/#13172' 'key share/#51' 'supported versions/#43' 'extended master secret/#23' 'application layer protocol negotiation/#16'" + }, + "TLS_session_ticket": { + "port": "443", + "severity": "INFO", + "finding": "valid for 64800 seconds only (": { + "port": "443", + "severity": "OK", + "finding": "SHA256 with RSA" + }, + "cert_keySize ": { + "port": "443", + "severity": "INFO", + "finding": "RSA 2048 bits" + }, + "cert_keyUsage ": { + "port": "443", + "severity": "INFO", + "finding": "Digital Signature, Key Encipherment" + }, + "cert_extKeyUsage ": { + "port": "443", + "severity": "INFO", + "finding": "TLS Web Server Authentication, TLS Web Client Authentication" + }, + "cert_serialNumber ": { + "port": "443", + "severity": "INFO", + "finding": "039CA4946FFEA7A31B98DE488D3DF8AB" + }, + "cert_fingerprintSHA1 ": { + "port": "443", + "severity": "INFO", + "finding": "sha1 48835A14D3749891152DD07B06B0FC0EF369108B" + }, + "cert_fingerprintSHA256 ": { + "port": "443", + "severity": "INFO", + "finding": "sha256 E06E2B75257BDD4B1DA9FBF332DAF67C76AF1395F06D47B9BF9BC0BA487CF7CC" + }, + "cert ": { + "port": "443", + "severity": "INFO", + "finding": "-----BEGIN CERTIFICATE----- MIIGyjCCBbKgAwIBAgIQA5yklG/+p6MbmN5IjT34qzANBgkqhkiG9w0BAQsFADBK MQswCQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UE AxMXQ2xvdWRmbGFyZSBJbmMgUlNBIENBLTIwHhcNMjIwNjE3MDAwMDAwWhcNMjMw NjE3MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5j LjEeMBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAtZqriCZo8uz5cy8ZksrPrL6wJIFNBGzsGd6lehcn 8bFF+jIIxcAtxB3Z1rNjA1WwQDySUAKhzcihWrIZmECrT/lKCNwMCzH/MzH7v8Dn r+PCrJZ1PgnHnTnrGO/Aa+8OjV/WmA63WXDTe5A85dqsWlrLm2sDqkM5Fj0aH+z+ 77I//11hgdpdQfUbJG2jJybv127wD5oqClM4TygprsXpELr7Mpuepzbd6KDnAv1b 1kY2buRNuyjpjacYG+KBIr81Sf+ovcetpyVGPhaZpKNVhCRkMTnCScSGGw4kZ+Gq a5pPxwaTju54MpvRtYQj7Bhtj87W2s6QHabe3gHunESC2QIDAQABo4IDfzCCA3sw HwYDVR0jBBgwFoAUGKka/LJFScFvMDQIK9mHnLAlV3owHQYDVR0OBBYEFIBMtmcR 6BU7WmD4BsF69tioHyeTMEQGA1UdEQQ9MDuCD3BzaWNvYWN0aXZhLmNvbYIRKi5w c2ljb2FjdGl2YS5jb22CFXNuaS5jbG91ZGZsYXJlc3NsLmNvbTAOBgNVHQ8BAf8E BAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHsGA1UdHwR0MHIw N6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9DbG91ZGZsYXJlSW5jUlNB Q0EtMi5jcmwwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9DbG91ZGZs YXJlSW5jUlNBQ0EtMi5jcmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEF BQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMHYGCCsGAQUFBwEBBGow aDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEAGCCsGAQUF BzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY1JT QUNBLTIuY3J0MAwGA1UdEwEB/wQCMAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsB aQB1AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAABgW8+E2EAAAQD AEYwRAIgE1CZuC75FvsPgaCxkwecCo5EV8BRM+Kxx5hSas2HBzoCIFvOB4g75l3r n7Yp8az+w9IYQE7Z0RVsKLHOoUeSbg4oAHcANc8ZG7+xbFe/D61MbULLu7YnICZR 6j/hKu+oA8M71kwAAAGBbz4TbgAABAMASDBGAiEAs1u1up6Y+QfG8pI1xHxJJ1Z+ +Q05KiquZx3fC6SoENMCIQD4v7ZP/lA9hoyZ4QifZPRl+Lu+zNcVSZ84my8FwJoQ mAB3ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55NqWaAAABgW8+E4oAAAQD AEgwRgIhAPXuTB3uW9P+WvQlBC9Yt+k+hgzUqhEZ0d1ujg+nBAqIAiEAqweYa87n pW3grltuUxzKJ0v7ihreRIIDC+Jhywz+/kowDQYJKoZIhvcNAQELBQADggEBABCa SXx57NiKUThWXVY94YKOn1QrOIoXCwcyAV53w2d6yS5+nl1anr1ZOiqhCO37rTDf gOUixwKNWHrDegLmRvL51IljNJKlt+/pS/d30rPDAbh0YqqU7dQmpbafW6A3Fblm KBojmWWdRua4N1Z0mYmDZEvEZcm9BLB0a0kvYh9Z6wOaHjLsGk0j8MaVJAZNGFoB hS6QDc5ksDz89oYB8gR8W7EULlcEuKHOKx9ypjZR8fa64+BebRp1YctZNyLD+acv 7CrJHZVdeUY3L8UYKS2OAZu96HAk8Kv6BzQGfIoAQtpmvH5gIpy8I5xF00D6voGY 5ru+ZswSOL6EfE4P6Ns= -----END CERTIFICATE-----" + }, + "cert_commonName ": { + "port": "443", + "severity": "OK", + "finding": "sni.cloudflaressl.com" + }, + "cert_commonName_wo_SNI ": { + "port": "443", + "severity": "INFO", + "finding": "request w/o SNI didn't succeed" + }, + "cert_subjectAltName ": { + "port": "443", + "severity": "INFO", + "finding": "psicoactiva.com *.psicoactiva.com sni.cloudflaressl.com" + }, + "cert_caIssuers ": { + "port": "443", + "severity": "INFO", + "finding": "Cloudflare Inc RSA CA-2 (Cloudflare, Inc. from US)" + }, + "cert_trust ": { + "port": "443", + "severity": "OK", + "finding": "Ok via SAN (SNI mandatory)" + }, + "cert_chain_of_trust ": { + "port": "443", + "severity": "OK", + "finding": "passed." + }, + "cert_certificatePolicies_EV ": { + "port": "443", + "severity": "INFO", + "finding": "no" + }, + "cert_eTLS ": { + "port": "443", + "severity": "INFO", + "finding": "not present" + }, + "cert_expirationStatus ": { + "port": "443", + "severity": "OK", + "finding": "100 >= 60 days" + }, + "cert_notBefore ": { + "port": "443", + "severity": "INFO", + "finding": "2022-06-17 02:00" + }, + "cert_notAfter ": { + "port": "443", + "severity": "OK", + "finding": "2023-06-18 01:59" + }, + "cert_validityPeriod ": { + "port": "443", + "severity": "INFO", + "finding": "No finding" + }, + "certs_countServer ": { + "port": "443", + "severity": "INFO", + "finding": "2" + }, + "certs_list_ordering_problem ": { + "port": "443", + "severity": "INFO", + "finding": "no" + }, + "cert_crlDistributionPoints ": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "cert_ocspURL ": { + "port": "443", + "severity": "INFO", + "finding": "http://ocsp.digicert.com" + }, + "OCSP_stapling ": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "cert_ocspRevoked ": { + "port": "443", + "severity": "OK", + "finding": "not revoked" + }, + "cert_mustStapleExtension ": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "DNS_CAArecord ": { + "port": "443", + "severity": "LOW", + "finding": "--" + }, + "certificate_transparency ": { + "port": "443", + "severity": "OK", + "finding": "yes (certificate extension)" + }, + "cert_signatureAlgorithm ": { + "port": "443", + "severity": "OK", + "finding": "ECDSA with SHA256" + }, + "cert_keySize ": { + "port": "443", + "severity": "OK", + "finding": "EC 256 bits" + }, + "cert_keyUsage ": { + "port": "443", + "severity": "INFO", + "finding": "Digital Signature" + }, + "cert_extKeyUsage ": { + "port": "443", + "severity": "INFO", + "finding": "TLS Web Server Authentication, TLS Web Client Authentication" + }, + "cert_serialNumber ": { + "port": "443", + "severity": "INFO", + "finding": "0AB23C8E316FFECA015D9077301B9F21" + }, + "cert_fingerprintSHA1 ": { + "port": "443", + "severity": "INFO", + "finding": "sha1 55727091D5A9A96C1539F96278EDC292EB5FB721" + }, + "cert_fingerprintSHA256 ": { + "port": "443", + "severity": "INFO", + "finding": "sha256 4466AC40E06F9C629B554E07331190A4645EEBCCDF1041DB7A2FD0C48A45CAFE" + }, + "cert ": { + "port": "443", + "severity": "INFO", + "finding": "-----BEGIN CERTIFICATE----- MIIFPDCCBOGgAwIBAgIQCrI8jjFv/soBXZB3MBufITAKBggqhkjOPQQDAjBKMQsw CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjIwNjE3MDAwMDAwWhcNMjMwNjE3 MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI zj0DAQcDQgAEWEiG6XL/XoNpr+81AhGCztrZYowNJkJLNdnLZGLMlpfOEW9sy7VQ X/SJKxIkdf+yfRBCF1FIbBFViEfqQv80UKOCA3wwggN4MB8GA1UdIwQYMBaAFKXO N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBTNi6Y4XRGMGfnJYcgoqJZJjjjl kDBEBgNVHREEPTA7gg9wc2ljb2FjdGl2YS5jb22CESoucHNpY29hY3RpdmEuY29t ghVzbmkuY2xvdWRmbGFyZXNzbC5jb20wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQW MBQGCCsGAQUFBwMBBggrBgEFBQcDAjB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8v Y3JsMy5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMDegNaAz hjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMu Y3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93 d3cuZGlnaWNlcnQuY29tL0NQUzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGG GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2Nh Y2VydHMuZGlnaWNlcnQuY29tL0Nsb3VkZmxhcmVJbmNFQ0NDQS0zLmNydDAMBgNV HRMBAf8EAjAAMIIBfAYKKwYBBAHWeQIEAgSCAWwEggFoAWYAdQDoPtDaPvUGNTLn Vyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYFvPhYZAAAEAwBGMEQCIAeWyxBVYxc5 vQxtW10e80d7fGmfcH25IyTaYjpSazydAiBNwMCxQYgQWGmbGBetDv1Z+j2JmQZZ z9pLgQQGn7WRiAB1ADXPGRu/sWxXvw+tTG1Cy7u2JyAmUeo/4SrvqAPDO9ZMAAAB gW8+FjkAAAQDAEYwRAIgUcabVd+wLyEuNb/Be6pDQ4E0+3gBMEFINUifwiWCNcoC IFbgfewsz/79X5K53aJhpVjYebSyrdr4mnC4EFbQngiaAHYAtz77JN+cTbp18jnF ulj0bF38Qs96nzXEnh0JgSXttJkAAAGBbz4WPgAABAMARzBFAiAJsYIQcKWIPb4X oYIITqYNyjxNWdMstWXf9khypukdYwIhAJyXW2CYYTbMaDSAbOmoNddnCO1g/tcD r5+3Z4spBIPHMAoGCCqGSM49BAMCA0kAMEYCIQDwQmYZNOf1DyjwKV6iFQ7TAEmz P17FV7IrCneJa1ZJegIhALBm2F228pma/EzfBOZ+plCCW6QDjJ+epyCCAZ+Iu6qN -----END CERTIFICATE-----" + }, + "cert_commonName ": { + "port": "443", + "severity": "OK", + "finding": "sni.cloudflaressl.com" + }, + "cert_commonName_wo_SNI ": { + "port": "443", + "severity": "INFO", + "finding": "request w/o SNI didn't succeed, usual for EC certificates" + }, + "cert_subjectAltName ": { + "port": "443", + "severity": "INFO", + "finding": "psicoactiva.com *.psicoactiva.com sni.cloudflaressl.com" + }, + "cert_caIssuers ": { + "port": "443", + "severity": "INFO", + "finding": "Cloudflare Inc ECC CA-3 (Cloudflare, Inc. from US)" + }, + "cert_trust ": { + "port": "443", + "severity": "OK", + "finding": "Ok via SAN (SNI mandatory)" + }, + "cert_chain_of_trust ": { + "port": "443", + "severity": "OK", + "finding": "passed." + }, + "cert_certificatePolicies_EV ": { + "port": "443", + "severity": "INFO", + "finding": "no" + }, + "cert_eTLS ": { + "port": "443", + "severity": "INFO", + "finding": "not present" + }, + "cert_expirationStatus ": { + "port": "443", + "severity": "OK", + "finding": "100 >= 60 days" + }, + "cert_notBefore ": { + "port": "443", + "severity": "INFO", + "finding": "2022-06-17 02:00" + }, + "cert_notAfter ": { + "port": "443", + "severity": "OK", + "finding": "2023-06-18 01:59" + }, + "cert_validityPeriod ": { + "port": "443", + "severity": "INFO", + "finding": "No finding" + }, + "certs_countServer ": { + "port": "443", + "severity": "INFO", + "finding": "2" + }, + "certs_list_ordering_problem ": { + "port": "443", + "severity": "INFO", + "finding": "no" + }, + "cert_crlDistributionPoints ": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "cert_ocspURL ": { + "port": "443", + "severity": "INFO", + "finding": "http://ocsp.digicert.com" + }, + "OCSP_stapling ": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "cert_ocspRevoked ": { + "port": "443", + "severity": "OK", + "finding": "not revoked" + }, + "cert_mustStapleExtension ": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "DNS_CAArecord ": { + "port": "443", + "severity": "LOW", + "finding": "--" + }, + "certificate_transparency ": { + "port": "443", + "severity": "OK", + "finding": "yes (certificate extension)" + }, + "HTTP_status_code": { + "port": "443", + "severity": "INFO", + "finding": "301 Moved Permanently ('/')" + }, + "HTTP_clock_skew": { + "port": "443", + "severity": "INFO", + "finding": "0 seconds from localtime" + }, + "HSTS_time": { + "port": "443", + "severity": "OK", + "finding": "180 days (=15552000 seconds) > 15465600 seconds" + }, + "HSTS_subdomains": { + "port": "443", + "severity": "OK", + "finding": "includes subdomains" + }, + "HSTS_preload": { + "port": "443", + "severity": "OK", + "finding": "domain IS marked for preloading" + }, + "HPKP": { + "port": "443", + "severity": "INFO", + "finding": "No support for HTTP Public Key Pinning" + }, + "banner_server": { + "port": "443", + "severity": "INFO", + "finding": "Server-Timing: cf-q-config;dur=7.0000005507609e-06 cloudflare" + }, + "banner_application": { + "port": "443", + "severity": "INFO", + "finding": "No application banner found" + }, + "cookie_count": { + "port": "443", + "severity": "INFO", + "finding": "0 at '/' (30x detected, better try target URL of 30x)" + }, + "X-Frame-Options": { + "port": "443", + "severity": "OK", + "finding": "SAMEORIGIN" + }, + "X-Content-Type-Options": { + "port": "443", + "severity": "OK", + "finding": "nosniff" + }, + "Content-Security-Policy": { + "port": "443", + "severity": "OK", + "finding": "frame-ancestors 'self' https://psicoactiva.com;" + }, + "X-XSS-Protection": { + "port": "443", + "severity": "INFO", + "finding": "1; mode=block" + }, + "banner_reverseproxy": { + "port": "443", + "severity": "INFO", + "cwe": "CWE-200", + "finding": "--" + }, + "heartbleed": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-0160", + "cwe": "CWE-119", + "finding": "not vulnerable, no heartbeat extension" + }, + "CCS": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-0224", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "ticketbleed": { + "port": "443", + "severity": "OK", + "cve": "CVE-2016-9244", + "cwe": "CWE-200", + "finding": "not vulnerable" + }, + "ROBOT": { + "port": "443", + "severity": "OK", + "cve": "CVE-2017-17382 CVE-2017-17427 CVE-2017-17428 CVE-2017-13098 CVE-2017-1000385 CVE-2017-13099 CVE-2016-6883 CVE-2012-5081 CVE-2017-6168", + "cwe": "CWE-203", + "finding": "not vulnerable" + }, + "secure_renego": { + "port": "443", + "severity": "WARN", + "cwe": "CWE-310", + "finding": "OpenSSL handshake didn't succeed" + }, + "secure_client_renego": { + "port": "443", + "severity": "OK", + "cve": "CVE-2011-1473", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "CRIME_TLS": { + "port": "443", + "severity": "OK", + "cve": "CVE-2012-4929", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "BREACH": { + "port": "443", + "severity": "OK", + "cve": "CVE-2013-3587", + "cwe": "CWE-310", + "finding": "not vulnerable, no HTTP compression - only supplied '/' tested" + }, + "POODLE_SSL": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-3566", + "cwe": "CWE-310", + "finding": "not vulnerable, no SSLv3" + }, + "fallback_SCSV": { + "port": "443", + "severity": "OK", + "finding": "no protocol below TLS 1.2 offered" + }, + "SWEET32": { + "port": "443", + "severity": "OK", + "cve": "CVE-2016-2183 CVE-2016-6329", + "cwe": "CWE-327", + "finding": "not vulnerable" + }, + "FREAK": { + "port": "443", + "severity": "OK", + "cve": "CVE-2015-0204", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "DROWN": { + "port": "443", + "severity": "OK", + "cve": "CVE-2016-0800 CVE-2016-0703", + "cwe": "CWE-310", + "finding": "not vulnerable on this host and port" + }, + "DROWN_hint": { + "port": "443", + "severity": "INFO", + "cve": "CVE-2016-0800 CVE-2016-0703", + "cwe": "CWE-310", + "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://censys.io/ipv4?q=sha256 E06E2B75257BDD4B1DA9FBF332DAF67C76AF1395F06D47B9BF9BC0BA487CF7CC" + }, + "LOGJAM": { + "port": "443", + "severity": "OK", + "cve": "CVE-2015-4000", + "cwe": "CWE-310", + "finding": "not vulnerable, no DH EXPORT ciphers," + }, + "LOGJAM-common_primes": { + "port": "443", + "severity": "OK", + "cve": "CVE-2015-4000", + "cwe": "CWE-310", + "finding": "no DH key with <= TLS 1.2" + }, + "BEAST": { + "port": "443", + "severity": "OK", + "cve": "CVE-2011-3389", + "cwe": "CWE-20", + "finding": "not vulnerable, no SSL3 or TLS1" + }, + "LUCKY13": { + "port": "443", + "severity": "LOW", + "cve": "CVE-2013-0169", + "cwe": "CWE-310", + "finding": "potentially vulnerable, uses TLS CBC ciphers" + }, + "RC4": { + "port": "443", + "severity": "OK", + "cve": "CVE-2013-2566 CVE-2015-2808", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "cipher_x1302": { + "port": "443", + "severity": "INFO", + "finding": "x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384" + }, + "cipher_x1303": { + "port": "443", + "severity": "INFO", + "finding": "x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256" + }, + "cipher_xcc14": { + "port": "443", + "severity": "INFO", + "finding": "xcc14 ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD" + }, + "cipher_xcc13": { + "port": "443", + "severity": "INFO", + "finding": "xcc13 ECDHE-RSA-CHACHA20-POLY1305-OLD ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD" + }, + "cipher_xc030": { + "port": "443", + "severity": "INFO", + "finding": "xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + }, + "cipher_xc02c": { + "port": "443", + "severity": "INFO", + "finding": "xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" + }, + "cipher_xc028": { + "port": "443", + "severity": "INFO", + "finding": "xc028 ECDHE-RSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" + }, + "cipher_xc024": { + "port": "443", + "severity": "INFO", + "finding": "xc024 ECDHE-ECDSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384" + }, + "cipher_xc014": { + "port": "443", + "severity": "INFO", + "finding": "xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" + }, + "cipher_xc00a": { + "port": "443", + "severity": "INFO", + "finding": "xc00a ECDHE-ECDSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" + }, + "cipher_xcca9": { + "port": "443", + "severity": "INFO", + "finding": "xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" + }, + "cipher_xcca8": { + "port": "443", + "severity": "INFO", + "finding": "xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" + }, + "cipher_x9d": { + "port": "443", + "severity": "INFO", + "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" + }, + "cipher_x3d": { + "port": "443", + "severity": "INFO", + "finding": "x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" + }, + "cipher_x35": { + "port": "443", + "severity": "INFO", + "finding": "x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" + }, + "cipher_x1301": { + "port": "443", + "severity": "INFO", + "finding": "x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256" + }, + "cipher_xc02f": { + "port": "443", + "severity": "INFO", + "finding": "xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + }, + "cipher_xc02b": { + "port": "443", + "severity": "INFO", + "finding": "xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + }, + "cipher_xc027": { + "port": "443", + "severity": "INFO", + "finding": "xc027 ECDHE-RSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" + }, + "cipher_xc023": { + "port": "443", + "severity": "INFO", + "finding": "xc023 ECDHE-ECDSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" + }, + "cipher_xc013": { + "port": "443", + "severity": "INFO", + "finding": "xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" + }, + "cipher_xc009": { + "port": "443", + "severity": "INFO", + "finding": "xc009 ECDHE-ECDSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" + }, + "cipher_x9c": { + "port": "443", + "severity": "INFO", + "finding": "x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" + }, + "cipher_x3c": { + "port": "443", + "severity": "INFO", + "finding": "x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" + }, + "cipher_x2f": { + "port": "443", + "severity": "INFO", + "finding": "x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" + }, + "clientsimulation-android_442": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-android_500": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305-OLD" + }, + "clientsimulation-android_60": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305-OLD" + }, + "clientsimulation-android_70": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-android_81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-android_90": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-android_X": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-chrome_74_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-chrome_79_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-firefox_66_win81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-firefox_71_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-ie_6_xp": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_8_win7": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_8_xp": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_11_win7": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-ie_11_win81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-ie_11_winphone81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-ie_11_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-edge_15_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-edge_17_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-opera_66_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-safari_9_ios9": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-safari_9_osx1011": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-safari_10_osx1012": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-safari_121_ios_122": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_CHACHA20_POLY1305_SHA256" + }, + "clientsimulation-safari_130_osx_10146": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_CHACHA20_POLY1305_SHA256" + }, + "clientsimulation-apple_ats_9_ios9": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-java_6u45": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-java_7u25": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-java_8u161": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-java1102": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-java1201": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "clientsimulation-openssl_102e": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + }, + "clientsimulation-openssl_110l": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305" + }, + "clientsimulation-openssl_111d": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" + }, + "clientsimulation-thunderbird_68_3_1": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + }, + "scanTime": { + "port": "443", + "severity": "INFO", + "finding": "101" + } + } +} \ No newline at end of file diff --git a/testssl_dump_old.json b/testssl_dump_old.json new file mode 100644 index 0000000..85085a1 --- /dev/null +++ b/testssl_dump_old.json @@ -0,0 +1,752 @@ +{ + "150.35.235.24": { + "service": { + "port": "443", + "severity": "INFO", + "finding": "HTTP" + }, + "pre_128cipher": { + "port": "443", + "severity": "INFO", + "finding": "No 128 cipher limit bug" + }, + "SSLv2": { + "port": "443", + "severity": "OK", + "finding": "not offered" + }, + "SSLv3": { + "port": "443", + "severity": "OK", + "finding": "not offered" + }, + "TLS1": { + "port": "443", + "severity": "INFO", + "finding": "not offered" + }, + "TLS1_1": { + "port": "443", + "severity": "INFO", + "finding": "is not offered" + }, + "TLS1_2": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "TLS1_3": { + "port": "443", + "severity": "INFO", + "finding": "not offered + downgraded to weaker protocol" + }, + "NPN": { + "port": "443", + "severity": "INFO", + "finding": "not offered" + }, + "ALPN": { + "port": "443", + "severity": "INFO", + "finding": "http/1.1" + }, + "cipherlist_NULL": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_aNULL": { + "port": "443", + "severity": "OK", + "cwe": "CWE-327", + "finding": "not offered" + }, + "cipherlist_EXPORT": { + "port": "443", + "severity": "CRITICAL", + "cwe": "CWE-327", + "finding": "offered" + }, + "cipherlist_LOW": { + "port": "443", + "severity": "HIGH", + "cwe": "CWE-327", + "finding": "offered" + }, + "cipherlist_3DES_IDEA": { + "port": "443", + "severity": "MEDIUM", + "cwe": "CWE-310", + "finding": "offered" + }, + "cipherlist_AVERAGE": { + "port": "443", + "severity": "LOW", + "cwe": "CWE-310", + "finding": "offered" + }, + "cipherlist_STRONG": { + "port": "443", + "severity": "OK", + "finding": "offered" + }, + "PFS": { + "port": "443", + "severity": "MEDIUM", + "finding": "No ciphers supporting (P)FS offered" + }, + "cipher_order": { + "port": "443", + "severity": "OK", + "finding": "server" + }, + "protocol_negotiated": { + "port": "443", + "severity": "OK", + "finding": "Default protocol TLS1.2" + }, + "cipher_negotiated": { + "port": "443", + "severity": "OK", + "finding": "AES256-GCM-SHA384 (your /usr/bin/openssl cannot show DH bits)" + }, + "cipherorder_TLSv1_2": { + "port": "443", + "severity": "INFO", + "finding": "AES256-GCM-SHA384 AES256-SHA256 AES256-SHA CAMELLIA256-SHA AES128-GCM-SHA256 AES128-SHA256 AES128-SHA SEED-SHA CAMELLIA128-SHA RC4-SHA RC4-MD5 DES-CBC3-SHA DES-CBC-SHA EXP1024-DES-CBC-SHA EXP1024-RC4-SHA EXP-DES-CBC-SHA EXP-RC4-MD5" + }, + "TLS_extensions": { + "port": "443", + "severity": "INFO", + "finding": "'renegotiation info/#65281' 'application layer protocol negotiation/#16'" + }, + "TLS_session_ticket": { + "port": "443", + "severity": "INFO", + "finding": "no -- no lifetime advertised" + }, + "SSL_sessionID_support": { + "port": "443", + "severity": "INFO", + "finding": "yes" + }, + "sessionresumption_ticket": { + "port": "443", + "severity": "INFO", + "finding": "not supported" + }, + "sessionresumption_ID": { + "port": "443", + "severity": "INFO", + "finding": "supported" + }, + "TLS_timestamp": { + "port": "443", + "severity": "INFO", + "finding": "random" + }, + "cert_numbers": { + "port": "443", + "severity": "INFO", + "finding": "1" + }, + "cert_Algorithm": { + "port": "443", + "severity": "OK", + "finding": "SHA256 with RSA" + }, + "cert_keySize": { + "port": "443", + "severity": "INFO", + "finding": "RSA 2048 bits" + }, + "cert_keyUsage": { + "port": "443", + "severity": "INFO", + "finding": "Digital Signature, Key Encipherment" + }, + "cert_extKeyUsage": { + "port": "443", + "severity": "INFO", + "finding": "TLS Web Server Authentication, TLS Web Client Authentication" + }, + "cert_serialNumber": { + "port": "443", + "severity": "INFO", + "finding": "041DA0D41B11DD0FE6963D44" + }, + "cert_fingerprintSHA1": { + "port": "443", + "severity": "INFO", + "finding": "sha1 4173134AFB85A0486926FADB1944F3EA6BF45B8B" + }, + "cert_fingerprintSHA256": { + "port": "443", + "severity": "INFO", + "finding": "sha256 EFD628F32052E30F5AAAE810ADBF1A22F58FD12F5E770C48937A1F1D893B2808" + }, + "cert": { + "port": "443", + "severity": "INFO", + "finding": "-----BEGIN CERTIFICATE----- MIIGuDCCBaCgAwIBAgIMBB2g1BsR3Q/mlj1EMA0GCSqGSIb3DQEBCwUAMGIxCzAJ BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTgwNgYDVQQDEy9H bG9iYWxTaWduIEV4dGVuZGVkIFZhbGlkYXRpb24gQ0EgLSBTSEEyNTYgLSBHMzAe Fw0yMzAyMTIyMzUxMDZaFw0yNDAzMTUyMzUxMDVaMIG7MR0wGwYDVQQPDBRQcml2 YXRlIE9yZ2FuaXphdGlvbjEXMBUGA1UEBRMOMTIwMC0wMS0wNTk2NjAxEzARBgsr BgEEAYI3PAIBAxMCSlAxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIEwVPc2FrYTEOMAwG A1UEBxMFT3Nha2ExIDAeBgNVBAoTF0RBSUtJTiBJTkRVU1RSSUVTLCBMVEQuMR0w GwYDVQQDExRzc28uYXNhLmRhaWtpbi5jby5qcDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAMN1BYZVfLW+o6H/qTT7U6d2aimCVt/04t0/qaQTe1mQtsJb 3eSsWoMzDpTij2JJUwPNygA1sRx9UG9oJNSEQcbWrsje00G5r25CAkEMrI0hO8S8 5hLyXtGhPga/yIG8D8DEv8oqzIMka2OE4V6GV34D6BgOLGjSFKw7Z4agnwjuqvwt Ox9DD2zr5fhiFkIMliTIkWovJWONBLRZKttuAU7HjDUhcuLc8hhucdOt4HtcrjEy dwMEPS1dKWMkZbGVMiLCX8X77AR34m5do37Akqlowkrs8aaj1g3hXli9PfXlpT/y mZVne00HOXfHfPfUYBsmm6b972Tf4WdoqhH4yFMCAwEAAaOCAxIwggMOMA4GA1Ud DwEB/wQEAwIFoDCBlgYIKwYBBQUHAQEEgYkwgYYwRwYIKwYBBQUHMAKGO2h0dHA6 Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzZXh0ZW5kdmFsc2hhMmcz cjMuY3J0MDsGCCsGAQUFBzABhi9odHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20v Z3NleHRlbmR2YWxzaGEyZzNyMzBVBgNVHSAETjBMMEEGCSsGAQQBoDIBATA0MDIG CCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5 LzAHBgVngQwBATAJBgNVHRMEAjAAMB8GA1UdEQQYMBaCFHNzby5hc2EuZGFpa2lu LmNvLmpwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAfBgNVHSMEGDAW gBTds+dtqC7oxU5uz3TmdTyUFc7oHTAdBgNVHQ4EFgQUHuBS8SatQ+XrzzUNpxEK l3MUc/UwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3AHPZnokbTJZ4oCB9R53m ssYc0FFecRkqjGuAEHrBd3K1AAABhkgKG+EAAAQDAEgwRgIhAMPZwOTdA1JRdWmL yiggEfGI+CiKinYXf5RuV4D2xJJYAiEA97sfmCby0uCkrC4KeQwv7+uZM16nxRMH sbAB1dhUm1oAdgDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYZI Ch66AAAEAwBHMEUCIASn62muO8cMCrKGZeF4NBI7rjTDC4Q6ld1A2QdVE7hvAiEA z4ZJ645mWRoULApzmyajLZNK7FvtXSoORxeM1Wy99j4AdgBIsONr2qZHNA/lagL6 nTDrHFIBy1bdLIHZu7+rOdiEcwAAAYZIChvQAAAEAwBHMEUCIC2tPmPjgBMzew5c UQT55FnFJyRvxwe9A0bFKL3Juh3WAiEA9czPILTEX/+CTDHRuQiFm0kfsOgN41nc lkGaP0tCpa8wDQYJKoZIhvcNAQELBQADggEBAJLL4911GcBvBihK3mN6azUAd6oP 9jswneKa9g8jIUju4/5ABMP7bD1dcLj/9a+UAEkXBr8KcDEjOwzty0AUFY6dsr5Q 7yho6X62NJqN1+OwJfAjt5wsn2rMKjWWaqQtko208cydpxLwsrJ+JbcSZIXoJbSw C+yuy007PHmKlsmeBGe54Q1gx0ipfkhjhos+0FhWhXYjfhWAzvHzU92CuM8zsqXG HsT5L812Vnj+/qvuKU/fEkNJEyCjMcASHnHnx710C5pfA655klt9jAiLNXFGVha7 LGegTs4f3OP7hatHtLUAYWb2Rk0TrHikgd7cxwZ6l52l1paJrIM1rikfdbs= -----END CERTIFICATE-----" + }, + "cert_commonName": { + "port": "443", + "severity": "OK", + "finding": "sso.asa.daikin.co.jp" + }, + "cert_commonName_wo_SNI": { + "port": "443", + "severity": "INFO", + "finding": "sso.asa.daikin.co.jp" + }, + "cert_subjectAltName": { + "port": "443", + "severity": "INFO", + "finding": "sso.asa.daikin.co.jp" + }, + "cert_caIssuers": { + "port": "443", + "severity": "INFO", + "finding": "GlobalSign Extended Validation CA - SHA256 - G3 (GlobalSign nv-sa from BE)" + }, + "cert_trust": { + "port": "443", + "severity": "OK", + "finding": "Ok via SAN (same w/o SNI)" + }, + "cert_chain_of_trust": { + "port": "443", + "severity": "OK", + "finding": "passed." + }, + "cert_certificatePolicies_EV": { + "port": "443", + "severity": "OK", + "finding": "yes" + }, + "cert_eTLS": { + "port": "443", + "severity": "INFO", + "finding": "not present" + }, + "cert_expirationStatus": { + "port": "443", + "severity": "OK", + "finding": "373 >= 60 days" + }, + "cert_notBefore": { + "port": "443", + "severity": "INFO", + "finding": "2023-02-13 00:51" + }, + "cert_notAfter": { + "port": "443", + "severity": "OK", + "finding": "2024-03-16 00:51" + }, + "cert_validityPeriod": { + "port": "443", + "severity": "INFO", + "finding": "No finding" + }, + "certs_countServer": { + "port": "443", + "severity": "INFO", + "finding": "2" + }, + "certs_list_ordering_problem": { + "port": "443", + "severity": "INFO", + "finding": "no" + }, + "cert_crlDistributionPoints": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "cert_ocspURL": { + "port": "443", + "severity": "INFO", + "finding": "http://ocsp2.globalsign.com/gsextendvalsha2g3r3" + }, + "OCSP_stapling": { + "port": "443", + "severity": "LOW", + "finding": "not offered" + }, + "cert_mustStapleExtension": { + "port": "443", + "severity": "INFO", + "finding": "--" + }, + "DNS_CAArecord": { + "port": "443", + "severity": "LOW", + "finding": "--" + }, + "certificate_transparency": { + "port": "443", + "severity": "OK", + "finding": "yes (certificate extension)" + }, + "HTTP_status_code": { + "port": "443", + "severity": "INFO", + "finding": "404 Not Found ('/')" + }, + "HTTP_clock_skew": { + "port": "443", + "severity": "INFO", + "finding": "0 seconds from localtime" + }, + "HSTS": { + "port": "443", + "severity": "LOW", + "finding": "not offered" + }, + "HPKP": { + "port": "443", + "severity": "INFO", + "finding": "No support for HTTP Public Key Pinning" + }, + "banner_server": { + "port": "443", + "severity": "INFO", + "finding": "Apache" + }, + "banner_application": { + "port": "443", + "severity": "INFO", + "finding": "No application banner found" + }, + "cookie_count": { + "port": "443", + "severity": "INFO", + "finding": "1 at '/' (30x detected, better try target URL of 30x)" + }, + "cookie_secure": { + "port": "443", + "severity": "INFO", + "finding": "0/1 at '/' marked as secure" + }, + "cookie_httponly": { + "port": "443", + "severity": "INFO", + "finding": "0/1 at '/' marked as HttpOnly (30x detected, better try target URL of 30x)" + }, + "security_headers": { + "port": "443", + "severity": "MEDIUM", + "finding": "--" + }, + "banner_reverseproxy": { + "port": "443", + "severity": "INFO", + "cwe": "CWE-200", + "finding": "--" + }, + "heartbleed": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-0160", + "cwe": "CWE-119", + "finding": "not vulnerable, no heartbeat extension" + }, + "CCS": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-0224", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "ticketbleed": { + "port": "443", + "severity": "OK", + "cve": "CVE-2016-9244", + "cwe": "CWE-200", + "finding": "no session ticket extension" + }, + "ROBOT": { + "port": "443", + "severity": "OK", + "cve": "CVE-2017-17382 CVE-2017-17427 CVE-2017-17428 CVE-2017-13098 CVE-2017-1000385 CVE-2017-13099 CVE-2016-6883 CVE-2012-5081 CVE-2017-6168", + "cwe": "CWE-203", + "finding": "not vulnerable" + }, + "secure_renego": { + "port": "443", + "severity": "OK", + "cwe": "CWE-310", + "finding": "supported" + }, + "secure_client_renego": { + "port": "443", + "severity": "HIGH", + "cve": "CVE-2011-1473", + "cwe": "CWE-310", + "finding": "VULNERABLE, DoS threat" + }, + "CRIME_TLS": { + "port": "443", + "severity": "OK", + "cve": "CVE-2012-4929", + "cwe": "CWE-310", + "finding": "not vulnerable" + }, + "BREACH": { + "port": "443", + "severity": "MEDIUM", + "cve": "CVE-2013-3587", + "cwe": "CWE-310", + "finding": "potentially VULNERABLE, gzip HTTP compression detected - only supplied '/' tested" + }, + "POODLE_SSL": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-3566", + "cwe": "CWE-310", + "finding": "not vulnerable, no SSLv3" + }, + "fallback_SCSV": { + "port": "443", + "severity": "OK", + "finding": "no protocol below TLS 1.2 offered" + }, + "SWEET32": { + "port": "443", + "severity": "LOW", + "cve": "CVE-2016-2183 CVE-2016-6329", + "cwe": "CWE-327", + "finding": "uses 64 bit block ciphers" + }, + "FREAK": { + "port": "443", + "severity": "CRITICAL", + "cve": "CVE-2015-0204", + "cwe": "CWE-310", + "finding": "VULNERABLE, uses EXPORT RSA ciphers" + }, + "DROWN": { + "port": "443", + "severity": "OK", + "cve": "CVE-2016-0800 CVE-2016-0703", + "cwe": "CWE-310", + "finding": "not vulnerable on this host and port" + }, + "DROWN_hint": { + "port": "443", + "severity": "INFO", + "cve": "CVE-2016-0800 CVE-2016-0703", + "cwe": "CWE-310", + "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://censys.io/ipv4?q=sha256 EFD628F32052E30F5AAAE810ADBF1A22F58FD12F5E770C48937A1F1D893B2808" + }, + "LOGJAM": { + "port": "443", + "severity": "OK", + "cve": "CVE-2015-4000", + "cwe": "CWE-310", + "finding": "not vulnerable, no DH EXPORT ciphers," + }, + "LOGJAM-common_primes": { + "port": "443", + "severity": "OK", + "cve": "CVE-2015-4000", + "cwe": "CWE-310", + "finding": "no DH key with <= TLS 1.2" + }, + "BEAST": { + "port": "443", + "severity": "OK", + "cve": "CVE-2011-3389", + "cwe": "CWE-20", + "finding": "not vulnerable, no SSL3 or TLS1" + }, + "LUCKY13": { + "port": "443", + "severity": "LOW", + "cve": "CVE-2013-0169", + "cwe": "CWE-310", + "finding": "potentially vulnerable, uses TLS CBC ciphers" + }, + "RC4": { + "port": "443", + "severity": "HIGH", + "cve": "CVE-2013-2566 CVE-2015-2808", + "cwe": "CWE-310", + "finding": "VULNERABLE, Detected ciphers: RC4-SHA RC4-MD5 EXP1024-RC4-SHA EXP-RC4-MD5" + }, + "cipher_x9d": { + "port": "443", + "severity": "INFO", + "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" + "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" + }, + "cipher_x3d": { + "port": "443", + "severity": "INFO", + "finding": "x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" + }, + "cipher_x35": { + "port": "443", + "severity": "INFO", + "finding": "x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" + }, + "cipher_x84": { + "port": "443", + "severity": "INFO", + "finding": "x84 CAMELLIA256-SHA RSA Camellia 256 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA" + }, + "cipher_x9c": { + "port": "443", + "severity": "INFO", + "finding": "x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" + }, + "cipher_x3c": { + "port": "443", + "severity": "INFO", + "finding": "x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" + }, + "cipher_x2f": { + "port": "443", + "severity": "INFO", + "finding": "x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" + }, + "cipher_x96": { + "port": "443", + "severity": "INFO", + "finding": "x96 SEED-SHA RSA SEED 128 TLS_RSA_WITH_SEED_CBC_SHA" + }, + "cipher_x41": { + "port": "443", + "severity": "INFO", + "finding": "x41 CAMELLIA128-SHA RSA Camellia 128 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA" + }, + "cipher_x05": { + "port": "443", + "severity": "INFO", + "finding": "x05 RC4-SHA RSA RC4 128 TLS_RSA_WITH_RC4_128_SHA" + }, + "cipher_x04": { + "port": "443", + "severity": "INFO", + "finding": "x04 RC4-MD5 RSA RC4 128 TLS_RSA_WITH_RC4_128_MD5" + }, + "cipher_x0a": { + "port": "443", + "severity": "INFO", + "finding": "x0a DES-CBC3-SHA RSA 3DES 168 TLS_RSA_WITH_3DES_EDE_CBC_SHA" + }, + "cipher_x62": { + "port": "443", + "severity": "INFO", + "finding": "x62 EXP1024-DES-CBC-SHA RSA(1024) DES 56,exp TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA" + }, + "cipher_x09": { + "port": "443", + "severity": "INFO", + "finding": "x09 DES-CBC-SHA RSA DES 56 TLS_RSA_WITH_DES_CBC_SHA" + }, + "cipher_x64": { + "port": "443", + "severity": "INFO", + "finding": "x64 EXP1024-RC4-SHA RSA(1024) RC4 56,exp TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" + }, + "cipher_x08": { + "port": "443", + "severity": "INFO", + "finding": "x08 EXP-DES-CBC-SHA RSA(512) DES 40,exp TLS_RSA_EXPORT_WITH_DES40_CBC_SHA" + }, + "cipher_x03": { + "port": "443", + "severity": "INFO", + "finding": "x03 EXP-RC4-MD5 RSA(512) RC4 40,exp TLS_RSA_EXPORT_WITH_RC4_40_MD5" + }, + "clientsimulation-android_442": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-android_500": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA" + }, + "clientsimulation-android_60": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA" + }, + "clientsimulation-android_70": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-android_81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-android_90": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-android_X": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-chrome_74_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-chrome_79_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-firefox_66_win81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA" + }, + "clientsimulation-firefox_71_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA" + }, + "clientsimulation-ie_6_xp": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_8_win7": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_8_xp": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-ie_11_win7": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-ie_11_win81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-ie_11_winphone81": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA256" + }, + "clientsimulation-ie_11_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-edge_15_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-edge_17_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-opera_66_win10": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-safari_9_ios9": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-safari_9_osx1011": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-safari_10_osx1012": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-safari_121_ios_122": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-safari_130_osx_10146": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-apple_ats_9_ios9": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-java_6u45": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-java_7u25": { + "port": "443", + "severity": "INFO", + "finding": "No connection" + }, + "clientsimulation-java_8u161": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-java1102": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-java1201": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-openssl_102e": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-openssl_110l": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-openssl_111d": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-GCM-SHA384" + }, + "clientsimulation-thunderbird_68_3_1": { + "port": "443", + "severity": "INFO", + "finding": "TLSv1.2 AES256-SHA" + }, + "scanTime": { + "port": "443", + "severity": "INFO", + "finding": "321" + } + } +} \ No newline at end of file diff --git a/utils/configuration.py b/utils/configuration.py index af50977..3c73acf 100644 --- a/utils/configuration.py +++ b/utils/configuration.py @@ -61,4 +61,4 @@ def get_aliases(): aliases = { "poodle" : ["tlspoodle","sslpoodle"] } - return aliases \ No newline at end of file + return aliases diff --git a/utils/database.py b/utils/database.py new file mode 100644 index 0000000..1653f17 --- /dev/null +++ b/utils/database.py @@ -0,0 +1,22 @@ +def get_version_name_for_database(version_name: str): + """This function prepares the version_name to be usable in the database as art of a table's name""" + version_name = version_name if "Unnamed" not in version_name else "" + version_name = version_name.strip().title().replace(" ", "").replace("-", "").replace("/", "_").replace("#", "") \ + .strip(".") + return version_name + + +def get_standard_name_for_database(standard_name): + """ + This function prepares the standard_name to be usable in the database as part of a table's name + :param: standard_name: The standard_name to sanitize + :type standard_name: str + """ + if " " in standard_name: + tokens = standard_name.split(" ") + if "+" in tokens[1]: + # The "added" entries are already present in the dict + standard_name = tokens[0] + elif len(tokens) > 2 and "/" in tokens[-1]: + standard_name = tokens[0] + tokens[-1].replace("/", "_") + return standard_name.strip(")") From 6e5f20c15d61705426a167d1bfa24f974573fa7d Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 14 Mar 2023 11:33:50 +0100 Subject: [PATCH 054/209] Improved readability of compliance_one and many and fixed the latter. Also made testssl parsing more consistent. --- modules/compliance/compliance_base.py | 38 +- modules/compliance/compliance_many.py | 15 +- modules/compliance/compliance_one.py | 13 +- testssl_dump.json | 767 ++++++++++++++------------ 4 files changed, 446 insertions(+), 387 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index b733123..7b08ea4 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -6,6 +6,13 @@ from utils.validation import Validator +def convert_signature_algorithm(sig_alg: str) -> str: + """ + This function is needed to convert the input from testssl to make it compatible with the requirements database + """ + pass + + class Compliance: def __init__(self): self._input_dict = {} @@ -33,10 +40,10 @@ def evaluation_to_use(self, evaluations, security: bool = True): security_mapping = "security" if security else "legacy" if not evaluations: raise IndexError("Evaluations list is empty") - first_value = self.evaluations_mapping.get(security_mapping, {}).get(evaluations[0].replace("°", "")) + first_value = self.evaluations_mapping.get(security_mapping, {}).get(evaluations[0].replace("°", ""), 4) best = 0 for i, el in enumerate(evaluations[1:]): - evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(el.replace("°", "")) + evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(el.replace("°", ""), 4) if first_value > evaluation_value: best = i + 1 # if they have the same value first wins @@ -126,7 +133,7 @@ def prepare_testssl_output(self, test_ssl_output): if new_version_name[-2] != '.': new_version_name += ".0" protocol_dict[new_version_name] = "not" not in actual_dict["finding"] - elif field.startswith("cipher_x"): + elif field.startswith("cipher") and "x" in field: if not self._user_configuration.get("CipherSuite"): self._user_configuration["CipherSuite"] = set() value = actual_dict.get("finding", "") @@ -162,11 +169,19 @@ def prepare_testssl_output(self, test_ssl_output): elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = set() - self._user_configuration["KeyLengths"].add(tuple(actual_dict["finding"].split(" ")[:2])) - elif field == "PFS_ECDHE_curves": + self._user_configuration["KeyLengths"].update(actual_dict["finding"].split(" ")[:2]) + elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ else actual_dict["finding"] self._user_configuration["Groups"] = values + elif field[-8:] == "sig_algs": + if not self._user_configuration.get("Signature"): + self._user_configuration["Signature"] = set() + finding = actual_dict["finding"] + values = finding.split(" ") if " " in finding else [finding] + values = [sig for sig in values] + self._user_configuration["Signature"].update(values) + elif field in self.misc_fields: if not self._user_configuration.get("Misc"): self._user_configuration["Misc"] = {} @@ -189,3 +204,16 @@ def update_result(self, sheet, name, evaluation, is_enabled): action = "should be disabled" if information_level: self._output_dict[sheet][name] = f"{information_level}: {name} {action}" + + def is_enabled(self, config_field, name, entry): + field_value = self._user_configuration[config_field] + enabled = False + if isinstance(field_value, dict): + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): + enabled = entry[:2] in field_value + elif isinstance(field_value, list) or isinstance(field_value, set): + enabled = name in field_value + return enabled diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index 6e21bee..8d275db 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -30,8 +30,7 @@ def _worker(self, sheets_to_check): tables = [] actual_evaluation = "" for sheet in sheets_to_check: - config_field = self._database_instance.sheet_mapping.get(sheet) - counter = 0 + counter = 1 for entry in entries[sheet]: name = entry[name_index] evaluation = entry[evaluation_index] @@ -39,15 +38,9 @@ def _worker(self, sheets_to_check): evaluations = [actual_evaluation, evaluation] best_evaluation = self.evaluation_to_use(evaluations) actual_evaluation = evaluations[best_evaluation] - if config_field and counter % len(sheets_to_check[sheet]) == 0: - field_value = self._user_configuration[config_field] - enabled = False - if isinstance(field_value, dict): - enabled = field_value.get(name, None) - if enabled is None: - enabled = True if "all" in field_value else False - elif isinstance(field_value, list): - enabled = name in field_value + if sheet and counter == len(sheets_to_check[sheet]): + counter = 0 + enabled = self.is_enabled(sheet, name, entry) self.update_result(sheet, name, actual_evaluation, enabled) actual_evaluation = "" counter += 1 diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py index c2e86f2..a468777 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compliance_one.py @@ -1,8 +1,6 @@ from modules.compliance.compliance_base import Compliance -kwargs = {"sheets_to_check": {"Extension":{"ANSSI":"", "BSI":""}, "KeyLengths":{"ANSSI":""}, "Misc":{"ANSSI":""}}, "test_ssl":True} - class ComplianceOne(Compliance): def _worker(self, sheets_to_check): """ @@ -31,14 +29,5 @@ def _worker(self, sheets_to_check): if config_field: name = entry[name_index] evaluation = entry[evaluation_index] - field_value = self._user_configuration[config_field] - enabled = False - if isinstance(field_value, dict): - enabled = field_value.get(name, None) - if enabled is None: - enabled = True if "all" in field_value else False - elif field_value and isinstance(field_value, list) and isinstance(field_value[0], tuple): - enabled = entry[:2] in field_value - elif isinstance(field_value, list): - enabled = name in field_value + enabled = self.is_enabled(config_field, name, entry) self.update_result(sheet, name, evaluation, enabled) diff --git a/testssl_dump.json b/testssl_dump.json index 0fc34df..de68e0b 100644 --- a/testssl_dump.json +++ b/testssl_dump.json @@ -1,5 +1,5 @@ { - "172.67.71.17": { + "44.236.48.31": { "service": { "port": "443", "severity": "INFO", @@ -22,13 +22,13 @@ }, "TLS1": { "port": "443", - "severity": "INFO", - "finding": "not offered" + "severity": "LOW", + "finding": "offered (deprecated)" }, "TLS1_1": { "port": "443", - "severity": "INFO", - "finding": "is not offered" + "severity": "LOW", + "finding": "offered (deprecated)" }, "TLS1_2": { "port": "443", @@ -43,7 +43,7 @@ "NPN": { "port": "443", "severity": "INFO", - "finding": "not offered" + "finding": "offered with h2, http/1.1 (advertised)" }, "ALPN_HTTP2": { "port": "443", @@ -85,376 +85,476 @@ "cwe": "CWE-310", "finding": "not offered" }, - "cipherlist_AVERAGE": { + "cipherlist_OBSOLETED": { "port": "443", "severity": "LOW", "cwe": "CWE-310", "finding": "offered" }, - "cipherlist_STRONG": { + "cipherlist_STRONG_NOFS": { "port": "443", "severity": "OK", "finding": "offered" }, - "PFS": { + "cipherlist_STRONG_FS": { "port": "443", "severity": "OK", "finding": "offered" }, - "PFS_ciphers": { + "cipher_order-tls1": { "port": "443", - "severity": "INFO", - "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDHE-RSA-CHACHA20-POLY1305-OLD ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-ECDSA-AES128-SHA256 ECDHE-RSA-AES128-SHA ECDHE-ECDSA-AES128-SHA" + "severity": "OK", + "finding": "server" }, - "PFS_ECDHE_curves": { + "cipher-tls1_xc013": { "port": "443", - "severity": "OK", - "finding": "prime256v1 secp384r1 secp521r1 X25519" + "severity": "LOW", + "finding": "TLSv1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" }, - "cipher_order": { + "cipher-tls1_xc014": { "port": "443", - "severity": "OK", - "finding": "server -- TLS 1.3 client determined" + "severity": "LOW", + "finding": "TLSv1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" }, - "protocol_negotiated": { + "cipher-tls1_x2f": { "port": "443", - "severity": "OK", - "finding": "Default protocol TLS1.3" + "severity": "LOW", + "finding": "TLSv1 x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" }, - "cipher_negotiated": { + "cipher-tls1_x35": { "port": "443", - "severity": "OK", - "finding": "TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519)" + "severity": "LOW", + "finding": "TLSv1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" }, - "cipherorder_TLSv1_2": { + "cipherorder_TLSv1": { "port": "443", "severity": "INFO", - "finding": "ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-CHACHA20-POLY1305-OLD ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA AES128-GCM-SHA256 AES128-SHA ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA AES256-GCM-SHA384 AES256-SHA ECDHE-RSA-AES128-SHA256 AES128-SHA256 ECDHE-RSA-AES256-SHA384 AES256-SHA256" + "finding": "ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-SHA AES256-SHA" }, - "TLS_extensions": { + "cipher_order-tls1_1": { "port": "443", - "severity": "INFO", - "finding": "'server name/#0' 'renegotiation info/#65281' 'EC point formats/#11' 'session ticket/#35' 'status request/#5' 'next protocol/#13172' 'key share/#51' 'supported versions/#43' 'extended master secret/#23' 'application layer protocol negotiation/#16'" + "severity": "OK", + "finding": "server" }, - "TLS_session_ticket": { + "cipher-tls1_1_xc013": { "port": "443", - "severity": "INFO", - "finding": "valid for 64800 seconds only (": { + "cipher-tls1_2_xc02f": { "port": "443", "severity": "OK", - "finding": "SHA256 with RSA" + "finding": "TLSv1.2 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }, - "cert_keySize ": { + "cipher-tls1_2_xc030": { "port": "443", - "severity": "INFO", - "finding": "RSA 2048 bits" + "severity": "OK", + "finding": "TLSv1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" }, - "cert_keyUsage ": { + "cipher-tls1_2_xcca8": { "port": "443", - "severity": "INFO", - "finding": "Digital Signature, Key Encipherment" + "severity": "OK", + "finding": "TLSv1.2 xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" }, - "cert_extKeyUsage ": { + "cipher-tls1_2_xc027": { "port": "443", - "severity": "INFO", - "finding": "TLS Web Server Authentication, TLS Web Client Authentication" + "severity": "LOW", + "finding": "TLSv1.2 xc027 ECDHE-RSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" }, - "cert_serialNumber ": { + "cipher-tls1_2_xc013": { "port": "443", - "severity": "INFO", - "finding": "039CA4946FFEA7A31B98DE488D3DF8AB" + "severity": "LOW", + "finding": "TLSv1.2 xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" }, - "cert_fingerprintSHA1 ": { + "cipher-tls1_2_xc028": { "port": "443", - "severity": "INFO", - "finding": "sha1 48835A14D3749891152DD07B06B0FC0EF369108B" + "severity": "LOW", + "finding": "TLSv1.2 xc028 ECDHE-RSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" }, - "cert_fingerprintSHA256 ": { + "cipher-tls1_2_xc014": { "port": "443", - "severity": "INFO", - "finding": "sha256 E06E2B75257BDD4B1DA9FBF332DAF67C76AF1395F06D47B9BF9BC0BA487CF7CC" + "severity": "LOW", + "finding": "TLSv1.2 xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" }, - "cert ": { + "cipher-tls1_2_x9c": { "port": "443", - "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE----- MIIGyjCCBbKgAwIBAgIQA5yklG/+p6MbmN5IjT34qzANBgkqhkiG9w0BAQsFADBK MQswCQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UE AxMXQ2xvdWRmbGFyZSBJbmMgUlNBIENBLTIwHhcNMjIwNjE3MDAwMDAwWhcNMjMw NjE3MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5j LjEeMBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAtZqriCZo8uz5cy8ZksrPrL6wJIFNBGzsGd6lehcn 8bFF+jIIxcAtxB3Z1rNjA1WwQDySUAKhzcihWrIZmECrT/lKCNwMCzH/MzH7v8Dn r+PCrJZ1PgnHnTnrGO/Aa+8OjV/WmA63WXDTe5A85dqsWlrLm2sDqkM5Fj0aH+z+ 77I//11hgdpdQfUbJG2jJybv127wD5oqClM4TygprsXpELr7Mpuepzbd6KDnAv1b 1kY2buRNuyjpjacYG+KBIr81Sf+ovcetpyVGPhaZpKNVhCRkMTnCScSGGw4kZ+Gq a5pPxwaTju54MpvRtYQj7Bhtj87W2s6QHabe3gHunESC2QIDAQABo4IDfzCCA3sw HwYDVR0jBBgwFoAUGKka/LJFScFvMDQIK9mHnLAlV3owHQYDVR0OBBYEFIBMtmcR 6BU7WmD4BsF69tioHyeTMEQGA1UdEQQ9MDuCD3BzaWNvYWN0aXZhLmNvbYIRKi5w c2ljb2FjdGl2YS5jb22CFXNuaS5jbG91ZGZsYXJlc3NsLmNvbTAOBgNVHQ8BAf8E BAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHsGA1UdHwR0MHIw N6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9DbG91ZGZsYXJlSW5jUlNB Q0EtMi5jcmwwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9DbG91ZGZs YXJlSW5jUlNBQ0EtMi5jcmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEF BQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMHYGCCsGAQUFBwEBBGow aDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEAGCCsGAQUF BzAChjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY1JT QUNBLTIuY3J0MAwGA1UdEwEB/wQCMAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsB aQB1AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAABgW8+E2EAAAQD AEYwRAIgE1CZuC75FvsPgaCxkwecCo5EV8BRM+Kxx5hSas2HBzoCIFvOB4g75l3r n7Yp8az+w9IYQE7Z0RVsKLHOoUeSbg4oAHcANc8ZG7+xbFe/D61MbULLu7YnICZR 6j/hKu+oA8M71kwAAAGBbz4TbgAABAMASDBGAiEAs1u1up6Y+QfG8pI1xHxJJ1Z+ +Q05KiquZx3fC6SoENMCIQD4v7ZP/lA9hoyZ4QifZPRl+Lu+zNcVSZ84my8FwJoQ mAB3ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55NqWaAAABgW8+E4oAAAQD AEgwRgIhAPXuTB3uW9P+WvQlBC9Yt+k+hgzUqhEZ0d1ujg+nBAqIAiEAqweYa87n pW3grltuUxzKJ0v7ihreRIIDC+Jhywz+/kowDQYJKoZIhvcNAQELBQADggEBABCa SXx57NiKUThWXVY94YKOn1QrOIoXCwcyAV53w2d6yS5+nl1anr1ZOiqhCO37rTDf gOUixwKNWHrDegLmRvL51IljNJKlt+/pS/d30rPDAbh0YqqU7dQmpbafW6A3Fblm KBojmWWdRua4N1Z0mYmDZEvEZcm9BLB0a0kvYh9Z6wOaHjLsGk0j8MaVJAZNGFoB hS6QDc5ksDz89oYB8gR8W7EULlcEuKHOKx9ypjZR8fa64+BebRp1YctZNyLD+acv 7CrJHZVdeUY3L8UYKS2OAZu96HAk8Kv6BzQGfIoAQtpmvH5gIpy8I5xF00D6voGY 5ru+ZswSOL6EfE4P6Ns= -----END CERTIFICATE-----" + "severity": "OK", + "finding": "TLSv1.2 x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" }, - "cert_commonName ": { + "cipher-tls1_2_x9d": { "port": "443", "severity": "OK", - "finding": "sni.cloudflaressl.com" + "finding": "TLSv1.2 x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" }, - "cert_commonName_wo_SNI ": { + "cipher-tls1_2_x3c": { "port": "443", - "severity": "INFO", - "finding": "request w/o SNI didn't succeed" + "severity": "LOW", + "finding": "TLSv1.2 x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" + }, + "cipher-tls1_2_x3d": { + "port": "443", + "severity": "LOW", + "finding": "TLSv1.2 x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" + }, + "cipher-tls1_2_x2f": { + "port": "443", + "severity": "LOW", + "finding": "TLSv1.2 x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" }, - "cert_subjectAltName ": { + "cipher-tls1_2_x35": { + "port": "443", + "severity": "LOW", + "finding": "TLSv1.2 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" + }, + "cipherorder_TLSv1_2": { "port": "443", "severity": "INFO", - "finding": "psicoactiva.com *.psicoactiva.com sni.cloudflaressl.com" + "finding": "ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA AES128-GCM-SHA256 AES256-GCM-SHA384 AES128-SHA256 AES256-SHA256 AES128-SHA AES256-SHA" }, - "cert_caIssuers ": { + "prioritize_chacha_TLSv1_2": { "port": "443", "severity": "INFO", - "finding": "Cloudflare Inc RSA CA-2 (Cloudflare, Inc. from US)" + "finding": "false" }, - "cert_trust ": { + "cipher_order-tls1_3": { "port": "443", "severity": "OK", - "finding": "Ok via SAN (SNI mandatory)" + "finding": "server" }, - "cert_chain_of_trust ": { + "cipher-tls1_3_x1302": { "port": "443", "severity": "OK", - "finding": "passed." + "finding": "TLSv1.3 x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384" + }, + "cipher-tls1_3_x1303": { + "port": "443", + "severity": "OK", + "finding": "TLSv1.3 x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256" }, - "cert_certificatePolicies_EV ": { + "cipher-tls1_3_x1301": { + "port": "443", + "severity": "OK", + "finding": "TLSv1.3 x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256" + }, + "cipherorder_TLSv1_3": { "port": "443", "severity": "INFO", - "finding": "no" + "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256" }, - "cert_eTLS ": { + "prioritize_chacha_TLSv1_3": { "port": "443", "severity": "INFO", - "finding": "not present" + "finding": "false" + }, + "cipher_order": { + "port": "443", + "severity": "OK", + "finding": "server" }, - "cert_expirationStatus ": { + "FS": { "port": "443", "severity": "OK", - "finding": "100 >= 60 days" + "finding": "offered" }, - "cert_notBefore ": { + "FS_ciphers": { "port": "443", "severity": "INFO", - "finding": "2022-06-17 02:00" + "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA" }, - "cert_notAfter ": { + "FS_ECDHE_curves": { "port": "443", "severity": "OK", - "finding": "2023-06-18 01:59" + "finding": "prime256v1 secp384r1 secp521r1 X25519 X448" }, - "cert_validityPeriod ": { + "FS_TLS12_sig_algs": { + "port": "443", + "severity": "LOW", + "finding": "RSA-PSS+SHA256 RSA-PSS+SHA384 RSA-PSS+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224 RSA+SHA1" + }, + "FS_TLS13_sig_algs": { "port": "443", "severity": "INFO", - "finding": "No finding" + "finding": "RSA-PSS+SHA256 RSA-PSS+SHA384 RSA-PSS+SHA512" }, - "certs_countServer ": { + "TLS_extensions": { "port": "443", "severity": "INFO", - "finding": "2" + "finding": "'renegotiation info/#65281' 'server name/#0' 'EC point formats/#11' 'next protocol/#13172' 'supported versions/#43' 'key share/#51' 'supported_groups/#10' 'max fragment length/#1' 'application layer protocol negotiation/#16' 'encrypt-then-mac/#22' 'extended master secret/#23'" }, - "certs_list_ordering_problem ": { + "TLS_session_ticket": { "port": "443", "severity": "INFO", - "finding": "no" + "finding": "no -- no lifetime advertised" }, - "cert_crlDistributionPoints ": { + "SSL_sessionID_support": { "port": "443", "severity": "INFO", - "finding": "--" + "finding": "yes" }, - "cert_ocspURL ": { + "sessionresumption_ticket": { "port": "443", "severity": "INFO", - "finding": "http://ocsp.digicert.com" + "finding": "not supported" }, - "OCSP_stapling ": { + "sessionresumption_ID": { "port": "443", - "severity": "OK", - "finding": "offered" + "severity": "INFO", + "finding": "not supported" }, - "cert_ocspRevoked ": { + "TLS_timestamp": { "port": "443", - "severity": "OK", - "finding": "not revoked" + "severity": "INFO", + "finding": "random" }, - "cert_mustStapleExtension ": { + "certificate_compression": { "port": "443", "severity": "INFO", - "finding": "--" + "finding": "none" }, - "DNS_CAArecord ": { + "clientAuth": { "port": "443", - "severity": "LOW", - "finding": "--" + "severity": "INFO", + "finding": "none" }, - "certificate_transparency ": { + "cert_numbers": { "port": "443", - "severity": "OK", - "finding": "yes (certificate extension)" + "severity": "INFO", + "finding": "1" }, - "cert_signatureAlgorithm ": { + "cert_signatureAlgorithm": { "port": "443", "severity": "OK", - "finding": "ECDSA with SHA256" + "finding": "SHA256 with RSA" }, - "cert_keySize ": { + "cert_keySize": { "port": "443", - "severity": "OK", - "finding": "EC 256 bits" + "severity": "INFO", + "finding": "RSA 2048 bits (exponent is 65537)" }, - "cert_keyUsage ": { + "cert_keyUsage": { "port": "443", "severity": "INFO", - "finding": "Digital Signature" + "finding": "Digital Signature, Key Encipherment" }, - "cert_extKeyUsage ": { + "cert_extKeyUsage": { "port": "443", "severity": "INFO", "finding": "TLS Web Server Authentication, TLS Web Client Authentication" }, - "cert_serialNumber ": { + "cert_serialNumber": { "port": "443", "severity": "INFO", - "finding": "0AB23C8E316FFECA015D9077301B9F21" + "finding": "037BE38EA85C55524F3257B593544F08832A" }, - "cert_fingerprintSHA1 ": { + "cert_serialNumberLen": { "port": "443", "severity": "INFO", - "finding": "sha1 55727091D5A9A96C1539F96278EDC292EB5FB721" + "finding": "18" }, - "cert_fingerprintSHA256 ": { + "cert_fingerprintSHA1": { "port": "443", "severity": "INFO", - "finding": "sha256 4466AC40E06F9C629B554E07331190A4645EEBCCDF1041DB7A2FD0C48A45CAFE" + "finding": "B5736D5276797C0BBF6AB47857C9263E9C05923F" }, - "cert ": { + "cert_fingerprintSHA256": { "port": "443", "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE----- MIIFPDCCBOGgAwIBAgIQCrI8jjFv/soBXZB3MBufITAKBggqhkjOPQQDAjBKMQsw CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjIwNjE3MDAwMDAwWhcNMjMwNjE3 MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI zj0DAQcDQgAEWEiG6XL/XoNpr+81AhGCztrZYowNJkJLNdnLZGLMlpfOEW9sy7VQ X/SJKxIkdf+yfRBCF1FIbBFViEfqQv80UKOCA3wwggN4MB8GA1UdIwQYMBaAFKXO N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBTNi6Y4XRGMGfnJYcgoqJZJjjjl kDBEBgNVHREEPTA7gg9wc2ljb2FjdGl2YS5jb22CESoucHNpY29hY3RpdmEuY29t ghVzbmkuY2xvdWRmbGFyZXNzbC5jb20wDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQW MBQGCCsGAQUFBwMBBggrBgEFBQcDAjB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8v Y3JsMy5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3JsMDegNaAz hjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMu Y3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93 d3cuZGlnaWNlcnQuY29tL0NQUzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGG GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2Nh Y2VydHMuZGlnaWNlcnQuY29tL0Nsb3VkZmxhcmVJbmNFQ0NDQS0zLmNydDAMBgNV HRMBAf8EAjAAMIIBfAYKKwYBBAHWeQIEAgSCAWwEggFoAWYAdQDoPtDaPvUGNTLn Vyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYFvPhYZAAAEAwBGMEQCIAeWyxBVYxc5 vQxtW10e80d7fGmfcH25IyTaYjpSazydAiBNwMCxQYgQWGmbGBetDv1Z+j2JmQZZ z9pLgQQGn7WRiAB1ADXPGRu/sWxXvw+tTG1Cy7u2JyAmUeo/4SrvqAPDO9ZMAAAB gW8+FjkAAAQDAEYwRAIgUcabVd+wLyEuNb/Be6pDQ4E0+3gBMEFINUifwiWCNcoC IFbgfewsz/79X5K53aJhpVjYebSyrdr4mnC4EFbQngiaAHYAtz77JN+cTbp18jnF ulj0bF38Qs96nzXEnh0JgSXttJkAAAGBbz4WPgAABAMARzBFAiAJsYIQcKWIPb4X oYIITqYNyjxNWdMstWXf9khypukdYwIhAJyXW2CYYTbMaDSAbOmoNddnCO1g/tcD r5+3Z4spBIPHMAoGCCqGSM49BAMCA0kAMEYCIQDwQmYZNOf1DyjwKV6iFQ7TAEmz P17FV7IrCneJa1ZJegIhALBm2F228pma/EzfBOZ+plCCW6QDjJ+epyCCAZ+Iu6qN -----END CERTIFICATE-----" + "finding": "A4AFB51D0E4754D22E3C2746FDFB12C0479E04851AB01E26B0782411BE93C494" }, - "cert_commonName ": { + "cert": { "port": "443", - "severity": "OK", - "finding": "sni.cloudflaressl.com" + "severity": "INFO", + "finding": "-----BEGIN CERTIFICATE-----\nMIIFHDCCBASgAwIBAgISA3vjjqhcVVJPMle1k1RPCIMqMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJSMzAeFw0yMzAyMjAwNjMyMjlaFw0yMzA1MjEwNjMyMjhaMBYxFDASBgNVBAMTC21vemlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyUrg3XvxqqCCWhNh7gBNMIAUicqbeYykKLElezIxCN5ECVOTg9Sarv41jsqSDubuEDtAP7pynHdVS2Wku8MU0LrkIBpGcIjKtwDJgD3X8VZitfgHbLf5uN38MbXfNJbmAfv0PFP3r1Yaltt0KYfkzImmj3vWT19lG2BXkNh+6VCSNhDVTsOAA1+yxuJJ7EwErAL/tA/J8llPGTqJ0tgScMJ6Be7ZRWAe5YOBQNycfjHkosMg44esE3ac4B65Rj4eDOldhbm6b04oz/2AJsx363Is0hI24oG0DSHlp7kHBVreEhxLDCFUjo6h+/oO5orRaTUgCD0yQCkieYGKmuTOnwIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS5sQu8XaFj/vyeFzJi7RTBnVXW0zAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAWBgNVHREEDzANggttb3ppbGxhLm9yZzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhm29Bl8AAAQDAEcwRQIgPrPtPLcpyd+w5N0ntsuPr9wswow3JN3+/5gsOIqcxe4CIQCCu3M6J/qBWse4GMuqVrlrQdjz9fd6+g4Ar+kS7UpyogB2AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutSAAABhm29CC0AAAQDAEcwRQIhAJ+12rPURQCkBI3tXz+NDexkXBr7+y6kXBQDdWcL8oMdAiADEIXTd11JwPa6Rwwxis2++LqwB38eci587dHpbzCCzjANBgkqhkiG9w0BAQsFAAOCAQEAnWA3G2F+zwjt4agdNXklZhCtr35yUgeLZlm4qb+p4ZLHcEdJ8czejsgLWoUoc1WkKmzi+c6zSUk5bTdX0piayV4VNARaA69XnhJ3mwIzOAhL8jP76bqPvVTHaGzm43Yh2gQiWqHfQZBV22bJCopkfmZPdil+gpiD2z2YtYw0cs1AZc4CwwG7GMZxDduurvGAUwfgdkrLcc8Dx3dZJhQi/ngIJ+BU4FWtvlnKoaalS+9LjvDZCPuMXqUxF5ofHPrruYBSmP9xoDrNL7Fs7YC7ML5PsgKXZ5uTXwSXwx4Bf6M6lkaiXCdod8/KlQmAik9hn4laYC5spJgufVj6sHKFNA==\n-----END CERTIFICATE-----" }, - "cert_commonName_wo_SNI ": { + "cert_commonName": { "port": "443", - "severity": "INFO", - "finding": "request w/o SNI didn't succeed, usual for EC certificates" + "severity": "OK", + "finding": "mozilla.org" }, - "cert_subjectAltName ": { + "cert_commonName_wo_SNI": { "port": "443", "severity": "INFO", - "finding": "psicoactiva.com *.psicoactiva.com sni.cloudflaressl.com" + "finding": "Kubernetes Ingress Controller Fake Certificate" }, - "cert_caIssuers ": { + "cert_subjectAltName": { "port": "443", "severity": "INFO", - "finding": "Cloudflare Inc ECC CA-3 (Cloudflare, Inc. from US)" + "finding": "mozilla.org" }, - "cert_trust ": { + "cert_trust": { "port": "443", "severity": "OK", - "finding": "Ok via SAN (SNI mandatory)" + "finding": "Ok via SAN and CN (SNI mandatory)" }, - "cert_chain_of_trust ": { + "cert_chain_of_trust": { "port": "443", "severity": "OK", "finding": "passed." }, - "cert_certificatePolicies_EV ": { + "cert_certificatePolicies_EV": { "port": "443", "severity": "INFO", "finding": "no" }, - "cert_eTLS ": { + "cert_expirationStatus": { + "port": "443", + "severity": "OK", + "finding": "67 >= 30 days" + }, + "cert_notBefore": { "port": "443", "severity": "INFO", - "finding": "not present" + "finding": "2023-02-20 06:32" }, - "cert_expirationStatus ": { + "cert_notAfter": { "port": "443", "severity": "OK", - "finding": "100 >= 60 days" + "finding": "2023-05-21 06:32" }, - "cert_notBefore ": { + "cert_extlifeSpan": { + "port": "443", + "severity": "OK", + "finding": "certificate has no extended life time according to browser forum" + }, + "cert_eTLS": { "port": "443", "severity": "INFO", - "finding": "2022-06-17 02:00" + "finding": "not present" }, - "cert_notAfter ": { + "cert_crlDistributionPoints": { "port": "443", - "severity": "OK", - "finding": "2023-06-18 01:59" + "severity": "INFO", + "finding": "--" + }, + "cert_ocspURL": { + "port": "443", + "severity": "INFO", + "finding": "http://r3.o.lencr.org" + }, + "OCSP_stapling": { + "port": "443", + "severity": "LOW", + "finding": "not offered" }, - "cert_validityPeriod ": { + "cert_mustStapleExtension": { "port": "443", "severity": "INFO", - "finding": "No finding" + "finding": "--" + }, + "DNS_CAArecord": { + "port": "443", + "severity": "OK", + "finding": "iodef=mailto:foxsec+caaiodef@mozilla.com, issue=amazon.com, issue=amazonaws.com, issue=amazontrust.com, issue=awstrust.com, issue=comodoca.com, issue=digicert.com, issue=letsencrypt.org, issue=pki.goog, issue=sectigo.com" + }, + "certificate_transparency": { + "port": "443", + "severity": "OK", + "finding": "yes (certificate extension)" }, - "certs_countServer ": { + "certs_countServer": { "port": "443", "severity": "INFO", - "finding": "2" + "finding": "3" }, - "certs_list_ordering_problem ": { + "certs_list_ordering_problem": { "port": "443", "severity": "INFO", "finding": "no" }, - "cert_crlDistributionPoints ": { + "cert_caIssuers": { "port": "443", "severity": "INFO", - "finding": "--" + "finding": "R3 (Let's Encrypt from US)" }, - "cert_ocspURL ": { + "intermediate_cert <#1>": { "port": "443", "severity": "INFO", - "finding": "http://ocsp.digicert.com" + "finding": "-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAwWhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cPR5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdxsxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8ZutmNHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxgZ3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6WPTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wlikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQzCkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BImlJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1OyK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90IdshCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6ZvMldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqXnLRbwHOoq7hHwg==\n-----END CERTIFICATE-----" }, - "OCSP_stapling ": { + "intermediate_cert_fingerprintSHA256 <#1>": { + "port": "443", + "severity": "INFO", + "finding": "67ADD1166B020AE61B8F5FC96813C04C2AA589960796865572A3C7E737613DFD" + }, + "intermediate_cert_notBefore <#1>": { + "port": "443", + "severity": "INFO", + "finding": "2020-09-04 00:00" + }, + "intermediate_cert_notAfter <#1>": { "port": "443", "severity": "OK", - "finding": "offered" + "finding": "2025-09-15 16:00" }, - "cert_ocspRevoked ": { + "intermediate_cert_expiration <#1>": { "port": "443", "severity": "OK", - "finding": "not revoked" + "finding": "ok > 40 days" }, - "cert_mustStapleExtension ": { + "intermediate_cert_chain <#1>": { "port": "443", "severity": "INFO", - "finding": "--" + "finding": "R3 <-- ISRG Root X1" }, - "DNS_CAArecord ": { + "intermediate_cert <#2>": { "port": "443", - "severity": "LOW", - "finding": "--" + "severity": "INFO", + "finding": "-----BEGIN CERTIFICATE-----\nMIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1owTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XCov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpLwYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+DLtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5ysR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZXmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBcSLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2qlPRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TNDTwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26ZtuMA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuGWCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9Ohe8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFCDfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n-----END CERTIFICATE-----" + }, + "intermediate_cert_fingerprintSHA256 <#2>": { + "port": "443", + "severity": "INFO", + "finding": "6D99FB265EB1C5B3744765FCBC648F3CD8E1BFFAFDC4C2F99B9D47CF7FF1C24F" + }, + "intermediate_cert_notBefore <#2>": { + "port": "443", + "severity": "INFO", + "finding": "2021-01-20 19:14" }, - "certificate_transparency ": { + "intermediate_cert_notAfter <#2>": { "port": "443", "severity": "OK", - "finding": "yes (certificate extension)" + "finding": "2024-09-30 18:14" + }, + "intermediate_cert_expiration <#2>": { + "port": "443", + "severity": "OK", + "finding": "ok > 40 days" + }, + "intermediate_cert_chain <#2>": { + "port": "443", + "severity": "INFO", + "finding": "ISRG Root X1 <-- DST Root CA X3" + }, + "intermediate_cert_badOCSP": { + "port": "443", + "severity": "OK", + "finding": "intermediate certificate(s) is/are ok" }, "HTTP_status_code": { "port": "443", @@ -466,10 +566,15 @@ "severity": "INFO", "finding": "0 seconds from localtime" }, + "HTTP_headerTime": { + "port": "443", + "severity": "INFO", + "finding": "1678787795" + }, "HSTS_time": { "port": "443", - "severity": "OK", - "finding": "180 days (=15552000 seconds) > 15465600 seconds" + "severity": "MEDIUM", + "finding": "max-age too short. 0 days (=60 seconds) < 15552000 seconds" }, "HSTS_subdomains": { "port": "443", @@ -478,8 +583,8 @@ }, "HSTS_preload": { "port": "443", - "severity": "OK", - "finding": "domain IS marked for preloading" + "severity": "INFO", + "finding": "domain is NOT marked for preloading" }, "HPKP": { "port": "443", @@ -489,7 +594,7 @@ "banner_server": { "port": "443", "severity": "INFO", - "finding": "Server-Timing: cf-q-config;dur=7.0000005507609e-06 cloudflare" + "finding": "No Server banner line in header, interesting!" }, "banner_application": { "port": "443", @@ -506,20 +611,15 @@ "severity": "OK", "finding": "SAMEORIGIN" }, - "X-Content-Type-Options": { - "port": "443", - "severity": "OK", - "finding": "nosniff" - }, "Content-Security-Policy": { "port": "443", "severity": "OK", - "finding": "frame-ancestors 'self' https://psicoactiva.com;" + "finding": "frame-ancestors 'none'" }, - "X-XSS-Protection": { + "Cache-Control": { "port": "443", "severity": "INFO", - "finding": "1; mode=block" + "finding": "max-age=3600" }, "banner_reverseproxy": { "port": "443", @@ -546,7 +646,7 @@ "severity": "OK", "cve": "CVE-2016-9244", "cwe": "CWE-200", - "finding": "not vulnerable" + "finding": "no session ticket extension" }, "ROBOT": { "port": "443", @@ -557,9 +657,9 @@ }, "secure_renego": { "port": "443", - "severity": "WARN", + "severity": "OK", "cwe": "CWE-310", - "finding": "OpenSSL handshake didn't succeed" + "finding": "supported" }, "secure_client_renego": { "port": "443", @@ -580,7 +680,7 @@ "severity": "OK", "cve": "CVE-2013-3587", "cwe": "CWE-310", - "finding": "not vulnerable, no HTTP compression - only supplied '/' tested" + "finding": "not vulnerable, no gzip/deflate/compress/br HTTP compression - only supplied '/' tested" }, "POODLE_SSL": { "port": "443", @@ -592,7 +692,7 @@ "fallback_SCSV": { "port": "443", "severity": "OK", - "finding": "no protocol below TLS 1.2 offered" + "finding": "supported" }, "SWEET32": { "port": "443", @@ -620,7 +720,7 @@ "severity": "INFO", "cve": "CVE-2016-0800 CVE-2016-0703", "cwe": "CWE-310", - "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://censys.io/ipv4?q=sha256 E06E2B75257BDD4B1DA9FBF332DAF67C76AF1395F06D47B9BF9BC0BA487CF7CC" + "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=A4AFB51D0E4754D22E3C2746FDFB12C0479E04851AB01E26B0782411BE93C494" }, "LOGJAM": { "port": "443", @@ -636,12 +736,19 @@ "cwe": "CWE-310", "finding": "no DH key with <= TLS 1.2" }, + "BEAST_CBC_TLS1": { + "port": "443", + "severity": "MEDIUM", + "cve": "CVE-2011-3389", + "cwe": "CWE-20", + "finding": "ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-SHA AES256-SHA" + }, "BEAST": { "port": "443", - "severity": "OK", + "severity": "LOW", "cve": "CVE-2011-3389", "cwe": "CWE-20", - "finding": "not vulnerable, no SSL3 or TLS1" + "finding": "VULNERABLE -- but also supports higher protocols TLSv1.1 TLSv1.2 (likely mitigated)" }, "LUCKY13": { "port": "443", @@ -650,6 +757,13 @@ "cwe": "CWE-310", "finding": "potentially vulnerable, uses TLS CBC ciphers" }, + "winshock": { + "port": "443", + "severity": "OK", + "cve": "CVE-2014-6321", + "cwe": "CWE-94", + "finding": "not vulnerable" + }, "RC4": { "port": "443", "severity": "OK", @@ -657,315 +771,250 @@ "cwe": "CWE-310", "finding": "not vulnerable" }, - "cipher_x1302": { - "port": "443", - "severity": "INFO", - "finding": "x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384" - }, - "cipher_x1303": { - "port": "443", - "severity": "INFO", - "finding": "x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256" - }, - "cipher_xcc14": { - "port": "443", - "severity": "INFO", - "finding": "xcc14 ECDHE-ECDSA-CHACHA20-POLY1305-OLD ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD" - }, - "cipher_xcc13": { - "port": "443", - "severity": "INFO", - "finding": "xcc13 ECDHE-RSA-CHACHA20-POLY1305-OLD ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD" - }, - "cipher_xc030": { - "port": "443", - "severity": "INFO", - "finding": "xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" - }, - "cipher_xc02c": { - "port": "443", - "severity": "INFO", - "finding": "xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" - }, - "cipher_xc028": { - "port": "443", - "severity": "INFO", - "finding": "xc028 ECDHE-RSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" - }, - "cipher_xc024": { - "port": "443", - "severity": "INFO", - "finding": "xc024 ECDHE-ECDSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384" - }, - "cipher_xc014": { - "port": "443", - "severity": "INFO", - "finding": "xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - }, - "cipher_xc00a": { - "port": "443", - "severity": "INFO", - "finding": "xc00a ECDHE-ECDSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" - }, - "cipher_xcca9": { - "port": "443", - "severity": "INFO", - "finding": "xcca9 ECDHE-ECDSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" - }, - "cipher_xcca8": { - "port": "443", - "severity": "INFO", - "finding": "xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" - }, - "cipher_x9d": { - "port": "443", - "severity": "INFO", - "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" - }, - "cipher_x3d": { + "clientsimulation-android_60": { "port": "443", "severity": "INFO", - "finding": "x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "cipher_x35": { + "clientsimulation-android_70": { "port": "443", "severity": "INFO", - "finding": "x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "cipher_x1301": { + "clientsimulation-android_81": { "port": "443", "severity": "INFO", - "finding": "x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "cipher_xc02f": { + "clientsimulation-android_90": { "port": "443", "severity": "INFO", - "finding": "xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_xc02b": { + "clientsimulation-android_X": { "port": "443", "severity": "INFO", - "finding": "xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_xc027": { + "clientsimulation-android_11": { "port": "443", "severity": "INFO", - "finding": "xc027 ECDHE-RSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_xc023": { + "clientsimulation-android_12": { "port": "443", "severity": "INFO", - "finding": "xc023 ECDHE-ECDSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_xc013": { + "clientsimulation-chrome_79_win10": { "port": "443", "severity": "INFO", - "finding": "xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_xc009": { + "clientsimulation-chrome_101_win10": { "port": "443", "severity": "INFO", - "finding": "xc009 ECDHE-ECDSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_x9c": { + "clientsimulation-firefox_66_win81": { "port": "443", "severity": "INFO", - "finding": "x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_x3c": { + "clientsimulation-firefox_100_win10": { "port": "443", "severity": "INFO", - "finding": "x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "cipher_x2f": { + "clientsimulation-ie_6_xp": { "port": "443", "severity": "INFO", - "finding": "x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" + "finding": "No connection" }, - "clientsimulation-android_442": { + "clientsimulation-ie_8_win7": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.0 ECDHE-RSA-AES128-SHA" }, - "clientsimulation-android_500": { + "clientsimulation-ie_8_xp": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305-OLD" + "finding": "No connection" }, - "clientsimulation-android_60": { + "clientsimulation-ie_11_win7": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305-OLD" + "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" }, - "clientsimulation-android_70": { + "clientsimulation-ie_11_win81": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" }, - "clientsimulation-android_81": { + "clientsimulation-ie_11_winphone81": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" }, - "clientsimulation-android_90": { + "clientsimulation-ie_11_win10": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-android_X": { + "clientsimulation-edge_15_win10": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-chrome_74_win10": { + "clientsimulation-edge_101_win10_21h2": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-chrome_79_win10": { + "clientsimulation-safari_121_ios_122": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-firefox_66_win81": { + "clientsimulation-safari_130_osx_10146": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-firefox_71_win10": { + "clientsimulation-safari_154_osx_1231": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-ie_6_xp": { + "clientsimulation-java_7u25": { "port": "443", "severity": "INFO", - "finding": "No connection" + "finding": "TLSv1.0 ECDHE-RSA-AES128-SHA" }, - "clientsimulation-ie_8_win7": { + "clientsimulation-java_8u161": { "port": "443", "severity": "INFO", - "finding": "No connection" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-ie_8_xp": { + "clientsimulation-java1102": { "port": "443", "severity": "INFO", - "finding": "No connection" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-ie_11_win7": { + "clientsimulation-java1703": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-ie_11_win81": { + "clientsimulation-go_1178": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-ie_11_winphone81": { + "clientsimulation-libressl_283": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-ie_11_win10": { + "clientsimulation-openssl_102e": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-edge_15_win10": { + "clientsimulation-openssl_110l": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-edge_17_win10": { + "clientsimulation-openssl_111d": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-opera_66_win10": { + "clientsimulation-openssl_303": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-safari_9_ios9": { + "clientsimulation-apple_mail_16_0": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" }, - "clientsimulation-safari_9_osx1011": { + "clientsimulation-thunderbird_91_9": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" }, - "clientsimulation-safari_10_osx1012": { + "rating_spec": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" }, - "clientsimulation-safari_121_ios_122": { + "rating_doc": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_CHACHA20_POLY1305_SHA256" + "finding": "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" }, - "clientsimulation-safari_130_osx_10146": { + "protocol_support_score": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_CHACHA20_POLY1305_SHA256" + "finding": "95" }, - "clientsimulation-apple_ats_9_ios9": { + "protocol_support_score_weighted": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "28" }, - "clientsimulation-java_6u45": { + "key_exchange_score": { "port": "443", "severity": "INFO", - "finding": "No connection" + "finding": "90" }, - "clientsimulation-java_7u25": { + "key_exchange_score_weighted": { "port": "443", "severity": "INFO", - "finding": "No connection" + "finding": "27" }, - "clientsimulation-java_8u161": { + "cipher_strength_score": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "finding": "90" }, - "clientsimulation-java1102": { + "cipher_strength_score_weighted": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "36" }, - "clientsimulation-java1201": { + "final_score": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "91" }, - "clientsimulation-openssl_102e": { + "overall_grade": { "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256" + "severity": "MEDIUM", + "finding": "B" }, - "clientsimulation-openssl_110l": { + "grade_cap_reason_1": { "port": "443", "severity": "INFO", - "finding": "TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305" + "finding": "Grade capped to B. TLS 1.1 offered" }, - "clientsimulation-openssl_111d": { + "grade_cap_reason_2": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" + "finding": "Grade capped to B. TLS 1.0 offered" }, - "clientsimulation-thunderbird_68_3_1": { + "grade_cap_reason_3": { "port": "443", "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_128_GCM_SHA256" + "finding": "Grade capped to A. HSTS max-age is too short" }, "scanTime": { "port": "443", "severity": "INFO", - "finding": "101" + "finding": "303" } } } \ No newline at end of file From 493a34dc83d1c64c2cf525af8b84d6f343bc6a2c Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 17 Mar 2023 10:04:49 +0100 Subject: [PATCH 055/209] Added function to extract signature algorithms from testssl output --- modules/compliance/compliance_base.py | 53 +++++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 7b08ea4..7a8c31a 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -10,7 +10,7 @@ def convert_signature_algorithm(sig_alg: str) -> str: """ This function is needed to convert the input from testssl to make it compatible with the requirements database """ - pass + return sig_alg.replace("-", "_").replace("+", "_").lower() class Compliance: @@ -154,18 +154,43 @@ def prepare_testssl_output(self, test_ssl_output): extensions_pairs.append(ex.split("/#")[0].lower().replace(" ", "_")) self._user_configuration["Extension"] = extensions_pairs elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): - # TODO make this part more stable, now it may crash if not self._user_configuration.get("CertificateSignature"): self._user_configuration["CertificateSignature"] = set() if not self._user_configuration.get("Hash"): self._user_configuration["Hash"] = set() - tokens = actual_dict["finding"].split(" ") - sig_alg = tokens[-1] - hash_alg = tokens[0] - if sig_alg.startswith("SHA"): - sig_alg, hash_alg = hash_alg, sig_alg - self._user_configuration["CertificateSignature"].add(sig_alg) - self._user_configuration["Hash"].add(hash_alg) + if " " in actual_dict["finding"]: + tokens = actual_dict["finding"].split(" ") + sig_alg = tokens[-1] + hash_alg = tokens[0] + if sig_alg.startswith("SHA"): + sig_alg, hash_alg = hash_alg, sig_alg + self._user_configuration["CertificateSignature"].add(sig_alg) + self._user_configuration["Hash"].add(hash_alg) + # TODO discuss this during the meeting + elif field[-11:] == "12_sig_algs": + if not self._user_configuration.get("CertificateSignature"): + self._user_configuration["CertificateSignature"] = set() + if not self._user_configuration.get("Hash"): + self._user_configuration["Hash"] = set() + finding = actual_dict["finding"] + elements = finding.split(" ") if " " in finding else [finding] + hashes = [] + signatures = [] + for el in elements: + if "-" not in el and "+" in el: + tokens = el.split("+") + signatures.append(tokens[0]) + hashes.append(tokens[1]) + self._user_configuration["CertificateSignature"].update(signatures) + self._user_configuration["Hash"].update(hashes) + elif field[-11:] == "13_sig_algs": + if not self._user_configuration.get("Signature"): + self._user_configuration["Signature"] = set() + finding = actual_dict["finding"] + values = finding.split(" ") if " " in finding else [finding] + values = [convert_signature_algorithm(sig) for sig in values] + self._user_configuration["Signature"].update(values) + elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = set() @@ -174,13 +199,6 @@ def prepare_testssl_output(self, test_ssl_output): values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ else actual_dict["finding"] self._user_configuration["Groups"] = values - elif field[-8:] == "sig_algs": - if not self._user_configuration.get("Signature"): - self._user_configuration["Signature"] = set() - finding = actual_dict["finding"] - values = finding.split(" ") if " " in finding else [finding] - values = [sig for sig in values] - self._user_configuration["Signature"].update(values) elif field in self.misc_fields: if not self._user_configuration.get("Misc"): @@ -206,6 +224,9 @@ def update_result(self, sheet, name, evaluation, is_enabled): self._output_dict[sheet][name] = f"{information_level}: {name} {action}" def is_enabled(self, config_field, name, entry): + """ + Checks if a field is enabled in the user configuration + """ field_value = self._user_configuration[config_field] enabled = False if isinstance(field_value, dict): From 0ba08038cca65ad3f3f38e1e57a2f4386b9b50aa Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 17 Mar 2023 16:06:43 +0100 Subject: [PATCH 056/209] Moved configurations files folder of the compliance module from config/modules to config/ and got transparency info from testssl output --- .../compliance/configuration_mapping.json | 0 .../compliance/evaluations_mapping.json | 0 .../{modules => }/compliance/misc_fields.json | 0 .../compliance/sheet_columns.json | 0 .../compliance/sheet_mapping.json | 0 configuration_ocsp.conf | 32 +++++++++++++++++++ modules/compliance/compliance_base.py | 21 ++++++++++-- modules/compliance/wrappers/db_reader.py | 4 +-- 8 files changed, 52 insertions(+), 5 deletions(-) rename configs/{modules => }/compliance/configuration_mapping.json (100%) rename configs/{modules => }/compliance/evaluations_mapping.json (100%) rename configs/{modules => }/compliance/misc_fields.json (100%) rename configs/{modules => }/compliance/sheet_columns.json (100%) rename configs/{modules => }/compliance/sheet_mapping.json (100%) create mode 100644 configuration_ocsp.conf diff --git a/configs/modules/compliance/configuration_mapping.json b/configs/compliance/configuration_mapping.json similarity index 100% rename from configs/modules/compliance/configuration_mapping.json rename to configs/compliance/configuration_mapping.json diff --git a/configs/modules/compliance/evaluations_mapping.json b/configs/compliance/evaluations_mapping.json similarity index 100% rename from configs/modules/compliance/evaluations_mapping.json rename to configs/compliance/evaluations_mapping.json diff --git a/configs/modules/compliance/misc_fields.json b/configs/compliance/misc_fields.json similarity index 100% rename from configs/modules/compliance/misc_fields.json rename to configs/compliance/misc_fields.json diff --git a/configs/modules/compliance/sheet_columns.json b/configs/compliance/sheet_columns.json similarity index 100% rename from configs/modules/compliance/sheet_columns.json rename to configs/compliance/sheet_columns.json diff --git a/configs/modules/compliance/sheet_mapping.json b/configs/compliance/sheet_mapping.json similarity index 100% rename from configs/modules/compliance/sheet_mapping.json rename to configs/compliance/sheet_mapping.json diff --git a/configuration_ocsp.conf b/configuration_ocsp.conf new file mode 100644 index 0000000..601f4c1 --- /dev/null +++ b/configuration_ocsp.conf @@ -0,0 +1,32 @@ +# generated 2023-03-17, Mozilla Guideline v5.6, Apache 2.4.41, OpenSSL 1.1.1k, intermediate configuration +# https://ssl-config.mozilla.org/#server=apache&version=2.4.41&config=intermediate&openssl=1.1.1k&guideline=5.6 + +# this configuration requires mod_ssl, mod_socache_shmcb, mod_rewrite, and mod_headers + + RewriteEngine On + RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/ + RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] + + + + SSLEngine on + + # curl https://ssl-config.mozilla.org/ffdhe2048.txt >> /path/to/signed_cert_and_intermediate_certs_and_dhparams + SSLCertificateFile /path/to/signed_cert_and_intermediate_certs_and_dhparams + SSLCertificateKeyFile /path/to/private_key + + # enable HTTP/2, if available + Protocols h2 http/1.1 + + # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds) + Header always set Strict-Transport-Security "max-age=63072000" + + +# intermediate configuration +SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 +SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +SSLHonorCipherOrder off +SSLSessionTickets off + +SSLUseStapling On +SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 7a8c31a..9b86ef0 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -20,9 +20,9 @@ def __init__(self): self._last_data = {} self._output_dict = {} self._user_configuration = {} - self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/modules/compliance/") - self.sheet_columns = load_configuration("sheet_columns", "configs/modules/compliance/") - self.misc_fields = load_configuration("misc_fields", "configs/modules/compliance/") + self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/compliance/") + self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") + self.misc_fields = load_configuration("misc_fields", "configs/compliance/") self.test_ssl = Testssl() def evaluation_to_use(self, evaluations, security: bool = True): @@ -125,14 +125,19 @@ def prepare_testssl_output(self, test_ssl_output): for site in test_ssl_output: for field in test_ssl_output[site]: actual_dict = test_ssl_output[site][field] + # Each protocol has its own field if (field.startswith("SSL") or field.startswith("TLS")) and field[3] != "_": if not self._user_configuration.get("Protocol"): self._user_configuration["Protocol"] = {} protocol_dict = self._user_configuration.get("Protocol") + # Standardization to have it compliant with the database new_version_name = field.replace("_", ".").replace("v", " ").replace("TLS1", "TLS 1") if new_version_name[-2] != '.': new_version_name += ".0" + # The protocols may appear both as supported and not supported, so they are saved in a dictionary + # with a boolean associated to the protocol to know if it is supported or not protocol_dict[new_version_name] = "not" not in actual_dict["finding"] + elif field.startswith("cipher") and "x" in field: if not self._user_configuration.get("CipherSuite"): self._user_configuration["CipherSuite"] = set() @@ -140,10 +145,12 @@ def prepare_testssl_output(self, test_ssl_output): if " " in value: value = value.split(" ")[-1] self._user_configuration["CipherSuite"].add(value) + elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = [] self._user_configuration["KeyLengths"].append(actual_dict["finding"].split(" ")[:2]) + elif field == "TLS_extensions": entry = actual_dict["finding"] entry = entry.replace("' '", ",").replace("'", "") @@ -153,6 +160,7 @@ def prepare_testssl_output(self, test_ssl_output): # the [1] is the iana code extensions_pairs.append(ex.split("/#")[0].lower().replace(" ", "_")) self._user_configuration["Extension"] = extensions_pairs + elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): if not self._user_configuration.get("CertificateSignature"): self._user_configuration["CertificateSignature"] = set() @@ -200,6 +208,13 @@ def prepare_testssl_output(self, test_ssl_output): else actual_dict["finding"] self._user_configuration["Groups"] = values + elif "transparency" in field: + if not self._user_configuration.get("Transparency"): + self._user_configuration["Transparency"] = {} + config_dict = self._user_configuration["Transparency"] + index = len(config_dict) + config_dict[index] = actual_dict["finding"] + elif field in self.misc_fields: if not self._user_configuration.get("Misc"): self._user_configuration["Misc"] = {} diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index d20c481..5a9cdfb 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -11,8 +11,8 @@ def __init__(self, file: str = database_file): self.database_file = file self.connection = sqlite3.connect(self.database_file) self.cursor = self.connection.cursor() - self.configuration_mapping = load_configuration("configuration_mapping", "configs/modules/compliance/") - self.sheet_mapping = load_configuration("sheet_mapping", "configs/modules/compliance/") + self.configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/") + self.sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") # # Retrieve the list of tables from the database # self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") From 5085e0b57c11e80b780af7303793c1998a1b5cfa Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 17 Mar 2023 16:28:45 +0100 Subject: [PATCH 057/209] Started working on the parser and improved comments --- modules/compliance/compliance_base.py | 29 ++++++++++++++++++++++++++- modules/compliance/compliance_many.py | 21 ++++++++++--------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 9b86ef0..5644721 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -13,6 +13,17 @@ def convert_signature_algorithm(sig_alg: str) -> str: return sig_alg.replace("-", "_").replace("+", "_").lower() +class ConditionParser: + def __init__(self): + self.result = None + self.expression = "" + self.logical_separators = ["and", "or"] + self.instructions = load_configuration("condition_instructions", "configs/compliance/") + + def solve(self): + pass + + class Compliance: def __init__(self): self._input_dict = {} @@ -138,17 +149,20 @@ def prepare_testssl_output(self, test_ssl_output): # with a boolean associated to the protocol to know if it is supported or not protocol_dict[new_version_name] = "not" not in actual_dict["finding"] + # All the ciphers appear in different fields whose form is cipher_%x% elif field.startswith("cipher") and "x" in field: if not self._user_configuration.get("CipherSuite"): self._user_configuration["CipherSuite"] = set() value = actual_dict.get("finding", "") if " " in value: + # Only the last part of the line is the actual cipher value = value.split(" ")[-1] self._user_configuration["CipherSuite"].add(value) elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = [] + # the first two tokens (after doing a space split) are the Algorithm and the keysize self._user_configuration["KeyLengths"].append(actual_dict["finding"].split(" ")[:2]) elif field == "TLS_extensions": @@ -161,6 +175,7 @@ def prepare_testssl_output(self, test_ssl_output): extensions_pairs.append(ex.split("/#")[0].lower().replace(" ", "_")) self._user_configuration["Extension"] = extensions_pairs + # From the certificate signature algorithm is possible to extract both CertificateSignature and Hash elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): if not self._user_configuration.get("CertificateSignature"): self._user_configuration["CertificateSignature"] = set() @@ -170,11 +185,13 @@ def prepare_testssl_output(self, test_ssl_output): tokens = actual_dict["finding"].split(" ") sig_alg = tokens[-1] hash_alg = tokens[0] + # sometimes the hashing algorithm comes first, so they must be switched if sig_alg.startswith("SHA"): sig_alg, hash_alg = hash_alg, sig_alg self._user_configuration["CertificateSignature"].add(sig_alg) self._user_configuration["Hash"].add(hash_alg) - # TODO discuss this during the meeting + + # In TLS 1.2 the certificate signatures and hashes are present in the signature algorithms field. elif field[-11:] == "12_sig_algs": if not self._user_configuration.get("CertificateSignature"): self._user_configuration["CertificateSignature"] = set() @@ -185,12 +202,17 @@ def prepare_testssl_output(self, test_ssl_output): hashes = [] signatures = [] for el in elements: + # The ones with the '-' inside are the ones for TLS 1.3. if "-" not in el and "+" in el: + # The entries are SigAlg+HashAlg tokens = el.split("+") signatures.append(tokens[0]) hashes.append(tokens[1]) self._user_configuration["CertificateSignature"].update(signatures) self._user_configuration["Hash"].update(hashes) + + # From TLS 1.3 the signature algorithms are different from the previous versions. + # So they are saved in a different field of the configuration dictionary. elif field[-11:] == "13_sig_algs": if not self._user_configuration.get("Signature"): self._user_configuration["Signature"] = set() @@ -203,15 +225,20 @@ def prepare_testssl_output(self, test_ssl_output): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = set() self._user_configuration["KeyLengths"].update(actual_dict["finding"].split(" ")[:2]) + + # The supported groups are available as a list in this field elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ else actual_dict["finding"] self._user_configuration["Groups"] = values + # The transparency field describes how the transparency is handled in each certificate. + # https://developer.mozilla.org/en-US/docs/Web/Security/Certificate_Transparency (for the possibilities) elif "transparency" in field: if not self._user_configuration.get("Transparency"): self._user_configuration["Transparency"] = {} config_dict = self._user_configuration["Transparency"] + # the index is basically the certificate number index = len(config_dict) config_dict[index] = actual_dict["finding"] diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index 8d275db..5feef10 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -1,7 +1,6 @@ from modules.compliance.compliance_base import Compliance -# TODO fix this class ComplianceMany(Compliance): def _worker(self, sheets_to_check): """ @@ -15,7 +14,7 @@ def _worker(self, sheets_to_check): raise ValueError("No configuration provided") columns = ["name", "evaluation", "condition"] name_index = columns.index("name") - evaluation_index = columns.index("evaluation") + level_index = columns.index("evaluation") entries = {} tables = [] for sheet in sheets_to_check: @@ -28,19 +27,23 @@ def _worker(self, sheets_to_check): data = self._database_instance.output(columns) entries[sheet] = data tables = [] - actual_evaluation = "" + # A more fitting name could be current_requirement_level + resulting_level = "" for sheet in sheets_to_check: counter = 1 for entry in entries[sheet]: name = entry[name_index] - evaluation = entry[evaluation_index] - if evaluation != actual_evaluation: - evaluations = [actual_evaluation, evaluation] + entry_level = entry[level_index] + if entry_level != resulting_level: + evaluations = [resulting_level, entry_level] best_evaluation = self.evaluation_to_use(evaluations) - actual_evaluation = evaluations[best_evaluation] + resulting_level = evaluations[best_evaluation] + # The entries are ordered by name so every time the counter is the same as the number of guidelines to + # check it is time to add the entry to the output dictionary. if sheet and counter == len(sheets_to_check[sheet]): counter = 0 enabled = self.is_enabled(sheet, name, entry) - self.update_result(sheet, name, actual_evaluation, enabled) - actual_evaluation = "" + self.update_result(sheet, name, resulting_level, enabled) + # the resulting level is reset so that it doesn't influence the next element. + resulting_level = "" counter += 1 From 1b1415b76ed1dbd5cb554608f91870d04c6aa382 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 17 Mar 2023 17:10:05 +0100 Subject: [PATCH 058/209] added condition for instructions --- configs/compliance/condition_instructions.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 configs/compliance/condition_instructions.json diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json new file mode 100644 index 0000000..c23eea6 --- /dev/null +++ b/configs/compliance/condition_instructions.json @@ -0,0 +1,5 @@ +{ + "EXTENSION": "Extension", + "CIPHER": "CipherSuite", + "TRANSPARENCY": "Transparency" +} \ No newline at end of file From 5583402da77d8081f37f95b9465dd8e0b3243856 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 20 Mar 2023 11:39:47 +0100 Subject: [PATCH 059/209] Changed evaluation to entry_level and Updated extensions in user_configuration. Now they are saved in a dictionary that also contains its IANA code --- modules/compliance/compliance_base.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 5644721..51d39c1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -45,7 +45,7 @@ def evaluation_to_use(self, evaluations, security: bool = True): :param security: True if security wins false if legacy wins, default to true :type security: bool :return: the standard which wins - :rtype: bool + :rtype: int """ # If an evaluation is not mapped it can be considered as a Not mentioned security_mapping = "security" if security else "legacy" @@ -169,10 +169,11 @@ def prepare_testssl_output(self, test_ssl_output): entry = actual_dict["finding"] entry = entry.replace("' '", ",").replace("'", "") extensions: list = entry.split(",") - extensions_pairs = [] + extensions_pairs = {} for ex in extensions: # the [1] is the iana code - extensions_pairs.append(ex.split("/#")[0].lower().replace(" ", "_")) + tokens = ex.split("/#") + extensions_pairs[tokens[1]] = tokens[0].lower().replace(" ", "_") self._user_configuration["Extension"] = extensions_pairs # From the certificate signature algorithm is possible to extract both CertificateSignature and Hash @@ -247,23 +248,23 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] - def update_result(self, sheet, name, evaluation, is_enabled): + def update_result(self, sheet, name, entry_level, is_enabled, source): information_level = None action = None - if evaluation == "must" and not is_enabled: + if entry_level == "must" and not is_enabled: information_level = "ERROR" action = "has to be enabled" - elif evaluation == "must not" and is_enabled: + elif entry_level == "must not" and is_enabled: information_level = "ERROR" action = "has to be disabled" - elif evaluation == "recommended" and not is_enabled: + elif entry_level == "recommended" and not is_enabled: information_level = "ALERT" action = "should be enabled" - elif evaluation == "not recommended" and is_enabled: + elif entry_level == "not recommended" and is_enabled: information_level = "ALERT" action = "should be disabled" if information_level: - self._output_dict[sheet][name] = f"{information_level}: {name} {action}" + self._output_dict[sheet][name] = f"{information_level}: {action} according to {source}" def is_enabled(self, config_field, name, entry): """ @@ -271,7 +272,10 @@ def is_enabled(self, config_field, name, entry): """ field_value = self._user_configuration[config_field] enabled = False - if isinstance(field_value, dict): + if isinstance(field_value, dict) and isinstance(field_value.get(name), str): + # Extensions case + enabled = name in field_value.items() + elif isinstance(field_value, dict): enabled = field_value.get(name, None) if enabled is None: enabled = True if "all" in field_value else False From 01c64786e4ecbc2249e8c51a203d81418dffa809 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 20 Mar 2023 11:40:17 +0100 Subject: [PATCH 060/209] Added the source to the compliance_many.py output --- modules/compliance/compliance_many.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index 5feef10..74823f9 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -4,7 +4,7 @@ class ComplianceMany(Compliance): def _worker(self, sheets_to_check): """ - :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} + :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol: version_of_protocol} :type sheets_to_check: dict :return: processed results @@ -12,9 +12,9 @@ def _worker(self, sheets_to_check): """ if not self._user_configuration: raise ValueError("No configuration provided") - columns = ["name", "evaluation", "condition"] + columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") - level_index = columns.index("evaluation") + level_index = columns.index("level") entries = {} tables = [] for sheet in sheets_to_check: @@ -27,23 +27,29 @@ def _worker(self, sheets_to_check): data = self._database_instance.output(columns) entries[sheet] = data tables = [] + self.entries = entries # A more fitting name could be current_requirement_level resulting_level = "" for sheet in sheets_to_check: counter = 1 + source_guideline = entries[sheet][-1] for entry in entries[sheet]: name = entry[name_index] entry_level = entry[level_index] + guideline = entry[-1] if entry_level != resulting_level: - evaluations = [resulting_level, entry_level] - best_evaluation = self.evaluation_to_use(evaluations) - resulting_level = evaluations[best_evaluation] + levels = [resulting_level, entry_level] + best_level = self.evaluation_to_use(levels) + # if best_level is 0 the source_guideline is the same + if best_level: + source_guideline = guideline + resulting_level = levels[best_level] # The entries are ordered by name so every time the counter is the same as the number of guidelines to # check it is time to add the entry to the output dictionary. if sheet and counter == len(sheets_to_check[sheet]): counter = 0 enabled = self.is_enabled(sheet, name, entry) - self.update_result(sheet, name, resulting_level, enabled) + self.update_result(sheet, name, resulting_level, enabled, source_guideline) # the resulting level is reset so that it doesn't influence the next element. resulting_level = "" counter += 1 From 3501d5d5d8ad76adc675d5ca6ef20cef68d3d8ab Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 20 Mar 2023 11:41:08 +0100 Subject: [PATCH 061/209] Updated configuration files to work with the new names --- configs/compliance/configuration_mapping.json | 4 ++-- configs/compliance/sheet_columns.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/compliance/configuration_mapping.json b/configs/compliance/configuration_mapping.json index 9525fda..9e6a085 100644 --- a/configs/compliance/configuration_mapping.json +++ b/configs/compliance/configuration_mapping.json @@ -1,4 +1,4 @@ { - "SSLProtocol": "Protocols", - "SSLCipherSuite": "Cipher Suites" + "SSLProtocol": "Protocol", + "SSLCipherSuite": "CipherSuite" } \ No newline at end of file diff --git a/configs/compliance/sheet_columns.json b/configs/compliance/sheet_columns.json index c8b33a9..4f27067 100644 --- a/configs/compliance/sheet_columns.json +++ b/configs/compliance/sheet_columns.json @@ -1,3 +1,3 @@ { - "KeyLength": ["name", "length", "evaluation", "condition"] + "KeyLength": ["name", "length", "level", "condition", "guidelineName"] } \ No newline at end of file From 1325766b55f33a5ebc4a20781b790511d728154b Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 20 Mar 2023 11:57:25 +0100 Subject: [PATCH 062/209] Fixed complianceOne to make it work with the new version --- modules/compliance/compliance_one.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py index a468777..452ea78 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compliance_one.py @@ -12,9 +12,9 @@ def _worker(self, sheets_to_check): """ if not self._user_configuration: raise ValueError("No configuration provided") - columns = ["name", "evaluation", "condition"] + columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") - evaluation_index = columns.index("evaluation") + evaluation_index = columns.index("level") for sheet in sheets_to_check: # If the sheet isn't in the dictionary then I can use the default value columns = self.sheet_columns.get(sheet, columns) @@ -30,4 +30,4 @@ def _worker(self, sheets_to_check): name = entry[name_index] evaluation = entry[evaluation_index] enabled = self.is_enabled(config_field, name, entry) - self.update_result(sheet, name, evaluation, enabled) + self.update_result(sheet, name, evaluation, enabled, entry[-1]) From 6a53ca0c76440fb8bb549be2e9fd8af4f59ee080 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 20 Mar 2023 17:26:53 +0100 Subject: [PATCH 063/209] Started working on the configuration_generator. At the moment it only works with one guideline and only with one guideline --- configs/compliance/configuration_mapping.json | 4 +- configs/compliance/configuration_rules.json | 32 +++++++++ configs/compliance/template_apache.conf | 9 +++ modules/compliance/compliance_base.py | 9 ++- modules/compliance/generate_one.py | 66 +++++++++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 configs/compliance/configuration_rules.json create mode 100644 configs/compliance/template_apache.conf create mode 100644 modules/compliance/generate_one.py diff --git a/configs/compliance/configuration_mapping.json b/configs/compliance/configuration_mapping.json index 9e6a085..01f3c15 100644 --- a/configs/compliance/configuration_mapping.json +++ b/configs/compliance/configuration_mapping.json @@ -1,4 +1,6 @@ { "SSLProtocol": "Protocol", - "SSLCipherSuite": "CipherSuite" + "SSLCipherSuite": "CipherSuite", + "SSLSessionTickets": {"Extension": "session_ticket"}, + "SSLUseStapling": {"Extension": "status_request"} } \ No newline at end of file diff --git a/configs/compliance/configuration_rules.json b/configs/compliance/configuration_rules.json new file mode 100644 index 0000000..6447706 --- /dev/null +++ b/configs/compliance/configuration_rules.json @@ -0,0 +1,32 @@ +{ + "SSLProtocol": { + "enable": "name", + "disable": "-name", + "separator": " ", + "added_negatives": true, + "replacements": { + " ": "v", + ".0": "" + } + }, + "SSLCipherSuite": { + "enable": "name", + "disable": "", + "separator": ":", + "added_negatives": false, + "replacements": { + "_": "-", + "TLS-": "" + } + }, + "SSLSessionTickets": { + "enable": "on", + "disable": "off", + "separator": "" + }, + "SSLUseStapling": { + "enable": "on", + "disable": "off", + "separator": "" + } +} \ No newline at end of file diff --git a/configs/compliance/template_apache.conf b/configs/compliance/template_apache.conf new file mode 100644 index 0000000..a18e210 --- /dev/null +++ b/configs/compliance/template_apache.conf @@ -0,0 +1,9 @@ +# Template based off the Figure 4 of the compliance_paper + + SSLEngine on + SSLCertificateFile /path/to/cert_chain + SSLCertificateKeyFile /path/to/private_key + Header always set Strict-Transport-Security "max-age=63072000" + + +SSLHonorCipherOrder off \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 51d39c1..70b5bfe 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -26,6 +26,7 @@ def solve(self): class Compliance: def __init__(self): + self._output_file = None self._input_dict = {} self._database_instance = Database() self._last_data = {} @@ -34,6 +35,7 @@ def __init__(self): self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/compliance/") self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") self.misc_fields = load_configuration("misc_fields", "configs/compliance/") + self._validator = Validator() self.test_ssl = Testssl() def evaluation_to_use(self, evaluations, security: bool = True): @@ -72,10 +74,12 @@ def input(self, **kwargs): * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol * *actual_configuration* (``dict``) -- The configuration to check, not needed if generating * *test_ssl* (``bool``) -- If true the user_configuration gets generated using testssl data + * *output_config* (``str``) -- The path and name of the output file """ actual_configuration = kwargs.get("actual_configuration") use_test_ssl = kwargs.get("test_ssl") - if actual_configuration and Validator([(actual_configuration, dict)]): + output_file = kwargs.get("output_config") + if actual_configuration and self._validator.dict(actual_configuration): self.prepare_configuration(actual_configuration) elif use_test_ssl: # test_ssl_output = self.test_ssl.run(**{"hostname": "falconvendor.davita.com"}) @@ -86,7 +90,8 @@ def input(self, **kwargs): with open("testssl_dump.json", 'r') as f: test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) - + elif output_file and self._validator.string(output_file): + self._output_file = output_file self._input_dict = kwargs # To override diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py new file mode 100644 index 0000000..6eb7606 --- /dev/null +++ b/modules/compliance/generate_one.py @@ -0,0 +1,66 @@ +from modules.compliance.compliance_base import Compliance +from utils.loader import load_configuration + + +class GenerateOne(Compliance): + def __init__(self): + super().__init__() + self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/") + + def _worker(self, sheets_to_check): + if not self._output_file: + raise ValueError("No file path provided") + string_to_add = "" + columns = ["name", "level", "condition", "guidelineName"] + name_index = columns.index("name") + level_index = columns.index("level") + conf_mapping = self._database_instance.configuration_mapping + for field in conf_mapping: + tmp_string = field + " " + sheet = conf_mapping[field] + query_filter = "" + if isinstance(sheet, dict): + table_to_search = list(sheet.keys())[0] + name_to_search = sheet[table_to_search] + query_filter = "WHERE name == \"" + name_to_search + "\"" + sheet = table_to_search + columns = self.sheet_columns.get(sheet, columns) + guideline = list(sheets_to_check[sheet].keys())[0] + table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + self._database_instance.input([table_name], other_filter=query_filter) + data = self._database_instance.output(columns) + field_rules = self._configuration_rules.get(field, {}) + allow_string = field_rules.get("enable", "name") + deny_string = field_rules.get("disable", "-name") + separator = field_rules.get("separator", " ") + # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) + added_negatives = field_rules.get("added_negatives", False) + replacements = field_rules.get("replacements", []) + for entry in data: + added = True + name = entry[name_index] + for replacement in replacements: + name = name.replace(replacement, replacements[replacement]) + if entry[level_index] in ["must", "recommended"]: + tmp_string += allow_string.replace("name", name) + elif entry[level_index] in ["must not", "not recommended"]: + tmp_string += deny_string.replace("name", name) + added = added_negatives + else: + added = False + if added: + tmp_string += separator + + if tmp_string and tmp_string[-1] == ":": + tmp_string = tmp_string[:-1] + if len(tmp_string) != len(field) + 1: + string_to_add += "\n" + tmp_string + with open("configs/compliance/template_apache.conf", "r") as f: + base_conf = f.read() + with open(self._output_file, "w") as f: + f.write(base_conf + "\n" + string_to_add) + self._output_dict["Done"] = True + + + + From 3758f73bbd1db8b40f1eb615290c1a41f4647281 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 21 Mar 2023 12:10:36 +0100 Subject: [PATCH 064/209] Reorganized some files, fixed check for presence of extensions and improved readability of compliance_many.py --- .../{ => apache}/configuration_mapping.json | 0 .../{ => apache}/configuration_rules.json | 0 .../{ => apache}/template_apache.conf | 0 modules/compliance/compliance_base.py | 116 ++++++++++++++++-- modules/compliance/compliance_many.py | 45 ++----- modules/compliance/generate_many.py | 12 ++ modules/compliance/generate_one.py | 37 +++--- modules/compliance/wrappers/db_reader.py | 1 - 8 files changed, 149 insertions(+), 62 deletions(-) rename configs/compliance/{ => apache}/configuration_mapping.json (100%) rename configs/compliance/{ => apache}/configuration_rules.json (100%) rename configs/compliance/{ => apache}/template_apache.conf (100%) create mode 100644 modules/compliance/generate_many.py diff --git a/configs/compliance/configuration_mapping.json b/configs/compliance/apache/configuration_mapping.json similarity index 100% rename from configs/compliance/configuration_mapping.json rename to configs/compliance/apache/configuration_mapping.json diff --git a/configs/compliance/configuration_rules.json b/configs/compliance/apache/configuration_rules.json similarity index 100% rename from configs/compliance/configuration_rules.json rename to configs/compliance/apache/configuration_rules.json diff --git a/configs/compliance/template_apache.conf b/configs/compliance/apache/template_apache.conf similarity index 100% rename from configs/compliance/template_apache.conf rename to configs/compliance/apache/template_apache.conf diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 70b5bfe..3a96340 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -26,15 +26,20 @@ def solve(self): class Compliance: def __init__(self): - self._output_file = None + self._config_template = "configs/compliance/apache/template_apache.conf" + self._apache = True + self._config_output = None self._input_dict = {} self._database_instance = Database() self._last_data = {} self._output_dict = {} self._user_configuration = {} + self.entries = {} + self.evaluated_entries = {} self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/compliance/") self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") self.misc_fields = load_configuration("misc_fields", "configs/compliance/") + self.configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/apache/") self._validator = Validator() self.test_ssl = Testssl() @@ -74,10 +79,14 @@ def input(self, **kwargs): * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol * *actual_configuration* (``dict``) -- The configuration to check, not needed if generating * *test_ssl* (``bool``) -- If true the user_configuration gets generated using testssl data - * *output_config* (``str``) -- The path and name of the output file + * *config_template* (``str``) -- (Optional) the file that should be used as template + * *apache* (``bool``) -- Default to True, if false nginx will be used + * *config_output* (``str``) -- The path and name of the output file """ actual_configuration = kwargs.get("actual_configuration") use_test_ssl = kwargs.get("test_ssl") + input_file = kwargs.get("input_config") + use_apache = kwargs.get("apache") output_file = kwargs.get("output_config") if actual_configuration and self._validator.dict(actual_configuration): self.prepare_configuration(actual_configuration) @@ -91,7 +100,12 @@ def input(self, **kwargs): test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) elif output_file and self._validator.string(output_file): - self._output_file = output_file + self._config_output = output_file + # This two parameters are needed only for the configuration generation + if input_file and self._validator.string(input_file): + self._config_template = input_file + if use_apache and self._validator.bool(use_apache): + self._apache = use_apache self._input_dict = kwargs # To override @@ -133,7 +147,7 @@ def prepare_configuration(self, actual_configuration): new_version_name += ".0" tmp_dict[new_version_name] = accepted new_field = tmp_dict - field_name = self._database_instance.configuration_mapping.get(field, field) + field_name = self.configuration_mapping.get(field, field) self._user_configuration[field_name] = new_field def prepare_testssl_output(self, test_ssl_output): @@ -277,15 +291,101 @@ def is_enabled(self, config_field, name, entry): """ field_value = self._user_configuration[config_field] enabled = False - if isinstance(field_value, dict) and isinstance(field_value.get(name), str): - # Extensions case - enabled = name in field_value.items() - elif isinstance(field_value, dict): + if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): enabled = field_value.get(name, None) if enabled is None: enabled = True if "all" in field_value else False + elif isinstance(field_value, dict): + # Extensions case + enabled = name in field_value.values() elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): enabled = entry[:2] in field_value elif isinstance(field_value, list) or isinstance(field_value, set): enabled = name in field_value return enabled + + def _retrieve_entries(self, sheets_to_check, columns): + """ + Given the input dictionary and the list of columns updates the entries field with a dictionary in the form + sheet: data. The data is ordered by name + """ + entries = {} + tables = [] + for sheet in sheets_to_check: + for guideline in sheets_to_check[sheet]: + if not self._output_dict.get(sheet): + self._output_dict[sheet] = {} + table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + tables.append(table_name) + self._database_instance.input(tables, other_filter="ORDER BY name") + data = self._database_instance.output(columns) + entries[sheet] = data + tables = [] + self.entries = entries + + def _evaluate_entries(self, sheets_to_check, level_index): + """ + This function checks the entries with the same name and chooses which guideline to follow for that entry. + The results can be found in the evaluated_entries field. The dictionary will have form: + self.evaluated_entries[sheet][count] = { + "name": str, The name of the entry + "level": str, The level that resulted from the evaluation + "source": str The guideline from which the level is deducted + } + :param sheets_to_check: The input dictionary + :param level_index: The index of the column that contains the requirement level of the entry + :type level_index: int + """ + # A more fitting name could be current_requirement_level + resulting_level = "" + for sheet in self.entries: + # The total value is used as an index to avoid eventual collisions between equal names in the same sheet + total = 0 + if not self.evaluated_entries.get(sheet): + self.evaluated_entries[sheet] = {} + counter = 1 + source_guideline = self.entries[sheet][-1] + for entry in self.entries[sheet]: + entry_level = entry[level_index] + guideline = entry[-1] + if entry_level != resulting_level: + levels = [resulting_level, entry_level] + best_level = self.evaluation_to_use(levels) + # if best_level is 0 the source_guideline is the same + if best_level: + source_guideline = guideline + resulting_level = levels[best_level] + # The entries are ordered by name so every time the counter is the same as the number of guidelines to + # check it is time to add the entry to the output dictionary. + if sheet and counter == len(sheets_to_check[sheet]): + counter = 0 + # Save it to the dictionary + self.evaluated_entries[sheet][total] = { + "entry": entry, + "level": resulting_level, + "source": source_guideline + } + # the resulting level is reset so that it doesn't influence the next element. + resulting_level = "" + total += 1 + counter += 1 + + +class Generator(Compliance): + """This class only exists to add fields that are needed by the generator to the Compliance class""" + def __init__(self): + super().__init__() + self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/apache") + + # To override + def _worker(self, sheets_to_check): + """ + :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} + :type sheets_to_check: dict + + :return: processed results + :rtype: dict + + :raise NotImplementedError: + """ + raise NotImplementedError("This method should be reimplemented") \ No newline at end of file diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index 74823f9..a5003d1 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -15,41 +15,12 @@ def _worker(self, sheets_to_check): columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") - entries = {} - tables = [] - for sheet in sheets_to_check: - for guideline in sheets_to_check[sheet]: - if not self._output_dict.get(sheet): - self._output_dict[sheet] = {} - table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) - tables.append(table_name) - self._database_instance.input(tables, other_filter="ORDER BY name") - data = self._database_instance.output(columns) - entries[sheet] = data - tables = [] - self.entries = entries - # A more fitting name could be current_requirement_level - resulting_level = "" - for sheet in sheets_to_check: - counter = 1 - source_guideline = entries[sheet][-1] - for entry in entries[sheet]: + # fill the entries field with the data from the sheets + self._retrieve_entries(sheets_to_check, columns) + self._evaluate_entries(sheets_to_check, level_index) + for sheet in self.evaluated_entries: + for entry_dict in self.evaluated_entries[sheet].values(): + entry = entry_dict["entry"] name = entry[name_index] - entry_level = entry[level_index] - guideline = entry[-1] - if entry_level != resulting_level: - levels = [resulting_level, entry_level] - best_level = self.evaluation_to_use(levels) - # if best_level is 0 the source_guideline is the same - if best_level: - source_guideline = guideline - resulting_level = levels[best_level] - # The entries are ordered by name so every time the counter is the same as the number of guidelines to - # check it is time to add the entry to the output dictionary. - if sheet and counter == len(sheets_to_check[sheet]): - counter = 0 - enabled = self.is_enabled(sheet, name, entry) - self.update_result(sheet, name, resulting_level, enabled, source_guideline) - # the resulting level is reset so that it doesn't influence the next element. - resulting_level = "" - counter += 1 + enabled = self.is_enabled(sheet, name, entry) + self.update_result(sheet, name, entry_dict["level"], enabled, entry_dict["source"]) diff --git a/modules/compliance/generate_many.py b/modules/compliance/generate_many.py new file mode 100644 index 0000000..aabf8f5 --- /dev/null +++ b/modules/compliance/generate_many.py @@ -0,0 +1,12 @@ +from modules.compliance.compliance_base import Generator + + +class GenerateMany(Generator): + def _worker(self, sheets_to_check): + if not self._config_output: + raise ValueError("No output file path provided") + columns = ["name", "level", "condition", "guidelineName"] + name_index = columns.index("name") + level_index = columns.index("level") + # fill the entries field with the data from the sheets + self._retrieve_entries(sheets_to_check, columns) diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 6eb7606..bb2633c 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -1,35 +1,39 @@ -from modules.compliance.compliance_base import Compliance -from utils.loader import load_configuration +import os.path +from modules.compliance.compliance_base import Generator -class GenerateOne(Compliance): - def __init__(self): - super().__init__() - self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/") +class GenerateOne(Generator): def _worker(self, sheets_to_check): - if not self._output_file: - raise ValueError("No file path provided") + if not self._config_output: + raise ValueError("No output file path provided") string_to_add = "" columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") - conf_mapping = self._database_instance.configuration_mapping + conf_mapping = self.configuration_mapping for field in conf_mapping: + if not self._output_dict.get(field): + self._output_dict[field] = {} tmp_string = field + " " sheet = conf_mapping[field] query_filter = "" + # Dictionaries are used for specific things like a directive that enables an extension for this reason it is + # used a filter on the query to get that specific thing by name if isinstance(sheet, dict): table_to_search = list(sheet.keys())[0] name_to_search = sheet[table_to_search] query_filter = "WHERE name == \"" + name_to_search + "\"" sheet = table_to_search columns = self.sheet_columns.get(sheet, columns) + # Only the first guideline of each sheet is the interesting one guideline = list(sheets_to_check[sheet].keys())[0] table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) self._database_instance.input([table_name], other_filter=query_filter) data = self._database_instance.output(columns) field_rules = self._configuration_rules.get(field, {}) + # the idea is that it is possible to define a custom value to insert like on/off or name to use the name + # defined in the config file allow_string = field_rules.get("enable", "name") deny_string = field_rules.get("disable", "-name") separator = field_rules.get("separator", " ") @@ -43,11 +47,15 @@ def _worker(self, sheets_to_check): name = name.replace(replacement, replacements[replacement]) if entry[level_index] in ["must", "recommended"]: tmp_string += allow_string.replace("name", name) + self._output_dict[field][name] = True elif entry[level_index] in ["must not", "not recommended"]: tmp_string += deny_string.replace("name", name) added = added_negatives + self._output_dict[field][name] = False else: added = False + self._output_dict[field][name] = False + if added: tmp_string += separator @@ -55,12 +63,9 @@ def _worker(self, sheets_to_check): tmp_string = tmp_string[:-1] if len(tmp_string) != len(field) + 1: string_to_add += "\n" + tmp_string - with open("configs/compliance/template_apache.conf", "r") as f: + if not os.path.isfile(self._config_template): + raise FileNotFoundError("Invalid template file") + with open(self._config_template, "r") as f: base_conf = f.read() - with open(self._output_file, "w") as f: + with open(self._config_output, "w") as f: f.write(base_conf + "\n" + string_to_add) - self._output_dict["Done"] = True - - - - diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index 5a9cdfb..f772ee5 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -11,7 +11,6 @@ def __init__(self, file: str = database_file): self.database_file = file self.connection = sqlite3.connect(self.database_file) self.cursor = self.connection.cursor() - self.configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/") self.sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") # # Retrieve the list of tables from the database From b053a0b4845825e8fc152d5df3edde3036be3ff1 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 21 Mar 2023 16:04:54 +0100 Subject: [PATCH 065/209] Changed how the compliance module takes input, now it accepts a file or a hostname. Reorganized the generation system by dividing it into different classes, one for each configuration. Nginx configuration still doesn't work --- .../apache/configuration_mapping.json | 6 -- configs/compliance/apache/mapping.json | 6 ++ configs/compliance/apache/rules.json | 1 + .../{template_apache.conf => template.conf} | 0 .../generate/configuration_mapping.json | 6 ++ .../configuration_rules.json | 8 +-- .../{requirements.db => __init__.py} | 0 modules/compliance/compliance_base.py | 58 +++++++++------ modules/compliance/configuration/__init__.py | 0 .../configuration/apache_configuration.py | 65 +++++++++++++++++ .../configuration/configuration_base.py | 50 +++++++++++++ .../configuration/nginx_configuration.py | 72 +++++++++++++++++++ modules/compliance/generate_many.py | 2 - modules/compliance/generate_one.py | 44 ++---------- 14 files changed, 244 insertions(+), 74 deletions(-) delete mode 100644 configs/compliance/apache/configuration_mapping.json create mode 100644 configs/compliance/apache/mapping.json create mode 100644 configs/compliance/apache/rules.json rename configs/compliance/apache/{template_apache.conf => template.conf} (100%) create mode 100644 configs/compliance/generate/configuration_mapping.json rename configs/compliance/{apache => generate}/configuration_rules.json (83%) rename modules/compliance/{requirements.db => __init__.py} (100%) create mode 100644 modules/compliance/configuration/__init__.py create mode 100644 modules/compliance/configuration/apache_configuration.py create mode 100644 modules/compliance/configuration/configuration_base.py create mode 100644 modules/compliance/configuration/nginx_configuration.py diff --git a/configs/compliance/apache/configuration_mapping.json b/configs/compliance/apache/configuration_mapping.json deleted file mode 100644 index 01f3c15..0000000 --- a/configs/compliance/apache/configuration_mapping.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "SSLProtocol": "Protocol", - "SSLCipherSuite": "CipherSuite", - "SSLSessionTickets": {"Extension": "session_ticket"}, - "SSLUseStapling": {"Extension": "status_request"} -} \ No newline at end of file diff --git a/configs/compliance/apache/mapping.json b/configs/compliance/apache/mapping.json new file mode 100644 index 0000000..ef05166 --- /dev/null +++ b/configs/compliance/apache/mapping.json @@ -0,0 +1,6 @@ +{ + "ProtocolList": "SSLProtocol", + "CipherSuiteList": "SSLCipherSuite", + "SessionTickets": "SSLSessionTickets", + "Stapling": "SSLUseStapling" +} \ No newline at end of file diff --git a/configs/compliance/apache/rules.json b/configs/compliance/apache/rules.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/configs/compliance/apache/rules.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/configs/compliance/apache/template_apache.conf b/configs/compliance/apache/template.conf similarity index 100% rename from configs/compliance/apache/template_apache.conf rename to configs/compliance/apache/template.conf diff --git a/configs/compliance/generate/configuration_mapping.json b/configs/compliance/generate/configuration_mapping.json new file mode 100644 index 0000000..6251d50 --- /dev/null +++ b/configs/compliance/generate/configuration_mapping.json @@ -0,0 +1,6 @@ +{ + "ProtocolList": "Protocol", + "CipherSuiteList": "CipherSuite", + "SessionTickets": {"Extension": "session_ticket"}, + "Stapling": {"Extension": "status_request"} +} \ No newline at end of file diff --git a/configs/compliance/apache/configuration_rules.json b/configs/compliance/generate/configuration_rules.json similarity index 83% rename from configs/compliance/apache/configuration_rules.json rename to configs/compliance/generate/configuration_rules.json index 6447706..73ced2b 100644 --- a/configs/compliance/apache/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -1,5 +1,5 @@ { - "SSLProtocol": { + "ProtocolList": { "enable": "name", "disable": "-name", "separator": " ", @@ -9,7 +9,7 @@ ".0": "" } }, - "SSLCipherSuite": { + "CipherSuiteList": { "enable": "name", "disable": "", "separator": ":", @@ -19,12 +19,12 @@ "TLS-": "" } }, - "SSLSessionTickets": { + "SessionTickets": { "enable": "on", "disable": "off", "separator": "" }, - "SSLUseStapling": { + "Stapling": { "enable": "on", "disable": "off", "separator": "" diff --git a/modules/compliance/requirements.db b/modules/compliance/__init__.py similarity index 100% rename from modules/compliance/requirements.db rename to modules/compliance/__init__.py diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 3a96340..041e0e7 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,8 +1,11 @@ import json +from modules.compliance.configuration.apache_configuration import ApacheConfiguration +from modules.compliance.configuration.nginx_configuration import NginxConfiguration from modules.compliance.wrappers.db_reader import Database from modules.server.wrappers.testssl import Testssl from utils.loader import load_configuration +from utils.logger import Logger from utils.validation import Validator @@ -26,11 +29,10 @@ def solve(self): class Compliance: def __init__(self): - self._config_template = "configs/compliance/apache/template_apache.conf" self._apache = True - self._config_output = None self._input_dict = {} self._database_instance = Database() + self.__logging = Logger("Compliance module") self._last_data = {} self._output_dict = {} self._user_configuration = {} @@ -39,9 +41,9 @@ def __init__(self): self.evaluations_mapping = load_configuration("evaluations_mapping", "configs/compliance/") self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") self.misc_fields = load_configuration("misc_fields", "configs/compliance/") - self.configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/apache/") self._validator = Validator() self.test_ssl = Testssl() + self._config_class = None def evaluation_to_use(self, evaluations, security: bool = True): """ @@ -77,35 +79,43 @@ def input(self, **kwargs): :Keyword Arguments: * *standard* (``list``) -- Guidelines to check against * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol - * *actual_configuration* (``dict``) -- The configuration to check, not needed if generating - * *test_ssl* (``bool``) -- If true the user_configuration gets generated using testssl data + * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating + * *hostname* (``str``) -- Hostname on which testssl should be used * *config_template* (``str``) -- (Optional) the file that should be used as template * *apache* (``bool``) -- Default to True, if false nginx will be used * *config_output* (``str``) -- The path and name of the output file """ - actual_configuration = kwargs.get("actual_configuration") - use_test_ssl = kwargs.get("test_ssl") - input_file = kwargs.get("input_config") - use_apache = kwargs.get("apache") + actual_configuration = kwargs.get("actual_configuration_path") + hostname = kwargs.get("hostname") + input_template = kwargs.get("input_config") + self._apache = kwargs.get("apache", True) output_file = kwargs.get("output_config") - if actual_configuration and self._validator.dict(actual_configuration): - self.prepare_configuration(actual_configuration) - elif use_test_ssl: - # test_ssl_output = self.test_ssl.run(**{"hostname": "falconvendor.davita.com"}) - # with open("dump.json", "w") as f: - # json.dump(test_ssl_output, f, indent=4) + if actual_configuration and self._validator.string(actual_configuration): + try: + self._config_class = ApacheConfiguration(actual_configuration) + except Exception as e: + self.__logging.debug( + f"Couldn't parse config as apache: {e}\ntrying with nginx..." + ) + self._config_class = NginxConfiguration(actual_configuration) + self.prepare_configuration(self._config_class.configuration) + elif hostname and self._validator.string(hostname): + # test_ssl_output = self.test_ssl.run(**{"hostname": hostname}) # this is temporary with open("testssl_dump.json", 'r') as f: test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) elif output_file and self._validator.string(output_file): - self._config_output = output_file # This two parameters are needed only for the configuration generation - if input_file and self._validator.string(input_file): - self._config_template = input_file - if use_apache and self._validator.bool(use_apache): - self._apache = use_apache + if self._apache: + self._config_class = ApacheConfiguration() + else: + self._config_class = NginxConfiguration() + if input_template and self._validator.string(input_template): + self._config_class.set_template(input_template) + self._config_class.set_out_file(output_file) + self._input_dict = kwargs # To override @@ -147,7 +157,7 @@ def prepare_configuration(self, actual_configuration): new_version_name += ".0" tmp_dict[new_version_name] = accepted new_field = tmp_dict - field_name = self.configuration_mapping.get(field, field) + field_name = self._config_class.reverse_mapping.get(field, field) self._user_configuration[field_name] = new_field def prepare_testssl_output(self, test_ssl_output): @@ -373,9 +383,11 @@ def _evaluate_entries(self, sheets_to_check, level_index): class Generator(Compliance): """This class only exists to add fields that are needed by the generator to the Compliance class""" + def __init__(self): super().__init__() - self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/apache") + self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/generate/") + self._configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/generate/") # To override def _worker(self, sheets_to_check): @@ -388,4 +400,4 @@ def _worker(self, sheets_to_check): :raise NotImplementedError: """ - raise NotImplementedError("This method should be reimplemented") \ No newline at end of file + raise NotImplementedError("This method should be reimplemented") diff --git a/modules/compliance/configuration/__init__.py b/modules/compliance/configuration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py new file mode 100644 index 0000000..be10a96 --- /dev/null +++ b/modules/compliance/configuration/apache_configuration.py @@ -0,0 +1,65 @@ +from pathlib import Path + +from apacheconfig import make_loader + +from modules.compliance.configuration.configuration_base import ConfigurationMaker + + +class ApacheConfiguration(ConfigurationMaker): + def __init__(self, file: Path = None): + super().__init__("apache") + if file: + self.__load_conf(file) + + # Stole this function from Configuration for testing purposes + def __load_conf(self, file: Path): + """ + Internal method to load the apache configuration file. + + :param file: path to the configuration file + :type file: str + """ + with make_loader() as loader: + self.configuration = loader.load(str(file.absolute())) + + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + config_field = self.mapping.get(field, None) + self._output_dict[field] = {} + + if not config_field: + # This field isn't available with this configuration + return + + tmp_string = config_field + " " + field_rules = self._specific_rules.get(field, field_rules) + # the idea is that it is possible to define a custom value to insert like on/off or name to use the name + # defined in the config file + allow_string = field_rules.get("enable", "name") + deny_string = field_rules.get("disable", "-name") + separator = field_rules.get("separator", " ") + # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) + added_negatives = field_rules.get("added_negatives", False) + replacements = field_rules.get("replacements", []) + for entry in data: + added = True + name = entry[name_index] + for replacement in replacements: + name = name.replace(replacement, replacements[replacement]) + if entry[level_index] in ["must", "recommended"]: + tmp_string += allow_string.replace("name", name) + self._output_dict[field][name] = True + elif entry[level_index] in ["must not", "not recommended"]: + tmp_string += deny_string.replace("name", name) + added = added_negatives + self._output_dict[field][name] = False + else: + added = False + self._output_dict[field][name] = False + + if added: + tmp_string += separator + + if tmp_string and tmp_string[-1] == ":": + tmp_string = tmp_string[:-1] + if len(tmp_string) != len(config_field) + 1: # this is to prevent adding a field without any value + self._string_to_add += "\n" + tmp_string diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py new file mode 100644 index 0000000..2ce51a5 --- /dev/null +++ b/modules/compliance/configuration/configuration_base.py @@ -0,0 +1,50 @@ +import os + +from utils.loader import load_configuration + + +class ConfigurationMaker: + def __init__(self, config_type): + self.mapping = load_configuration("mapping", f"configs/compliance/{config_type}/") + self.reverse_mapping = dict((v, k) for k, v in self.mapping.items()) + self._output_dict = {} + self._string_to_add = "" + self._config_template = f"configs/compliance/{config_type}/template.conf" + self._config_output = None + self.configuration = None + self._specific_rules = load_configuration("rules", f"configs/compliance/{config_type}/") + + def output_file(self): + return self._config_output + + def set_template(self, path): + self._config_template = path + + def set_out_file(self, path): + self._config_output = path + + def _load_template(self): + if not os.path.isfile(self._config_template): + raise FileNotFoundError("Invalid template file") + with open(self._config_template, "r") as f: + return f.read() + + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + """ + :param field: the field that should be added (taken from configuration_rules) + :param field_rules: + :param data: + :param name_index: + :param level_index: + :return: + """ + raise NotImplementedError("This method should be reimplemented") + + def write_to_file(self): + """ + Loads the template, adds the new text and writes the result to the output_file. + :return: a dictionary containing a report of what was added and what not + """ + with open(self._config_output, "w") as f: + f.write(self._load_template() + "\n" + self._string_to_add) + return self._output_dict.copy() diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py new file mode 100644 index 0000000..32e2092 --- /dev/null +++ b/modules/compliance/configuration/nginx_configuration.py @@ -0,0 +1,72 @@ +from pathlib import Path + +from crossplane import parse as nginx_parse + +from modules.compliance.configuration.configuration_base import ConfigurationMaker + + +class NginxConfiguration(ConfigurationMaker): + def __init__(self, file: Path = None): + super().__init__("nginx") + if file: + self.__load_conf(file) + + # Stole this function from Configuration for testing purposes + def __load_conf(self, file: Path): + """ + Internal method to load the nginx configuration file. + + :param file: path to the configuration file + :type file: str + """ + self.configuration = nginx_parse(str(file.absolute())) + + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + config_field = self.mapping.get(field, None) + self._output_dict[field] = {} + if not config_field: + # This field isn't available with this configuration + return + tmp_string = "\t" + config_field + " " + field_rules = self._specific_rules.get(field, field_rules) + # the idea is that it is possible to define a custom value to insert like on/off or name to use the name + # defined in the config file + allow_string = field_rules.get("enable", "name") + deny_string = field_rules.get("disable", "-name") + separator = field_rules.get("separator", " ") + # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) + added_negatives = field_rules.get("added_negatives", False) + replacements = field_rules.get("replacements", []) + for entry in data: + added = True + name = entry[name_index] + for replacement in replacements: + name = name.replace(replacement, replacements[replacement]) + if entry[level_index] in ["must", "recommended"]: + tmp_string += allow_string.replace("name", name) + self._output_dict[field][name] = True + elif entry[level_index] in ["must not", "not recommended"]: + tmp_string += deny_string.replace("name", name) + added = added_negatives + self._output_dict[field][name] = False + else: + added = False + self._output_dict[field][name] = False + + if added: + tmp_string += separator + + if tmp_string and tmp_string[-1] == ":": + tmp_string = tmp_string[:-1] + if len(tmp_string) != len(config_field) + 1: # this is to prevent adding a field without any value + self._string_to_add += "\n" + tmp_string + + def write_to_file(self): + """ + Loads the template, adds the new text and writes the result to the output_file. + This one will also add a final "}" so that user doesn't need to move all the directives inside the server block. + :return: a dictionary containing a report of what was added and what not + """ + with open(self._config_output, "w") as f: + f.write(self._load_template() + "\n" + self._string_to_add + "}") + return self._output_dict.copy() diff --git a/modules/compliance/generate_many.py b/modules/compliance/generate_many.py index aabf8f5..907c99b 100644 --- a/modules/compliance/generate_many.py +++ b/modules/compliance/generate_many.py @@ -3,8 +3,6 @@ class GenerateMany(Generator): def _worker(self, sheets_to_check): - if not self._config_output: - raise ValueError("No output file path provided") columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index bb2633c..3c59487 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -5,17 +5,15 @@ class GenerateOne(Generator): def _worker(self, sheets_to_check): - if not self._config_output: + if not self._config_class.output_file(): raise ValueError("No output file path provided") - string_to_add = "" columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") - conf_mapping = self.configuration_mapping + conf_mapping = self._configuration_mapping for field in conf_mapping: if not self._output_dict.get(field): self._output_dict[field] = {} - tmp_string = field + " " sheet = conf_mapping[field] query_filter = "" # Dictionaries are used for specific things like a directive that enables an extension for this reason it is @@ -32,40 +30,8 @@ def _worker(self, sheets_to_check): self._database_instance.input([table_name], other_filter=query_filter) data = self._database_instance.output(columns) field_rules = self._configuration_rules.get(field, {}) - # the idea is that it is possible to define a custom value to insert like on/off or name to use the name - # defined in the config file - allow_string = field_rules.get("enable", "name") - deny_string = field_rules.get("disable", "-name") - separator = field_rules.get("separator", " ") - # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) - added_negatives = field_rules.get("added_negatives", False) - replacements = field_rules.get("replacements", []) - for entry in data: - added = True - name = entry[name_index] - for replacement in replacements: - name = name.replace(replacement, replacements[replacement]) - if entry[level_index] in ["must", "recommended"]: - tmp_string += allow_string.replace("name", name) - self._output_dict[field][name] = True - elif entry[level_index] in ["must not", "not recommended"]: - tmp_string += deny_string.replace("name", name) - added = added_negatives - self._output_dict[field][name] = False - else: - added = False - self._output_dict[field][name] = False + self._config_class.add_configuration_for_field(field, field_rules, data, name_index, level_index) - if added: - tmp_string += separator + def output(self): + return self._config_class.write_to_file() - if tmp_string and tmp_string[-1] == ":": - tmp_string = tmp_string[:-1] - if len(tmp_string) != len(field) + 1: - string_to_add += "\n" + tmp_string - if not os.path.isfile(self._config_template): - raise FileNotFoundError("Invalid template file") - with open(self._config_template, "r") as f: - base_conf = f.read() - with open(self._config_output, "w") as f: - f.write(base_conf + "\n" + string_to_add) From e28a359fb8486c66fc16c094e03297f90a55b3b5 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 22 Mar 2023 14:54:58 +0100 Subject: [PATCH 066/209] Reorganized the configuration system, now both a configuration and a template can be loaded. Removed custom template support. Started adding support for nginx. --- .../generate/configuration_rules.json | 2 +- configs/compliance/nginx/mapping.json | 6 +++ configs/compliance/nginx/rules.json | 1 + configs/compliance/nginx/template.conf | 14 +++++ modules/compliance/compliance_base.py | 12 ++--- .../configuration/apache_configuration.py | 18 ++++++- .../configuration/configuration_base.py | 52 ++++++++++++------- .../configuration/nginx_configuration.py | 35 ++++++++----- modules/compliance/generate_one.py | 5 -- 9 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 configs/compliance/nginx/mapping.json create mode 100644 configs/compliance/nginx/rules.json create mode 100644 configs/compliance/nginx/template.conf diff --git a/configs/compliance/generate/configuration_rules.json b/configs/compliance/generate/configuration_rules.json index 73ced2b..f331d0f 100644 --- a/configs/compliance/generate/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -11,7 +11,7 @@ }, "CipherSuiteList": { "enable": "name", - "disable": "", + "disable": "!name", "separator": ":", "added_negatives": false, "replacements": { diff --git a/configs/compliance/nginx/mapping.json b/configs/compliance/nginx/mapping.json new file mode 100644 index 0000000..6fb6a17 --- /dev/null +++ b/configs/compliance/nginx/mapping.json @@ -0,0 +1,6 @@ +{ + "ProtocolList": "ssl_protocols", + "CipherSuiteList": "ssl_ciphers", + "SessionTickets": "ssl_session_tickets", + "Stapling": "ssl_stapling" +} \ No newline at end of file diff --git a/configs/compliance/nginx/rules.json b/configs/compliance/nginx/rules.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/configs/compliance/nginx/rules.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/configs/compliance/nginx/template.conf b/configs/compliance/nginx/template.conf new file mode 100644 index 0000000..494f10f --- /dev/null +++ b/configs/compliance/nginx/template.conf @@ -0,0 +1,14 @@ +events { + worker_connections 1024; +} + +http { + ssl_prefer_server_ciphers off; + server { + listen 443 ssl http2; + + location / { + root html; + } + } +} diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 041e0e7..dbf6c87 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,4 +1,5 @@ import json +from pathlib import Path from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration @@ -81,13 +82,11 @@ def input(self, **kwargs): * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used - * *config_template* (``str``) -- (Optional) the file that should be used as template * *apache* (``bool``) -- Default to True, if false nginx will be used * *config_output* (``str``) -- The path and name of the output file """ actual_configuration = kwargs.get("actual_configuration_path") hostname = kwargs.get("hostname") - input_template = kwargs.get("input_config") self._apache = kwargs.get("apache", True) output_file = kwargs.get("output_config") if actual_configuration and self._validator.string(actual_configuration): @@ -107,14 +106,11 @@ def input(self, **kwargs): test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) elif output_file and self._validator.string(output_file): - # This two parameters are needed only for the configuration generation if self._apache: self._config_class = ApacheConfiguration() else: self._config_class = NginxConfiguration() - if input_template and self._validator.string(input_template): - self._config_class.set_template(input_template) - self._config_class.set_out_file(output_file) + self._config_class.set_out_file(Path(output_file)) self._input_dict = kwargs @@ -401,3 +397,7 @@ def _worker(self, sheets_to_check): :raise NotImplementedError: """ raise NotImplementedError("This method should be reimplemented") + + def output(self): + return self._config_class.configuration_output() + diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index be10a96..cbdabfd 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from apacheconfig import make_loader @@ -6,13 +7,15 @@ class ApacheConfiguration(ConfigurationMaker): + def __init__(self, file: Path = None): super().__init__("apache") + self._string_to_add = "" if file: - self.__load_conf(file) + self._load_conf(file) # Stole this function from Configuration for testing purposes - def __load_conf(self, file: Path): + def _load_conf(self, file: Path): """ Internal method to load the apache configuration file. @@ -22,6 +25,10 @@ def __load_conf(self, file: Path): with make_loader() as loader: self.configuration = loader.load(str(file.absolute())) + def _load_template(self): + with open(self._config_template_path, "r") as f: + self._template = f.read() + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): config_field = self.mapping.get(field, None) self._output_dict[field] = {} @@ -63,3 +70,10 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = tmp_string[:-1] if len(tmp_string) != len(config_field) + 1: # this is to prevent adding a field without any value self._string_to_add += "\n" + tmp_string + + def _write_to_file(self): + if not os.path.isfile(self._config_template_path): + raise FileNotFoundError("Invalid template file") + + with open(self._config_output, "w") as f: + f.write(self._template + self._string_to_add) diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py index 2ce51a5..bf944ef 100644 --- a/modules/compliance/configuration/configuration_base.py +++ b/modules/compliance/configuration/configuration_base.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path from utils.loader import load_configuration @@ -7,27 +7,44 @@ class ConfigurationMaker: def __init__(self, config_type): self.mapping = load_configuration("mapping", f"configs/compliance/{config_type}/") self.reverse_mapping = dict((v, k) for k, v in self.mapping.items()) - self._output_dict = {} - self._string_to_add = "" - self._config_template = f"configs/compliance/{config_type}/template.conf" + self._output_dict = {"configuration": config_type} + self._config_template_path = f"configs/compliance/{config_type}/template.conf" + self._template = None self._config_output = None self.configuration = None self._specific_rules = load_configuration("rules", f"configs/compliance/{config_type}/") + def set_out_file(self, output_file): + """ + Used to set the output file for the config generator, if this function is called it also loads the template. + :param output_file: Output file path + :type output_file: Path + """ + self._load_template() + self._config_output = output_file + def output_file(self): + """ + This function returns the value of the _config_output field. + :return: The path at which the configuration will be saved + """ return self._config_output - def set_template(self, path): - self._config_template = path - - def set_out_file(self, path): - self._config_output = path + def _load_conf(self, file: Path): + """ + This method loads a configuration from the given path. + :param file: Path to the configuration + """ + raise NotImplementedError("This method should be reimplemented") def _load_template(self): - if not os.path.isfile(self._config_template): - raise FileNotFoundError("Invalid template file") - with open(self._config_template, "r") as f: - return f.read() + """ + This method loads the template in the instance. (Needed only for generation) + """ + raise NotImplementedError("This method should be reimplemented") + + def _write_to_file(self): + raise NotImplementedError("This method should be reimplemented") def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): """ @@ -40,11 +57,6 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve """ raise NotImplementedError("This method should be reimplemented") - def write_to_file(self): - """ - Loads the template, adds the new text and writes the result to the output_file. - :return: a dictionary containing a report of what was added and what not - """ - with open(self._config_output, "w") as f: - f.write(self._load_template() + "\n" + self._string_to_add) + def configuration_output(self): + self._write_to_file() return self._output_dict.copy() diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 32e2092..da819aa 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -1,6 +1,8 @@ +import os from pathlib import Path from crossplane import parse as nginx_parse +from crossplane import build as nginx_build from modules.compliance.configuration.configuration_base import ConfigurationMaker @@ -9,10 +11,10 @@ class NginxConfiguration(ConfigurationMaker): def __init__(self, file: Path = None): super().__init__("nginx") if file: - self.__load_conf(file) + self._load_conf(file) # Stole this function from Configuration for testing purposes - def __load_conf(self, file: Path): + def _load_conf(self, file: Path): """ Internal method to load the nginx configuration file. @@ -20,6 +22,8 @@ def __load_conf(self, file: Path): :type file: str """ self.configuration = nginx_parse(str(file.absolute())) + if self.configuration.get("errors", []): + raise ValueError("Invalid nginx config file") def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): config_field = self.mapping.get(field, None) @@ -27,7 +31,8 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve if not config_field: # This field isn't available with this configuration return - tmp_string = "\t" + config_field + " " + + tmp_string = "" field_rules = self._specific_rules.get(field, field_rules) # the idea is that it is possible to define a custom value to insert like on/off or name to use the name # defined in the config file @@ -58,15 +63,19 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] - if len(tmp_string) != len(config_field) + 1: # this is to prevent adding a field without any value - self._string_to_add += "\n" + tmp_string + tmp_string = tmp_string.strip() + if tmp_string: # this is to prevent adding a field without any value + print(self._template) + # The directive gets added at the beginning the http directive + self._template["config"][0]["parsed"][1]["block"].insert(0, {"directive": field, "args": [tmp_string]}) + + def _load_template(self): + self._load_conf(Path(self._config_template_path)) + self._template = self.configuration + + def _write_to_file(self): + if not os.path.isfile(self._config_template_path): + raise FileNotFoundError("Invalid template file") - def write_to_file(self): - """ - Loads the template, adds the new text and writes the result to the output_file. - This one will also add a final "}" so that user doesn't need to move all the directives inside the server block. - :return: a dictionary containing a report of what was added and what not - """ with open(self._config_output, "w") as f: - f.write(self._load_template() + "\n" + self._string_to_add + "}") - return self._output_dict.copy() + f.write(nginx_build(self._template["config"][0]["parsed"], header=True)) diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 3c59487..7e7ea7c 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -1,5 +1,3 @@ -import os.path - from modules.compliance.compliance_base import Generator @@ -32,6 +30,3 @@ def _worker(self, sheets_to_check): field_rules = self._configuration_rules.get(field, {}) self._config_class.add_configuration_for_field(field, field_rules, data, name_index, level_index) - def output(self): - return self._config_class.write_to_file() - From 5209ed005b37b33e67d9ae67f9eed7c0ffcc6933 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 22 Mar 2023 15:55:34 +0100 Subject: [PATCH 067/209] Improved nginx configuration template and added EarlyData extension to configuration generator --- .../compliance/generate/configuration_mapping.json | 3 ++- configs/compliance/generate/configuration_rules.json | 5 +++++ configs/compliance/nginx/mapping.json | 3 ++- configs/compliance/nginx/template.conf | 3 +++ .../compliance/configuration/nginx_configuration.py | 12 ++++++++++-- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/configs/compliance/generate/configuration_mapping.json b/configs/compliance/generate/configuration_mapping.json index 6251d50..d8ab9c7 100644 --- a/configs/compliance/generate/configuration_mapping.json +++ b/configs/compliance/generate/configuration_mapping.json @@ -2,5 +2,6 @@ "ProtocolList": "Protocol", "CipherSuiteList": "CipherSuite", "SessionTickets": {"Extension": "session_ticket"}, - "Stapling": {"Extension": "status_request"} + "Stapling": {"Extension": "status_request"}, + "EarlyData": {"Extension": "early_data"} } \ No newline at end of file diff --git a/configs/compliance/generate/configuration_rules.json b/configs/compliance/generate/configuration_rules.json index f331d0f..7f7bb7e 100644 --- a/configs/compliance/generate/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -28,5 +28,10 @@ "enable": "on", "disable": "off", "separator": "" + }, + "EarlyData": { + "enable": "on", + "disable": "off", + "separator": "" } } \ No newline at end of file diff --git a/configs/compliance/nginx/mapping.json b/configs/compliance/nginx/mapping.json index 6fb6a17..4cab4fe 100644 --- a/configs/compliance/nginx/mapping.json +++ b/configs/compliance/nginx/mapping.json @@ -2,5 +2,6 @@ "ProtocolList": "ssl_protocols", "CipherSuiteList": "ssl_ciphers", "SessionTickets": "ssl_session_tickets", - "Stapling": "ssl_stapling" + "Stapling": "ssl_stapling", + "EarlyData": "ssl_early_data" } \ No newline at end of file diff --git a/configs/compliance/nginx/template.conf b/configs/compliance/nginx/template.conf index 494f10f..0d91b85 100644 --- a/configs/compliance/nginx/template.conf +++ b/configs/compliance/nginx/template.conf @@ -4,6 +4,9 @@ events { http { ssl_prefer_server_ciphers off; + ssl_certificate /path/to/cert_chain; + ssl_certificate_key /path/to/private_key; + ssl_dhparam /path/to/dhparam; server { listen 443 ssl http2; diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index da819aa..b9e1096 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -28,6 +28,7 @@ def _load_conf(self, file: Path): def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): config_field = self.mapping.get(field, None) self._output_dict[field] = {} + if not config_field: # This field isn't available with this configuration return @@ -65,9 +66,16 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = tmp_string[:-1] tmp_string = tmp_string.strip() if tmp_string: # this is to prevent adding a field without any value - print(self._template) # The directive gets added at the beginning the http directive - self._template["config"][0]["parsed"][1]["block"].insert(0, {"directive": field, "args": [tmp_string]}) + # the breakdown of the below instruction is: + # loaded_template: dictionary + # config: list of loaded files (in this case one) + # parsed: list of dictionaries that represent directives (1 is the http directive) + # block: list of dictionaries that represent directives inside the directive got before + # each directive has a directive field for the name and an args (list) one for the params it should have + # The args value is a list only containing tmp_string because the params are prepared while reading them. + directive_to_add = {"directive": config_field, "args": [tmp_string]} + self._template["config"][0]["parsed"][1]["block"].insert(0, directive_to_add) def _load_template(self): self._load_conf(Path(self._config_template_path)) From f82509fc9000fae71fc525a699048b20f27d805c Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 23 Mar 2023 10:22:56 +0100 Subject: [PATCH 068/209] Moved configuration string generation to configuration_base.py. Added generate_many. Added source guideline to generator output --- .../generate/configuration_rules.json | 2 +- .../configuration/apache_configuration.py | 44 +++++++--------- .../configuration/configuration_base.py | 50 +++++++++++++++++-- .../configuration/nginx_configuration.py | 40 ++++++--------- modules/compliance/generate_many.py | 21 ++++++++ modules/compliance/generate_one.py | 2 +- utils/database.py | 10 ++++ 7 files changed, 112 insertions(+), 57 deletions(-) diff --git a/configs/compliance/generate/configuration_rules.json b/configs/compliance/generate/configuration_rules.json index 7f7bb7e..15e9b58 100644 --- a/configs/compliance/generate/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -13,7 +13,7 @@ "enable": "name", "disable": "!name", "separator": ":", - "added_negatives": false, + "added_negatives": true, "replacements": { "_": "-", "TLS-": "" diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index cbdabfd..0ada2ec 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -29,7 +29,7 @@ def _load_template(self): with open(self._config_template_path, "r") as f: self._template = f.read() - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): config_field = self.mapping.get(field, None) self._output_dict[field] = {} @@ -39,36 +39,30 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = config_field + " " field_rules = self._specific_rules.get(field, field_rules) - # the idea is that it is possible to define a custom value to insert like on/off or name to use the name - # defined in the config file - allow_string = field_rules.get("enable", "name") - deny_string = field_rules.get("disable", "-name") - separator = field_rules.get("separator", " ") - # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) - added_negatives = field_rules.get("added_negatives", False) - replacements = field_rules.get("replacements", []) + + field_rules = self._specific_rules.get(field, field_rules) for entry in data: - added = True - name = entry[name_index] - for replacement in replacements: - name = name.replace(replacement, replacements[replacement]) - if entry[level_index] in ["must", "recommended"]: - tmp_string += allow_string.replace("name", name) - self._output_dict[field][name] = True - elif entry[level_index] in ["must not", "not recommended"]: - tmp_string += deny_string.replace("name", name) - added = added_negatives - self._output_dict[field][name] = False + if isinstance(entry, dict): + name = entry["entry"][name_index] + level = entry["level"] + guideline = entry["source"] else: - added = False - self._output_dict[field][name] = False + name = entry[name_index] + level = entry[level_index] - if added: - tmp_string += separator + if target and target != name: + continue + + replacements = field_rules.get("replacements", []) + for replacement in replacements: + name = name.replace(replacement, replacements[replacement]) + tmp_string += self._get_string_to_add(field_rules, name, level, field) + self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] - if len(tmp_string) != len(config_field) + 1: # this is to prevent adding a field without any value + # this check prevents adding a field without any value + if len(tmp_string) != len(config_field) + 1: self._string_to_add += "\n" + tmp_string def _write_to_file(self): diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py index bf944ef..ffa8995 100644 --- a/modules/compliance/configuration/configuration_base.py +++ b/modules/compliance/configuration/configuration_base.py @@ -1,5 +1,6 @@ from pathlib import Path +from utils.database import get_standardized_level from utils.loader import load_configuration @@ -46,13 +47,15 @@ def _load_template(self): def _write_to_file(self): raise NotImplementedError("This method should be reimplemented") - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): """ :param field: the field that should be added (taken from configuration_rules) - :param field_rules: - :param data: - :param name_index: - :param level_index: + :param field_rules: the rules that should be applied to that field + :param data: data from which to gather the field information + :param name_index: index of the name column + :param level_index: index of the level column + :param guideline: the guideline from which the level was deducted + :param target: (Optional) if defined only the entry with target name will be used :return: """ raise NotImplementedError("This method should be reimplemented") @@ -60,3 +63,40 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve def configuration_output(self): self._write_to_file() return self._output_dict.copy() + + def _get_string_to_add(self, field_rules, name, level, field): + """ + :param field_rules: set of rules that should be used for this field + :type field_rules: dict + :param name: name of the element to evaluate + :type name: str + :param level: level associated with the name + :type level: str + :param field: Name of the field in the configuration file + :type field: str + :return: The string that should be added to the configuration + :rtype: str + """ + string_to_add = "" + added = True + allow_string = field_rules.get("enable", "name") + deny_string = field_rules.get("disable", "-name") + separator = field_rules.get("separator", " ") + # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) + added_negatives = field_rules.get("added_negatives", False) + if not self._output_dict.get(field): + self._output_dict[field] = {} + if get_standardized_level(level) in ["must", "recommended"]: + string_to_add += allow_string.replace("name", name) + self._output_dict[field][name] = {"added": True} + elif get_standardized_level(level) in ["must not", "not recommended"]: + string_to_add += deny_string.replace("name", name) + added = added_negatives + self._output_dict[field][name] = {"added": False} + else: + added = False + self._output_dict[field][name] = {"added": False} + if added: + string_to_add += separator + + return string_to_add diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index b9e1096..c0689b0 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -1,8 +1,8 @@ import os from pathlib import Path -from crossplane import parse as nginx_parse from crossplane import build as nginx_build +from crossplane import parse as nginx_parse from modules.compliance.configuration.configuration_base import ConfigurationMaker @@ -25,7 +25,7 @@ def _load_conf(self, file: Path): if self.configuration.get("errors", []): raise ValueError("Invalid nginx config file") - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index): + def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): config_field = self.mapping.get(field, None) self._output_dict[field] = {} @@ -35,32 +35,22 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = "" field_rules = self._specific_rules.get(field, field_rules) - # the idea is that it is possible to define a custom value to insert like on/off or name to use the name - # defined in the config file - allow_string = field_rules.get("enable", "name") - deny_string = field_rules.get("disable", "-name") - separator = field_rules.get("separator", " ") - # This parameter is needed to avoid having separators even if nothing gets added to deny (like ciphersuites) - added_negatives = field_rules.get("added_negatives", False) - replacements = field_rules.get("replacements", []) for entry in data: - added = True - name = entry[name_index] - for replacement in replacements: - name = name.replace(replacement, replacements[replacement]) - if entry[level_index] in ["must", "recommended"]: - tmp_string += allow_string.replace("name", name) - self._output_dict[field][name] = True - elif entry[level_index] in ["must not", "not recommended"]: - tmp_string += deny_string.replace("name", name) - added = added_negatives - self._output_dict[field][name] = False + if isinstance(entry, dict): + name = entry["entry"][name_index] + level = entry["level"] + guideline = entry["source"] else: - added = False - self._output_dict[field][name] = False + name = entry[name_index] + level = entry[level_index] - if added: - tmp_string += separator + if target and target != name: + continue + + replacements = field_rules.get("replacements", []) + for replacement in replacements: + name = name.replace(replacement, replacements[replacement]) + tmp_string += self._get_string_to_add(field_rules, name, level, field) if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] diff --git a/modules/compliance/generate_many.py b/modules/compliance/generate_many.py index 907c99b..b4b853b 100644 --- a/modules/compliance/generate_many.py +++ b/modules/compliance/generate_many.py @@ -3,8 +3,29 @@ class GenerateMany(Generator): def _worker(self, sheets_to_check): + if not self._config_class.output_file(): + raise ValueError("No output file path provided") columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") + conf_mapping = self._configuration_mapping # fill the entries field with the data from the sheets self._retrieve_entries(sheets_to_check, columns) + self._evaluate_entries(sheets_to_check, level_index) + for field in conf_mapping: + if not self._output_dict.get(field): + self._output_dict[field] = {} + sheet = conf_mapping[field] + target = None + # Dictionaries are used for specific things like a directive that enables an extension for this reason it is + # used a filter on the query to get that specific thing by name + if isinstance(sheet, dict): + table_to_search = list(sheet.keys())[0] + # Since for generate_many the data are retrieved and processed before this block the filtering is + # postponed by using the target variable + target = sheet[table_to_search] + sheet = table_to_search + field_rules = self._configuration_rules.get(field, {}) + # the guideline here is defined as None because it will be defined in the function + self._config_class.add_configuration_for_field(field, field_rules, self.evaluated_entries[sheet].values(), + name_index, level_index, None, target) diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 7e7ea7c..950864b 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -5,7 +5,7 @@ class GenerateOne(Generator): def _worker(self, sheets_to_check): if not self._config_class.output_file(): raise ValueError("No output file path provided") - columns = ["name", "level", "condition", "guidelineName"] + columns = ["name", "level", "condition"] name_index = columns.index("name") level_index = columns.index("level") conf_mapping = self._configuration_mapping diff --git a/utils/database.py b/utils/database.py index 1653f17..a1274af 100644 --- a/utils/database.py +++ b/utils/database.py @@ -20,3 +20,13 @@ def get_standard_name_for_database(standard_name): elif len(tokens) > 2 and "/" in tokens[-1]: standard_name = tokens[0] + tokens[-1].replace("/", "_") return standard_name.strip(")") + + +def get_standardized_level(level): + """ + Takes a level in input and returns it after removing °,* and trailing spaces + :param level: + :type level: str + :return: + """ + return level.replace("*", "").replace("°", "").strip() From 0a9fdee8b654c393af7f80a52e040a0c50944679 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 23 Mar 2023 11:47:24 +0100 Subject: [PATCH 069/209] Made so that all standard and version names in the database are UPPER. Also added guideline source to nginx and generate_one --- modules/compliance/compliance_base.py | 9 +++++++-- .../configuration/nginx_configuration.py | 1 + modules/compliance/generate_one.py | 7 +++++-- requirements.db | Bin 1044480 -> 1052672 bytes utils/database.py | 4 ++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index dbf6c87..2cf9570 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -43,8 +43,13 @@ def __init__(self): self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") self.misc_fields = load_configuration("misc_fields", "configs/compliance/") self._validator = Validator() + + # This will be removed when integrating the module in the core self.test_ssl = Testssl() + self._config_class = None + self._database_instance.input(["Guideline"]) + self._guidelines = [name[0] for name in self._database_instance.output()] def evaluation_to_use(self, evaluations, security: bool = True): """ @@ -318,9 +323,9 @@ def _retrieve_entries(self, sheets_to_check, columns): entries = {} tables = [] for sheet in sheets_to_check: + if not self._output_dict.get(sheet): + self._output_dict[sheet] = {} for guideline in sheets_to_check[sheet]: - if not self._output_dict.get(sheet): - self._output_dict[sheet] = {} table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) tables.append(table_name) self._database_instance.input(tables, other_filter="ORDER BY name") diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index c0689b0..74d239a 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -51,6 +51,7 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve for replacement in replacements: name = name.replace(replacement, replacements[replacement]) tmp_string += self._get_string_to_add(field_rules, name, level, field) + self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 950864b..aff431f 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -5,9 +5,10 @@ class GenerateOne(Generator): def _worker(self, sheets_to_check): if not self._config_class.output_file(): raise ValueError("No output file path provided") - columns = ["name", "level", "condition"] + columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") level_index = columns.index("level") + guideline_index = columns.index("guidelineName") conf_mapping = self._configuration_mapping for field in conf_mapping: if not self._output_dict.get(field): @@ -25,8 +26,10 @@ def _worker(self, sheets_to_check): # Only the first guideline of each sheet is the interesting one guideline = list(sheets_to_check[sheet].keys())[0] table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + # Get the guideline name saved in the database for this field + guideline = guideline_index self._database_instance.input([table_name], other_filter=query_filter) data = self._database_instance.output(columns) field_rules = self._configuration_rules.get(field, {}) - self._config_class.add_configuration_for_field(field, field_rules, data, name_index, level_index) + self._config_class.add_configuration_for_field(field, field_rules, data, name_index, level_index, guideline) diff --git a/requirements.db b/requirements.db index e594cf4cb841a473cbd0e0891848adeea04b8cea..2cec82c1b70a89a9412605112fb76b2d9c9843c3 100644 GIT binary patch literal 1052672 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%0Dfl8oDCEN1V8`; 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 Ux8am=TJR>EFRm)S@ss8M1AmjllmGw# literal 1044480 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 diff --git a/utils/database.py b/utils/database.py index a1274af..eab1e42 100644 --- a/utils/database.py +++ b/utils/database.py @@ -3,7 +3,7 @@ def get_version_name_for_database(version_name: str): version_name = version_name if "Unnamed" not in version_name else "" version_name = version_name.strip().title().replace(" ", "").replace("-", "").replace("/", "_").replace("#", "") \ .strip(".") - return version_name + return version_name.upper() def get_standard_name_for_database(standard_name): @@ -19,7 +19,7 @@ def get_standard_name_for_database(standard_name): standard_name = tokens[0] elif len(tokens) > 2 and "/" in tokens[-1]: standard_name = tokens[0] + tokens[-1].replace("/", "_") - return standard_name.strip(")") + return standard_name.strip(")").upper() def get_standardized_level(level): From dee4dd290d405dfdbbde9f901f82e58aa1bf619b Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 23 Mar 2023 12:03:31 +0100 Subject: [PATCH 070/209] Renamed evaluation_to_use to level_to_use and fixed minor issue with compliance output --- modules/compliance/compliance_base.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 2cf9570..3165f44 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -5,6 +5,7 @@ from modules.compliance.configuration.nginx_configuration import NginxConfiguration from modules.compliance.wrappers.db_reader import Database from modules.server.wrappers.testssl import Testssl +from utils.database import get_standardized_level from utils.loader import load_configuration from utils.logger import Logger from utils.validation import Validator @@ -51,25 +52,25 @@ def __init__(self): self._database_instance.input(["Guideline"]) self._guidelines = [name[0] for name in self._database_instance.output()] - def evaluation_to_use(self, evaluations, security: bool = True): + def level_to_use(self, levels, security: bool = True): """ Given two evaluations returns true if the first one wins, false otherwise. - :param evaluations: list of evaluations to be checked - :type evaluations: list + :param levels: list of evaluations to be checked + :type levels: list :param security: True if security wins false if legacy wins, default to true :type security: bool :return: the standard which wins :rtype: int """ - # If an evaluation is not mapped it can be considered as a Not mentioned + # If a level is not mapped it can be considered as a Not mentioned security_mapping = "security" if security else "legacy" - if not evaluations: - raise IndexError("Evaluations list is empty") - first_value = self.evaluations_mapping.get(security_mapping, {}).get(evaluations[0].replace("°", ""), 4) + if not levels: + raise IndexError("Levels list is empty") + first_value = self.evaluations_mapping.get(security_mapping, {}).get(get_standardized_level(levels[0]), 4) best = 0 - for i, el in enumerate(evaluations[1:]): - evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(el.replace("°", ""), 4) + for i, el in enumerate(levels[1:]): + evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(get_standardized_level(el), 4) if first_value > evaluation_value: best = i + 1 # if they have the same value first wins @@ -281,6 +282,7 @@ def prepare_testssl_output(self, test_ssl_output): def update_result(self, sheet, name, entry_level, is_enabled, source): information_level = None action = None + entry_level = get_standardized_level(entry_level) if entry_level == "must" and not is_enabled: information_level = "ERROR" action = "has to be enabled" @@ -361,7 +363,7 @@ def _evaluate_entries(self, sheets_to_check, level_index): guideline = entry[-1] if entry_level != resulting_level: levels = [resulting_level, entry_level] - best_level = self.evaluation_to_use(levels) + best_level = self.level_to_use(levels) # if best_level is 0 the source_guideline is the same if best_level: source_guideline = guideline @@ -405,4 +407,3 @@ def _worker(self, sheets_to_check): def output(self): return self._config_class.configuration_output() - From 568b5c2b0cc2768e0178d1eecc1f0b7be0c61a63 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 23 Mar 2023 14:37:25 +0100 Subject: [PATCH 071/209] Added possibility for the user to define custom defined guidelines with custom rules --- modules/compliance/compliance_base.py | 47 ++++++++++++++++++++------- modules/compliance/compliance_many.py | 3 +- modules/compliance/generate_many.py | 2 +- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 3165f44..0277ce3 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -31,6 +31,7 @@ def solve(self): class Compliance: def __init__(self): + self._custom_guidelines = None self._apache = True self._input_dict = {} self._database_instance = Database() @@ -50,7 +51,7 @@ def __init__(self): self._config_class = None self._database_instance.input(["Guideline"]) - self._guidelines = [name[0] for name in self._database_instance.output()] + self._guidelines = [name[0].upper() for name in self._database_instance.output()] def level_to_use(self, levels, security: bool = True): """ @@ -85,16 +86,18 @@ def input(self, **kwargs): :Keyword Arguments: * *standard* (``list``) -- Guidelines to check against - * *sheets_to_check* (``dict``) -- of sheets that should be checked in the form: sheet:version_of_protocol + * *sheets_to_check* (``dict``) -- dictionary of sheets that should be checked in the form: sheet:version_of_protocol * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used * *apache* (``bool``) -- Default to True, if false nginx will be used * *config_output* (``str``) -- The path and name of the output file + * *custom_guidelines* (``dict``) -- dictionary with form: { sheet : {guideline: name: {"level":level}} """ actual_configuration = kwargs.get("actual_configuration_path") hostname = kwargs.get("hostname") self._apache = kwargs.get("apache", True) output_file = kwargs.get("output_config") + self._custom_guidelines = kwargs.get("custom_guidelines") if actual_configuration and self._validator.string(actual_configuration): try: self._config_class = ApacheConfiguration(actual_configuration) @@ -117,7 +120,6 @@ def input(self, **kwargs): else: self._config_class = NginxConfiguration() self._config_class.set_out_file(Path(output_file)) - self._input_dict = kwargs # To override @@ -305,6 +307,7 @@ def is_enabled(self, config_field, name, entry): field_value = self._user_configuration[config_field] enabled = False if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): + # Protocols case enabled = field_value.get(name, None) if enabled is None: enabled = True if "all" in field_value else False @@ -328,15 +331,17 @@ def _retrieve_entries(self, sheets_to_check, columns): if not self._output_dict.get(sheet): self._output_dict[sheet] = {} for guideline in sheets_to_check[sheet]: - table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) - tables.append(table_name) + if guideline.upper() in self._guidelines: + table_name = self._database_instance.get_table_name(sheet, guideline, + sheets_to_check[sheet][guideline]) + tables.append(table_name) self._database_instance.input(tables, other_filter="ORDER BY name") data = self._database_instance.output(columns) entries[sheet] = data tables = [] self.entries = entries - def _evaluate_entries(self, sheets_to_check, level_index): + def _evaluate_entries(self, sheets_to_check, columns): """ This function checks the entries with the same name and chooses which guideline to follow for that entry. The results can be found in the evaluated_entries field. The dictionary will have form: @@ -346,21 +351,24 @@ def _evaluate_entries(self, sheets_to_check, level_index): "source": str The guideline from which the level is deducted } :param sheets_to_check: The input dictionary - :param level_index: The index of the column that contains the requirement level of the entry - :type level_index: int + :param columns: columns used to retrieve data from database + :type columns: list """ # A more fitting name could be current_requirement_level resulting_level = "" + guideline_index = columns.index("guidelineName") + level_index = columns.index("level") + name_index = columns.index("name") for sheet in self.entries: # The total value is used as an index to avoid eventual collisions between equal names in the same sheet total = 0 if not self.evaluated_entries.get(sheet): self.evaluated_entries[sheet] = {} counter = 1 - source_guideline = self.entries[sheet][-1] + source_guideline = self.entries[sheet][guideline_index] for entry in self.entries[sheet]: entry_level = entry[level_index] - guideline = entry[-1] + guideline = entry[guideline_index] if entry_level != resulting_level: levels = [resulting_level, entry_level] best_level = self.level_to_use(levels) @@ -370,8 +378,25 @@ def _evaluate_entries(self, sheets_to_check, level_index): resulting_level = levels[best_level] # The entries are ordered by name so every time the counter is the same as the number of guidelines to # check it is time to add the entry to the output dictionary. - if sheet and counter == len(sheets_to_check[sheet]): + custom_guidelines_list = sheets_to_check[sheet].keys() - self._guidelines + if sheet and counter == len(sheets_to_check[sheet]) - len(custom_guidelines_list): counter = 0 + name = entry[name_index] + for guideline in custom_guidelines_list: + custom_entry = self._custom_guidelines[sheet][guideline].get(name) + if custom_entry: + levels = [resulting_level, custom_entry["level"]] + guidelines_to_check = list(sheets_to_check[sheet]) + # If the custom_guideline appears before the source_guideline (actual guideline from which + # the level was deducted) it has greater priority, so it is necessary to switch them + if guidelines_to_check.index(guideline) < guidelines_to_check.index(source_guideline): + levels = levels[::-1] + best_level = self.level_to_use(levels) + # if best_level is 0 the source_guideline is the best + if best_level: + source_guideline = guideline + resulting_level = levels[best_level] + # Save it to the dictionary self.evaluated_entries[sheet][total] = { "entry": entry, diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index a5003d1..ca5ae90 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -14,10 +14,9 @@ def _worker(self, sheets_to_check): raise ValueError("No configuration provided") columns = ["name", "level", "condition", "guidelineName"] name_index = columns.index("name") - level_index = columns.index("level") # fill the entries field with the data from the sheets self._retrieve_entries(sheets_to_check, columns) - self._evaluate_entries(sheets_to_check, level_index) + self._evaluate_entries(sheets_to_check, columns) for sheet in self.evaluated_entries: for entry_dict in self.evaluated_entries[sheet].values(): entry = entry_dict["entry"] diff --git a/modules/compliance/generate_many.py b/modules/compliance/generate_many.py index b4b853b..80a89c3 100644 --- a/modules/compliance/generate_many.py +++ b/modules/compliance/generate_many.py @@ -11,7 +11,7 @@ def _worker(self, sheets_to_check): conf_mapping = self._configuration_mapping # fill the entries field with the data from the sheets self._retrieve_entries(sheets_to_check, columns) - self._evaluate_entries(sheets_to_check, level_index) + self._evaluate_entries(sheets_to_check, columns) for field in conf_mapping: if not self._output_dict.get(field): self._output_dict[field] = {} From 285f4f0adbfd045bb393df29894990b2f974685a Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 24 Mar 2023 10:03:34 +0100 Subject: [PATCH 072/209] Added support for comments in Nginx rules, added enable_one_time rule field and added dhparams support --- configs/compliance/apache/mapping.json | 3 ++- configs/compliance/apache/rules.json | 9 ++++++++- .../compliance/generate/configuration_mapping.json | 3 ++- .../compliance/generate/configuration_rules.json | 6 ++++++ configs/compliance/nginx/mapping.json | 3 ++- configs/compliance/nginx/rules.json | 9 ++++++++- configs/compliance/nginx/template.conf | 1 - modules/compliance/compliance_base.py | 4 ++-- .../configuration/apache_configuration.py | 6 +++--- .../compliance/configuration/configuration_base.py | 8 +++++++- .../configuration/nginx_configuration.py | 14 +++++++++++--- modules/compliance/generate_one.py | 2 +- 12 files changed, 52 insertions(+), 16 deletions(-) diff --git a/configs/compliance/apache/mapping.json b/configs/compliance/apache/mapping.json index ef05166..80e490b 100644 --- a/configs/compliance/apache/mapping.json +++ b/configs/compliance/apache/mapping.json @@ -2,5 +2,6 @@ "ProtocolList": "SSLProtocol", "CipherSuiteList": "SSLCipherSuite", "SessionTickets": "SSLSessionTickets", - "Stapling": "SSLUseStapling" + "Stapling": "SSLUseStapling", + "DHParams": "# You should run openssl dhparam" } \ No newline at end of file diff --git a/configs/compliance/apache/rules.json b/configs/compliance/apache/rules.json index 9e26dfe..19016fc 100644 --- a/configs/compliance/apache/rules.json +++ b/configs/compliance/apache/rules.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "DHParams": { + "enable": "2048 >> /path/to/cert_chain", + "disable": "", + "separator": "", + "enable_one_time": true + } +} \ No newline at end of file diff --git a/configs/compliance/generate/configuration_mapping.json b/configs/compliance/generate/configuration_mapping.json index d8ab9c7..430e2cb 100644 --- a/configs/compliance/generate/configuration_mapping.json +++ b/configs/compliance/generate/configuration_mapping.json @@ -3,5 +3,6 @@ "CipherSuiteList": "CipherSuite", "SessionTickets": {"Extension": "session_ticket"}, "Stapling": {"Extension": "status_request"}, - "EarlyData": {"Extension": "early_data"} + "EarlyData": {"Extension": "early_data"}, + "DHParams": {"CipherSuite": "*DH*"} } \ No newline at end of file diff --git a/configs/compliance/generate/configuration_rules.json b/configs/compliance/generate/configuration_rules.json index 15e9b58..4c936ba 100644 --- a/configs/compliance/generate/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -33,5 +33,11 @@ "enable": "on", "disable": "off", "separator": "" + }, + "DHParams": { + "enable": "OVERRIDE THIS RULE", + "disable": "", + "separator": "", + "enable_one_time": true } } \ No newline at end of file diff --git a/configs/compliance/nginx/mapping.json b/configs/compliance/nginx/mapping.json index 4cab4fe..f813fe4 100644 --- a/configs/compliance/nginx/mapping.json +++ b/configs/compliance/nginx/mapping.json @@ -3,5 +3,6 @@ "CipherSuiteList": "ssl_ciphers", "SessionTickets": "ssl_session_tickets", "Stapling": "ssl_stapling", - "EarlyData": "ssl_early_data" + "EarlyData": "ssl_early_data", + "DHParams": "ssl_dhparam" } \ No newline at end of file diff --git a/configs/compliance/nginx/rules.json b/configs/compliance/nginx/rules.json index 9e26dfe..c250bfe 100644 --- a/configs/compliance/nginx/rules.json +++ b/configs/compliance/nginx/rules.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "DHParams": { + "enable": "/path/to/dhparam# To use the directive below you have to run openssl dhparam 2048 >> /path/to/dhparam", + "disable": "", + "separator": "", + "enable_one_time": true + } +} \ No newline at end of file diff --git a/configs/compliance/nginx/template.conf b/configs/compliance/nginx/template.conf index 0d91b85..9f149e7 100644 --- a/configs/compliance/nginx/template.conf +++ b/configs/compliance/nginx/template.conf @@ -6,7 +6,6 @@ http { ssl_prefer_server_ciphers off; ssl_certificate /path/to/cert_chain; ssl_certificate_key /path/to/private_key; - ssl_dhparam /path/to/dhparam; server { listen 443 ssl http2; diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 0277ce3..b812e81 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -107,14 +107,14 @@ def input(self, **kwargs): ) self._config_class = NginxConfiguration(actual_configuration) self.prepare_configuration(self._config_class.configuration) - elif hostname and self._validator.string(hostname): + if hostname and self._validator.string(hostname): # test_ssl_output = self.test_ssl.run(**{"hostname": hostname}) # this is temporary with open("testssl_dump.json", 'r') as f: test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) - elif output_file and self._validator.string(output_file): + if output_file and self._validator.string(output_file): if self._apache: self._config_class = ApacheConfiguration() else: diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index 0ada2ec..a2e814e 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -40,7 +40,6 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = config_field + " " field_rules = self._specific_rules.get(field, field_rules) - field_rules = self._specific_rules.get(field, field_rules) for entry in data: if isinstance(entry, dict): name = entry["entry"][name_index] @@ -50,14 +49,15 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve name = entry[name_index] level = entry[level_index] - if target and target != name: + if target and target.replace("*", "") not in name: continue replacements = field_rules.get("replacements", []) for replacement in replacements: name = name.replace(replacement, replacements[replacement]) tmp_string += self._get_string_to_add(field_rules, name, level, field) - self._output_dict[field][name]["guideline"] = guideline + if self._output_dict[field].get(name): + self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py index ffa8995..d06784c 100644 --- a/modules/compliance/configuration/configuration_base.py +++ b/modules/compliance/configuration/configuration_base.py @@ -13,6 +13,7 @@ def __init__(self, config_type): self._template = None self._config_output = None self.configuration = None + self._enabled_once = set() self._specific_rules = load_configuration("rules", f"configs/compliance/{config_type}/") def set_out_file(self, output_file): @@ -55,7 +56,7 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve :param name_index: index of the name column :param level_index: index of the level column :param guideline: the guideline from which the level was deducted - :param target: (Optional) if defined only the entry with target name will be used + :param target: (Optional) if defined only the entries whose name contains target will be used :return: """ raise NotImplementedError("This method should be reimplemented") @@ -86,7 +87,12 @@ def _get_string_to_add(self, field_rules, name, level, field): added_negatives = field_rules.get("added_negatives", False) if not self._output_dict.get(field): self._output_dict[field] = {} + if field in self._enabled_once: + return "" + if get_standardized_level(level) in ["must", "recommended"]: + if field_rules.get("enable_one_time"): + self._enabled_once.add(field) string_to_add += allow_string.replace("name", name) self._output_dict[field][name] = {"added": True} elif get_standardized_level(level) in ["must not", "not recommended"]: diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 74d239a..45df867 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -44,14 +44,15 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve name = entry[name_index] level = entry[level_index] - if target and target != name: + if target and target.replace("*", "") not in name: continue replacements = field_rules.get("replacements", []) for replacement in replacements: name = name.replace(replacement, replacements[replacement]) tmp_string += self._get_string_to_add(field_rules, name, level, field) - self._output_dict[field][name]["guideline"] = guideline + if self._output_dict[field].get(name): + self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] @@ -65,8 +66,15 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve # block: list of dictionaries that represent directives inside the directive got before # each directive has a directive field for the name and an args (list) one for the params it should have # The args value is a list only containing tmp_string because the params are prepared while reading them. - directive_to_add = {"directive": config_field, "args": [tmp_string]} + args = tmp_string + comment = "" + if tmp_string.count("#") == 1: + args, comment = tmp_string.split("#") + directive_to_add = {"directive": config_field, "args": [args]} self._template["config"][0]["parsed"][1]["block"].insert(0, directive_to_add) + if comment: + directive_to_add = {"directive": "#", "comment": comment} + self._template["config"][0]["parsed"][1]["block"].insert(0, directive_to_add) def _load_template(self): self._load_conf(Path(self._config_template_path)) diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index aff431f..3b1a383 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -20,7 +20,7 @@ def _worker(self, sheets_to_check): if isinstance(sheet, dict): table_to_search = list(sheet.keys())[0] name_to_search = sheet[table_to_search] - query_filter = "WHERE name == \"" + name_to_search + "\"" + query_filter = "WHERE name LIKE \"" + name_to_search + "\"" sheet = table_to_search columns = self.sheet_columns.get(sheet, columns) # Only the first guideline of each sheet is the interesting one From 41a6e7f36e9c3d25e24e8d63b7a8499a044d8025 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 27 Mar 2023 10:18:12 +0200 Subject: [PATCH 073/209] Update database to contain standardized extensions conditions --- requirements.db | Bin 1052672 -> 1052672 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.db b/requirements.db index 2cec82c1b70a89a9412605112fb76b2d9c9843c3..78fe4d1eb0a50f473cc993a58753c1a40fbb00fd 100644 GIT binary patch delta 8595 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 REo*|w`{sSKyMV&>{{TUs7=-`; From cea60d02b4834ff27e53d531484ddc3f11ac9e5a Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 28 Mar 2023 12:20:58 +0200 Subject: [PATCH 074/209] Moved is_enabled function inside ConditionParser class. Improved ConditionParser by a lot and added support for custom functions --- .../compliance/condition_instructions.json | 4 +- modules/compliance/compliance_base.py | 206 +++++++++++++++--- modules/compliance/compliance_many.py | 2 +- modules/compliance/compliance_one.py | 2 +- .../configuration/apache_configuration.py | 2 +- .../configuration/nginx_configuration.py | 2 +- requirements.db | Bin 1052672 -> 1052672 bytes 7 files changed, 183 insertions(+), 35 deletions(-) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index c23eea6..95d7806 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -1,5 +1,7 @@ { "EXTENSION": "Extension", "CIPHER": "CipherSuite", - "TRANSPARENCY": "Transparency" + "TRANSPARENCY": "Transparency", + "TLS": "Protocol", + "YEAR": "FUNCTION check_year" } \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index b812e81..12cb712 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,4 +1,6 @@ +import datetime import json +import re from pathlib import Path from modules.compliance.configuration.apache_configuration import ApacheConfiguration @@ -19,14 +21,149 @@ def convert_signature_algorithm(sig_alg: str) -> str: class ConditionParser: - def __init__(self): - self.result = None + def __init__(self, user_configuration): self.expression = "" - self.logical_separators = ["and", "or"] + self._logical_separators = ["and", "or"] + # simple regex to find all occurrences of the separators + self._splitting_regex = "|".join(self._logical_separators) + # same as above but also captures the separators + self._splitting_capturing_regex = "(" + ")|(".join(self._logical_separators) + ")" + self._user_configuration = user_configuration self.instructions = load_configuration("condition_instructions", "configs/compliance/") + self._custom_functions = CustomFunctions(user_configuration) + self._operators = { + "and": lambda op1, op2: op1 and op2, + "or": lambda op1, op2: op1 or op2, + } + + @staticmethod + def _partial_match_checker(field_value, name): + """ + Iters through field_value to check if name is contained in any of them + :param field_value: iterator to search + :param name: name to search + :return: True if the element is contained in the iterator + :rtype: bool + """ + enabled = False + for element in field_value: + if name in element: + enabled = True + break + return enabled + + @staticmethod + def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False): + """ + Checks if a field is enabled in the user configuration + :param user_configuration: the configuration in which the data should be searched + :param config_field: the field of the configuration containing the target data + :param name: the value to search + :param entry: the database entry (only the first two elements are checked) + :param partial_match: Default to false, if True the + :return: + """ + field_value = user_configuration[config_field] + enabled = False + print(field_value) + if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): + # Protocols case + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + elif isinstance(field_value, dict): + # Extensions and transparency case + if name.isnumeric(): + # Iana code case + enabled = name in field_value + else: + enabled = name in field_value.values() + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value.values(), name) + elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): + enabled = entry[:2] in field_value + elif isinstance(field_value, list) or isinstance(field_value, set): + enabled = name in field_value + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value, name) + return enabled - def solve(self): - pass + def input(self, expression): + self.expression = expression + + def _closing_parenthesis_index(self, start): + count = 0 + for i, c in enumerate(self.expression[start:]): + if c == "(": + count += 1 + elif c == ")": + count -= 1 + if count == 0: + return i + start + + def _solve(self, start, finish): + to_solve = self.expression[start: finish + 1] + + while "(" in to_solve: + # So that I'm sure that there aren't any parenthesis in the way + starting_index = to_solve.index("(") + start + end_index = self._closing_parenthesis_index(starting_index) - 1 + replacement = self._solve(starting_index + 1, end_index) + to_replace = self.expression[starting_index:end_index + 2] + to_solve = to_solve.replace(to_replace, replacement) + tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) + tokens = [token.strip() for token in tokens] + for token in tokens: + to_solve = to_solve.replace(token, str(self._evaluate_condition(token))) + tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) + tokens = [token for token in tokens if token] + while len(tokens) >= 3: + first_instruction = tokens.pop(0).strip() == "True" + logical_operation = self._operators[tokens.pop(0).lower()] + second_instruction = tokens.pop(0).strip() == "True" + result = logical_operation(first_instruction, second_instruction) + # After calculating the result it is inserted at the beginning of the tokens list to substitute the three + # removed elements + tokens.insert(0, str(result)) + return tokens[0] + + def _evaluate_condition(self, condition): + """ + Evaluates a condition and returns if it is True or False + :param condition: condition to evaluate + :type condition: str + :return: "True" or "False" accordingly + :rtype: bool + """ + negation = False + if condition[0] == "!": + condition = condition[1:] + negation = True + condition = condition.strip() + if condition in ["True", "False"]: + return condition + if " " not in condition or condition.split(" ")[0] not in self.instructions: + # TODO use logging module + print("Invalid condition: ", condition, " returning False") + return "False" + tokens = condition.split(" ") + field = tokens[0] + to_search = self._prepare_to_search(field, tokens[-1]) + config_field = self.instructions.get(field) + if config_field.startswith("FUNCTION"): + assert config_field[8] == " " + result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**{"data":to_search}) + else: + enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) + result = enabled if not negation else not enabled + return result + + @staticmethod + def _prepare_to_search(field, to_search): + new_to_search = to_search + if field == "TLS": + new_to_search = "TLS " + to_search.strip() + return new_to_search class Compliance: @@ -45,6 +182,7 @@ def __init__(self): self.sheet_columns = load_configuration("sheet_columns", "configs/compliance/") self.misc_fields = load_configuration("misc_fields", "configs/compliance/") self._validator = Validator() + self._condition_parser = ConditionParser(self._user_configuration) # This will be removed when integrating the module in the core self.test_ssl = Testssl() @@ -281,45 +419,25 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] - def update_result(self, sheet, name, entry_level, is_enabled, source): + def update_result(self, sheet, name, entry_level, enabled, source): information_level = None action = None entry_level = get_standardized_level(entry_level) - if entry_level == "must" and not is_enabled: + if entry_level == "must" and not enabled: information_level = "ERROR" action = "has to be enabled" - elif entry_level == "must not" and is_enabled: + elif entry_level == "must not" and enabled: information_level = "ERROR" action = "has to be disabled" - elif entry_level == "recommended" and not is_enabled: + elif entry_level == "recommended" and not enabled: information_level = "ALERT" action = "should be enabled" - elif entry_level == "not recommended" and is_enabled: + elif entry_level == "not recommended" and enabled: information_level = "ALERT" action = "should be disabled" if information_level: self._output_dict[sheet][name] = f"{information_level}: {action} according to {source}" - def is_enabled(self, config_field, name, entry): - """ - Checks if a field is enabled in the user configuration - """ - field_value = self._user_configuration[config_field] - enabled = False - if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): - # Protocols case - enabled = field_value.get(name, None) - if enabled is None: - enabled = True if "all" in field_value else False - elif isinstance(field_value, dict): - # Extensions case - enabled = name in field_value.values() - elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): - enabled = entry[:2] in field_value - elif isinstance(field_value, list) or isinstance(field_value, set): - enabled = name in field_value - return enabled - def _retrieve_entries(self, sheets_to_check, columns): """ Given the input dictionary and the list of columns updates the entries field with a dictionary in the form @@ -432,3 +550,31 @@ def _worker(self, sheets_to_check): def output(self): return self._config_class.configuration_output() + + +class CustomFunctions: + def __init__(self, user_configuration): + self._user_configuration = user_configuration + self._validator = Validator() + + # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: + # function(**kwargs) -> bool + + def check_year(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if the year indicated has already passed + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- Year to check + """ + year = kwargs.get("data", None) + if not year: + raise ValueError("No year provided") + self._validator.string(year) + + actual_date = datetime.date.today() + parsed_date = datetime.datetime.strptime(year+"-12-31", "%Y-%m-%d") + return parsed_date.date() > actual_date + diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index ca5ae90..65f54ab 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -21,5 +21,5 @@ def _worker(self, sheets_to_check): for entry_dict in self.evaluated_entries[sheet].values(): entry = entry_dict["entry"] name = entry[name_index] - enabled = self.is_enabled(sheet, name, entry) + enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, name, entry) self.update_result(sheet, name, entry_dict["level"], enabled, entry_dict["source"]) diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py index 452ea78..ab37bc8 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compliance_one.py @@ -29,5 +29,5 @@ def _worker(self, sheets_to_check): if config_field: name = entry[name_index] evaluation = entry[evaluation_index] - enabled = self.is_enabled(config_field, name, entry) + enabled = self._condition_parser.is_enabled(self._user_configuration, config_field, name, entry) self.update_result(sheet, name, evaluation, enabled, entry[-1]) diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index a2e814e..a354ce4 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -14,7 +14,7 @@ def __init__(self, file: Path = None): if file: self._load_conf(file) - # Stole this function from Configuration for testing purposes + # Borrowing this function from Configuration for testing purposes def _load_conf(self, file: Path): """ Internal method to load the apache configuration file. diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 45df867..5f76c30 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -13,7 +13,7 @@ def __init__(self, file: Path = None): if file: self._load_conf(file) - # Stole this function from Configuration for testing purposes + # Borrowing this function from Configuration for testing purposes def _load_conf(self, file: Path): """ Internal method to load the nginx configuration file. diff --git a/requirements.db b/requirements.db index 78fe4d1eb0a50f473cc993a58753c1a40fbb00fd..7ac11448ff99aaa21c60d3a39e1e0ac8bb75b11a 100644 GIT binary patch delta 904 zcmZvYOH30%7{{mWbRXTFoh@1n*0$D0Eh(B(z^EiZJeY71;_IfgQ6okpL`b}t0Lh{; znpiYuj&KnUaL|bEMZlw6l!&i`fDjXHqXZ*HAU62TY%e|zzuo`*@}Eb(?tzBxfrjj6 zw=~?h|-@(uS48WZ}^bP`l zz#97q?{O_()BRpRvUR__@_dJ1FChAcj?f-@oF*tTW{fAsZKKtwGfc8TM#*KeUoXIX zrgc*5hdDGQkSXSnUW~H45rvXk)6tY`rj5x_b}Y<2b-W?!BT&D}b%nvF8AC+W`)1@Q zsUVKhg({p-KjS5Q+O7r3%$3l(9UF9B7yFz7dt}bZok#XwB4ArrO#&&$V65Tu3ETRlAdMx1{pwKh3JW z)qgczlJehXs%Foh&B3R#X(^U!`7F)SEn*p?6en%mPJhuslbD0en)x_Qu{ngZ)Vzls NhVTI0S}aY`hTqX3^Y{P& delta 471 zcmXZW&r1S96bEoupFh^sW)%!-OCyPHwNaNYx^)X83ca`gKp>R2AUg~Z9kPPF4YG zIj`}He2AAa9{J3n1BwOASYg{AoNN^~|dFg7x-U6f(d9S-3Bq;JYU zHxr5OdHTDxcm*Ubs3$A1HPq8InM^ Date: Wed, 29 Mar 2023 15:40:47 +0200 Subject: [PATCH 075/209] Added custom condition functions for year, vlp, ca and this. Update compliance_one.py to support conditions --- .../compliance/condition_instructions.json | 6 +- modules/compliance/compliance_base.py | 128 +++++++++++++++--- modules/compliance/compliance_one.py | 28 +++- requirements.db | Bin 1052672 -> 1040384 bytes 4 files changed, 139 insertions(+), 23 deletions(-) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index 95d7806..1d0da86 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -3,5 +3,9 @@ "CIPHER": "CipherSuite", "TRANSPARENCY": "Transparency", "TLS": "Protocol", - "YEAR": "FUNCTION check_year" + "CERTSIG": "CertificateSignature", + "YEAR": "FUNCTION check_year", + "VLP": "FUNCTION check_vlp", + "CA": "FUNCTION check_ca", + "THIS": "FUNCTION check_this" } \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 12cb712..97fd6b9 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -31,6 +31,8 @@ def __init__(self, user_configuration): self._user_configuration = user_configuration self.instructions = load_configuration("condition_instructions", "configs/compliance/") self._custom_functions = CustomFunctions(user_configuration) + self.entry_updates = {} + self._enabled = None self._operators = { "and": lambda op1, op2: op1 and op2, "or": lambda op1, op2: op1 or op2, @@ -65,7 +67,6 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match """ field_value = user_configuration[config_field] enabled = False - print(field_value) if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): # Protocols case enabled = field_value.get(name, None) @@ -88,8 +89,12 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = ConditionParser._partial_match_checker(field_value, name) return enabled - def input(self, expression): - self.expression = expression + @staticmethod + def _prepare_to_search(field, to_search): + new_to_search = to_search + if field == "TLS": + new_to_search = "TLS " + to_search.strip() + return new_to_search def _closing_parenthesis_index(self, start): count = 0 @@ -142,7 +147,8 @@ def _evaluate_condition(self, condition): condition = condition.strip() if condition in ["True", "False"]: return condition - if " " not in condition or condition.split(" ")[0] not in self.instructions: + if condition not in self.instructions and \ + (" " not in condition and condition.split(" ")[0] not in self.instructions): # TODO use logging module print("Invalid condition: ", condition, " returning False") return "False" @@ -152,18 +158,29 @@ def _evaluate_condition(self, condition): config_field = self.instructions.get(field) if config_field.startswith("FUNCTION"): assert config_field[8] == " " - result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**{"data":to_search}) + args = { + "data": to_search, + "enabled": self._enabled + } + result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) else: enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) result = enabled if not negation else not enabled return result - @staticmethod - def _prepare_to_search(field, to_search): - new_to_search = to_search - if field == "TLS": - new_to_search = "TLS " + to_search.strip() - return new_to_search + def input(self, expression, enabled): + self.expression = expression + self._enabled = enabled + + def run(self, expression, enabled): + if expression: + self.input(expression, enabled) + return self.output() + + def output(self): + self.entry_updates = self._custom_functions.entry_updates + self._custom_functions.reset() + return self._solve(0, len(self.expression)) == "True" class Compliance: @@ -414,25 +431,35 @@ def prepare_testssl_output(self, test_ssl_output): index = len(config_dict) config_dict[index] = actual_dict["finding"] + elif field.startswith("cert_chain_of_trust"): + if not self._user_configuration.get("TrustedCerts"): + self._user_configuration["TrustedCerts"] = {} + config_dict = self._user_configuration["TrustedCerts"] + # the index is basically the certificate number + index = len(config_dict) + config_dict[index] = actual_dict["finding"] + elif field in self.misc_fields: if not self._user_configuration.get("Misc"): self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] - def update_result(self, sheet, name, entry_level, enabled, source): + def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None action = None + if name.startswith("TLS "): + print(name, enabled, valid_condition, entry_level) entry_level = get_standardized_level(entry_level) - if entry_level == "must" and not enabled: + if entry_level == "must" and valid_condition and not enabled: information_level = "ERROR" action = "has to be enabled" - elif entry_level == "must not" and enabled: + elif entry_level == "must not" and valid_condition and enabled: information_level = "ERROR" action = "has to be disabled" - elif entry_level == "recommended" and not enabled: + elif entry_level == "recommended" and valid_condition and not enabled: information_level = "ALERT" action = "should be enabled" - elif entry_level == "not recommended" and enabled: + elif entry_level == "not recommended" and valid_condition and enabled: information_level = "ALERT" action = "should be disabled" if information_level: @@ -495,7 +522,7 @@ def _evaluate_entries(self, sheets_to_check, columns): source_guideline = guideline resulting_level = levels[best_level] # The entries are ordered by name so every time the counter is the same as the number of guidelines to - # check it is time to add the entry to the output dictionary. + # check. At this point it is time to add the entry to the output dictionary. custom_guidelines_list = sheets_to_check[sheet].keys() - self._guidelines if sheet and counter == len(sheets_to_check[sheet]) - len(custom_guidelines_list): counter = 0 @@ -556,6 +583,13 @@ class CustomFunctions: def __init__(self, user_configuration): self._user_configuration = user_configuration self._validator = Validator() + self._entry_updates = {"levels": []} + self._operators = { + ">": lambda op1, op2: op1 > op2, + "<": lambda op1, op2: op1 < op2, + ">=": lambda op1, op2: op1 >= op2, + "<=": lambda op1, op2: op1 <= op2, + } # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: # function(**kwargs) -> bool @@ -575,6 +609,64 @@ def check_year(self, **kwargs): self._validator.string(year) actual_date = datetime.date.today() - parsed_date = datetime.datetime.strptime(year+"-12-31", "%Y-%m-%d") + parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") return parsed_date.date() > actual_date + def check_vlp(self, **kwargs): + result = False + for version in range(3): + enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) + if enabled: + result = True + self._entry_updates["levels"].append("must not") + return result + + def check_ca(self, **kwargs): + to_check = kwargs.get("data", None) + if not to_check: + raise ValueError("No year provided") + self._validator.string(to_check) + if " " in to_check: + tokens = to_check.split(" ") + if tokens[0] == "count": + count = self._count_ca() + op = tokens[1] + num = tokens[2] + self._validator.int(num) + return self._operators[op](count, num) + elif tokens[0] == "publicly": + certs_trust_dict = self._user_configuration.get("TrustedCerts", {}) + trusted = True + if not certs_trust_dict: + trusted = False + for cert in certs_trust_dict: + if certs_trust_dict[cert] != "passed.": + trusted = False + return trusted + + def _count_ca(self): + cas = set() + for field in self._user_configuration: + if field.startswith("cert_caIssuers"): + cas.add(self._user_configuration[field]["finding"]) + return len(cas) + + def check_this(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if the year indicated has already passed + :rtype: bool + :Keyword Arguments: + * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not + """ + enabled = kwargs.get("enabled", False) + self._entry_updates["has_alternative"] = True + return enabled + + @property + def entry_updates(self): + return self._entry_updates + + def reset(self): + self._entry_updates = {"levels": []} diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py index ab37bc8..a1e454a 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compliance_one.py @@ -13,11 +13,12 @@ def _worker(self, sheets_to_check): if not self._user_configuration: raise ValueError("No configuration provided") columns = ["name", "level", "condition", "guidelineName"] - name_index = columns.index("name") - evaluation_index = columns.index("level") for sheet in sheets_to_check: # If the sheet isn't in the dictionary then I can use the default value columns = self.sheet_columns.get(sheet, columns) + name_index = columns.index("name") + level_index = columns.index("level") + condition_index = columns.index("condition") guideline = list(sheets_to_check[sheet].keys())[0] if not self._output_dict.get(sheet): self._output_dict[sheet] = {} @@ -28,6 +29,25 @@ def _worker(self, sheets_to_check): for entry in data: if config_field: name = entry[name_index] - evaluation = entry[evaluation_index] + level = entry[level_index] enabled = self._condition_parser.is_enabled(self._user_configuration, config_field, name, entry) - self.update_result(sheet, name, evaluation, enabled, entry[-1]) + valid_condition = True + condition = entry[condition_index] + if condition: + valid_condition = self._condition_parser.run(condition, enabled) + if self._condition_parser.entry_updates.get("levels"): + levels = self._condition_parser.entry_updates.get("levels") + levels.insert(0, level) + to_use = self.level_to_use(levels) + level = levels[to_use] + has_alternative = self._condition_parser.entry_updates.get("has_alternative") + if has_alternative: + # This is to trigger the output condition + valid_condition = True + self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) + if has_alternative and self._output_dict[sheet].get(name) and \ + isinstance(condition, str) and condition.count(" ") > 1: + parts = entry[condition_index].split(" ") + # Tokens[1] is the logical operator + note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + self._output_dict[sheet][name] += note diff --git a/requirements.db b/requirements.db index 7ac11448ff99aaa21c60d3a39e1e0ac8bb75b11a..d38281af750d2bf427f4e6399fe5087a4a361fbb 100644 GIT binary patch delta 7863 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 SjgD>!n)QyGVR_BPvi4u(BP-MZ delta 10201 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 Date: Thu, 30 Mar 2023 11:10:55 +0200 Subject: [PATCH 076/209] Improved comments in compliance_one.py --- modules/compliance/compliance_one.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compliance_one.py index a1e454a..dd71d30 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compliance_one.py @@ -42,9 +42,11 @@ def _worker(self, sheets_to_check): level = levels[to_use] has_alternative = self._condition_parser.entry_updates.get("has_alternative") if has_alternative: - # This is to trigger the output condition + # This is to trigger the output condition. This works because I'm assuming that "THIS" is only + # used in a positive (recommended, must) context. valid_condition = True self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) + if has_alternative and self._output_dict[sheet].get(name) and \ isinstance(condition, str) and condition.count(" ") > 1: parts = entry[condition_index].split(" ") From e3ac53efe3e3b1c93de2223d7d0163a31793fe34 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 30 Mar 2023 14:33:45 +0200 Subject: [PATCH 077/209] Now the db_reader.py wrapper joins tables if there is more than one defined. __evaluate_entries and _retrieve_entries were updated to work with the updated wrapper. Added condition support to compliance_many.py --- modules/compliance/compliance_base.py | 151 +++++++++++++++-------- modules/compliance/compliance_many.py | 10 +- modules/compliance/wrappers/db_reader.py | 33 ++--- 3 files changed, 123 insertions(+), 71 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 97fd6b9..eaf5bd8 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -61,7 +61,7 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match :param user_configuration: the configuration in which the data should be searched :param config_field: the field of the configuration containing the target data :param name: the value to search - :param entry: the database entry (only the first two elements are checked) + :param entry: the database entry (only the first two elements are checked, they are neededd for KeyLengths) :param partial_match: Default to false, if True the :return: """ @@ -82,6 +82,7 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match if not enabled and partial_match: enabled = ConditionParser._partial_match_checker(field_value.values(), name) elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): + # KeyLengths case enabled = entry[:2] in field_value elif isinstance(field_value, list) or isinstance(field_value, set): enabled = name in field_value @@ -164,6 +165,8 @@ def _evaluate_condition(self, condition): } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) else: + # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use + # (None, None) enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) result = enabled if not negation else not enabled return result @@ -178,9 +181,10 @@ def run(self, expression, enabled): return self.output() def output(self): - self.entry_updates = self._custom_functions.entry_updates + solution = self._solve(0, len(self.expression)) == "True" + self.entry_updates = self._custom_functions.entry_updates.copy() self._custom_functions.reset() - return self._solve(0, len(self.expression)) == "True" + return solution class Compliance: @@ -447,8 +451,6 @@ def prepare_testssl_output(self, test_ssl_output): def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None action = None - if name.startswith("TLS "): - print(name, enabled, valid_condition, entry_level) entry_level = get_standardized_level(entry_level) if entry_level == "must" and valid_condition and not enabled: information_level = "ERROR" @@ -473,6 +475,7 @@ def _retrieve_entries(self, sheets_to_check, columns): entries = {} tables = [] for sheet in sheets_to_check: + columns_to_get = [] if not self._output_dict.get(sheet): self._output_dict[sheet] = {} for guideline in sheets_to_check[sheet]: @@ -480,8 +483,14 @@ def _retrieve_entries(self, sheets_to_check, columns): table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) tables.append(table_name) - self._database_instance.input(tables, other_filter="ORDER BY name") - data = self._database_instance.output(columns) + for t in tables: + for column in columns: + # all the columns are repeated to make easier index access later + columns_to_get.append(f"{t}.{column}") + + join_condition = "ON {first_table}.id == {table}.id".format(first_table=tables[0], table="{table}") + self._database_instance.input(tables, join_condition=join_condition) + data = self._database_instance.output(columns_to_get) entries[sheet] = data tables = [] self.entries = entries @@ -493,65 +502,99 @@ def _evaluate_entries(self, sheets_to_check, columns): self.evaluated_entries[sheet][count] = { "name": str, The name of the entry "level": str, The level that resulted from the evaluation - "source": str The guideline from which the level is deducted + "source": str, The guideline from which the level is deducted + "enabled": bool, If the entry is enabled in the configuration, + "valid_condition": bool, If the condition is valid or not + "note": str, Eventual note } :param sheets_to_check: The input dictionary :param columns: columns used to retrieve data from database :type columns: list """ # A more fitting name could be current_requirement_level - resulting_level = "" guideline_index = columns.index("guidelineName") level_index = columns.index("level") name_index = columns.index("name") + condition_index = columns.index("condition") + # this variable is needed to get the relativa position of the condition in respect of the level + level_to_condition_index = condition_index - level_index + step = len(columns) for sheet in self.entries: - # The total value is used as an index to avoid eventual collisions between equal names in the same sheet - total = 0 + entries = self.entries[sheet] if not self.evaluated_entries.get(sheet): self.evaluated_entries[sheet] = {} - counter = 1 - source_guideline = self.entries[sheet][guideline_index] - for entry in self.entries[sheet]: - entry_level = entry[level_index] - guideline = entry[guideline_index] - if entry_level != resulting_level: - levels = [resulting_level, entry_level] - best_level = self.level_to_use(levels) - # if best_level is 0 the source_guideline is the same - if best_level: - source_guideline = guideline - resulting_level = levels[best_level] - # The entries are ordered by name so every time the counter is the same as the number of guidelines to - # check. At this point it is time to add the entry to the output dictionary. - custom_guidelines_list = sheets_to_check[sheet].keys() - self._guidelines - if sheet and counter == len(sheets_to_check[sheet]) - len(custom_guidelines_list): - counter = 0 - name = entry[name_index] - for guideline in custom_guidelines_list: - custom_entry = self._custom_guidelines[sheet][guideline].get(name) - if custom_entry: - levels = [resulting_level, custom_entry["level"]] - guidelines_to_check = list(sheets_to_check[sheet]) - # If the custom_guideline appears before the source_guideline (actual guideline from which - # the level was deducted) it has greater priority, so it is necessary to switch them - if guidelines_to_check.index(guideline) < guidelines_to_check.index(source_guideline): - levels = levels[::-1] - best_level = self.level_to_use(levels) - # if best_level is 0 the source_guideline is the best - if best_level: - source_guideline = guideline - resulting_level = levels[best_level] - - # Save it to the dictionary - self.evaluated_entries[sheet][total] = { - "entry": entry, - "level": resulting_level, - "source": source_guideline - } - # the resulting level is reset so that it doesn't influence the next element. - resulting_level = "" - total += 1 - counter += 1 + custom_guidelines_list = sheets_to_check[sheet].keys() - self._guidelines + total = 0 + for entry in entries: + # These three are lists and not a single dictionary because the function level_to_use takes a list + conditions = [] + levels = [] + # list holding all the notes so that a note gets displayed only if needed + notes = [] + name = entry[name_index] + enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], entry) + + pos = level_index + while pos < len(entry): + level = entry[pos] + condition = entry[pos + level_to_condition_index] + valid_condition = True + notes.append("") + if condition: + valid_condition = self._condition_parser.run(condition, enabled) + if self._condition_parser.entry_updates.get("levels"): + potential_levels = self._condition_parser.entry_updates.get("levels") + potential_levels.insert(0, level) + level = potential_levels[self.level_to_use(potential_levels)] + has_alternative = self._condition_parser.entry_updates.get("has_alternative") + if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: + parts = entry[condition_index].split(" ") + # Tokens[1] is the logical operator + notes[-1] = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + # This is to trigger the output condition. This works because I'm assuming that "THIS" + # is only used in a positive (recommended, must) context. + valid_condition = True + + conditions.append(valid_condition) + levels.append(level) + pos += step + + best_level = self.level_to_use(levels) + resulting_level = levels[best_level] + condition = conditions[best_level] + note = notes[best_level] + # if best level is 0 it is the first one + source_guideline = entry[guideline_index + step * best_level] + + for guideline in custom_guidelines_list: + custom_entry = self._custom_guidelines[sheet][guideline].get(name) + if custom_entry: + levels = [resulting_level, custom_entry["level"]] + guidelines_to_check = list(sheets_to_check[sheet]) + # If the custom_guideline appears before the source_guideline (actual guideline from which + # the level was deducted) it has greater priority, so it is necessary to switch them + if guidelines_to_check.index(guideline) < guidelines_to_check.index(source_guideline): + levels = levels[::-1] + best_level = self.level_to_use(levels) + # if best_level is 0 the source_guideline is the best + if best_level: + source_guideline = guideline + resulting_level = levels[best_level] + + # Custom guidelines don't have notes + if source_guideline.upper() not in self._guidelines: + note = "" + + # Save it to the dictionary + self.evaluated_entries[sheet][total] = { + "entry": entry, + "level": resulting_level, + "source": source_guideline, + "enabled": enabled, + "valid_condition": condition, + "note": note + } + total += 1 class Generator(Compliance): diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compliance_many.py index 65f54ab..901af67 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compliance_many.py @@ -21,5 +21,11 @@ def _worker(self, sheets_to_check): for entry_dict in self.evaluated_entries[sheet].values(): entry = entry_dict["entry"] name = entry[name_index] - enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, name, entry) - self.update_result(sheet, name, entry_dict["level"], enabled, entry_dict["source"]) + level = entry_dict["level"] + enabled = entry_dict["enabled"] + valid_condition = entry_dict["valid_condition"] + note = entry_dict["note"] + + self.update_result(sheet, name, level, enabled, entry_dict["source"], valid_condition) + if note and self._output_dict[sheet].get(name): + self._output_dict[sheet][name] += entry_dict.get("note") diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index f772ee5..febc368 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -35,27 +35,27 @@ def get_table_name(self, sheet, standard_name, version=""): version_name = db_utils.get_version_name_for_database(version) return sheet_name + standard_name + version_name - def get_sheet_name(self, sheet, default_value = None): + def get_sheet_name(self, sheet, default_value=None): """ Returns the sheet_name for the sheet, returns None if there isn't a mapping available """ return self.sheet_mapping.get(sheet, default_value) - def input(self, tables, evaluation="", other_filter=""): + def input(self, tables, join_condition="1==1", other_filter=""): """ Set the input parameters :param: tables -- List of tables from which data should be retrieved :type tables: list - :param: evaluation -- (Optional) To filter between evaluations - :type evaluation: str + :param join_condition: Default to 1==1, the condition to apply to the join in case of multiple tables + :type join_condition: str :param: other_filter -- (Optional) A filter to add to the query the WHERE/AND part will be handled automatically :type other_filter: str """ self.__input_dict = { "tables": tables, - "evaluation": evaluation, - "other_filter": other_filter + "other_filter": other_filter, + "join_condition": join_condition } def output(self, columns="*"): @@ -64,20 +64,23 @@ def output(self, columns="*"): :param: tables -- List of tables from which data should be retrieved :type columns: list - :param: evaluation -- (Optional) To filter between evaluations """ - query = "" - first = True - evaluation = self.__input_dict.get("evaluation") + query = f"SELECT {', '.join(columns)} FROM " + first = 2 other_filter = self.__input_dict.get("other_filter") for table in self.__input_dict.get("tables", []): if first: - first = False + first -= 1 else: - query += " UNION ALL " - query += f"SELECT {','.join(columns)} FROM {table}" - if evaluation: - query += f" where evaluation = {evaluation}" + query += " JOIN " + query += table + if not first: + join_condition = self.__input_dict["join_condition"] + if "{table}" in join_condition: + join_condition = join_condition.format(table=table) + query += " " + join_condition + else: + first -= 1 if other_filter: query += " " + other_filter self.cursor.execute(query) From 05c5a209a1d1d1b103fb80a4c05e0af84f641f30 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 30 Mar 2023 17:23:52 +0200 Subject: [PATCH 078/209] Added conditions information to generate functions --- .../generate/configuration_rules.json | 3 ++- modules/compliance/compliance_base.py | 6 ++++++ .../configuration/apache_configuration.py | 15 ++++++++++++++- .../configuration/configuration_base.py | 6 +++--- .../configuration/nginx_configuration.py | 17 +++++++++++++++-- modules/compliance/generate_many.py | 9 ++++++--- modules/compliance/generate_one.py | 7 +------ 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/configs/compliance/generate/configuration_rules.json b/configs/compliance/generate/configuration_rules.json index 4c936ba..e2c1c32 100644 --- a/configs/compliance/generate/configuration_rules.json +++ b/configs/compliance/generate/configuration_rules.json @@ -27,7 +27,8 @@ "Stapling": { "enable": "on", "disable": "off", - "separator": "" + "separator": "", + "enable_one_time": true }, "EarlyData": { "enable": "on", diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index eaf5bd8..00371b1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -605,6 +605,12 @@ def __init__(self): self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/generate/") self._configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/generate/") + def _get_config_name(self, field): + name = self._configuration_mapping.get(field, field) + if isinstance(name, dict): + name = list(name.keys())[0] + return name + # To override def _worker(self, sheets_to_check): """ diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index a354ce4..957007a 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -29,8 +29,11 @@ def _load_template(self): with open(self._config_template_path, "r") as f: self._template = f.read() - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): + def add_configuration_for_field(self, field, field_rules, data, columns, guideline, target=None): config_field = self.mapping.get(field, None) + name_index = columns.index("name") + level_index = columns.index("level") + condition_index = columns.index("condition") self._output_dict[field] = {} if not config_field: @@ -41,13 +44,21 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve field_rules = self._specific_rules.get(field, field_rules) for entry in data: + condition = "" if isinstance(entry, dict): name = entry["entry"][name_index] level = entry["level"] guideline = entry["source"] + if guideline in entry["entry"]: + guideline_pos = entry["entry"].index(guideline) + # to get the condition for the guideline I calculate guideline's index and then search it near it + step = len(columns) + guideline_counter = guideline_pos // step + condition = entry["entry"][condition_index + guideline_counter * step] else: name = entry[name_index] level = entry[level_index] + condition = entry[condition_index] if target and target.replace("*", "") not in name: continue @@ -57,6 +68,8 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve name = name.replace(replacement, replacements[replacement]) tmp_string += self._get_string_to_add(field_rules, name, level, field) if self._output_dict[field].get(name): + if condition: + self.conditions_to_check[name] = condition self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py index d06784c..6ce69b8 100644 --- a/modules/compliance/configuration/configuration_base.py +++ b/modules/compliance/configuration/configuration_base.py @@ -14,6 +14,7 @@ def __init__(self, config_type): self._config_output = None self.configuration = None self._enabled_once = set() + self.conditions_to_check = {} self._specific_rules = load_configuration("rules", f"configs/compliance/{config_type}/") def set_out_file(self, output_file): @@ -48,13 +49,12 @@ def _load_template(self): def _write_to_file(self): raise NotImplementedError("This method should be reimplemented") - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): + def add_configuration_for_field(self, field, field_rules, data, columns, guideline, target=None): """ :param field: the field that should be added (taken from configuration_rules) :param field_rules: the rules that should be applied to that field :param data: data from which to gather the field information - :param name_index: index of the name column - :param level_index: index of the level column + :param columns: list of columns used to retrieve data from the database :param guideline: the guideline from which the level was deducted :param target: (Optional) if defined only the entries whose name contains target will be used :return: diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 5f76c30..3d6d9c6 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -25,8 +25,11 @@ def _load_conf(self, file: Path): if self.configuration.get("errors", []): raise ValueError("Invalid nginx config file") - def add_configuration_for_field(self, field, field_rules, data, name_index, level_index, guideline, target=None): + def add_configuration_for_field(self, field, field_rules, data, columns, guideline, target=None): config_field = self.mapping.get(field, None) + name_index = columns.index("name") + level_index = columns.index("level") + condition_index = columns.index("condition") self._output_dict[field] = {} if not config_field: @@ -36,13 +39,21 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve tmp_string = "" field_rules = self._specific_rules.get(field, field_rules) for entry in data: + condition = "" if isinstance(entry, dict): name = entry["entry"][name_index] level = entry["level"] guideline = entry["source"] + if guideline in entry["entry"]: + guideline_pos = entry["entry"].index(guideline) + # to get the condition for the guideline I calculate guideline's index and then search it near it + step = len(columns) + guideline_counter = guideline_pos // step + condition = entry["entry"][condition_index + guideline_counter * step] else: name = entry[name_index] level = entry[level_index] + condition = entry[condition_index] if target and target.replace("*", "") not in name: continue @@ -52,13 +63,15 @@ def add_configuration_for_field(self, field, field_rules, data, name_index, leve name = name.replace(replacement, replacements[replacement]) tmp_string += self._get_string_to_add(field_rules, name, level, field) if self._output_dict[field].get(name): + if condition: + self.conditions_to_check[name] = condition self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] tmp_string = tmp_string.strip() if tmp_string: # this is to prevent adding a field without any value - # The directive gets added at the beginning the http directive + # The directive gets added at the beginning of the http directive # the breakdown of the below instruction is: # loaded_template: dictionary # config: list of loaded files (in this case one) diff --git a/modules/compliance/generate_many.py b/modules/compliance/generate_many.py index 80a89c3..af16841 100644 --- a/modules/compliance/generate_many.py +++ b/modules/compliance/generate_many.py @@ -6,8 +6,6 @@ def _worker(self, sheets_to_check): if not self._config_class.output_file(): raise ValueError("No output file path provided") columns = ["name", "level", "condition", "guidelineName"] - name_index = columns.index("name") - level_index = columns.index("level") conf_mapping = self._configuration_mapping # fill the entries field with the data from the sheets self._retrieve_entries(sheets_to_check, columns) @@ -28,4 +26,9 @@ def _worker(self, sheets_to_check): field_rules = self._configuration_rules.get(field, {}) # the guideline here is defined as None because it will be defined in the function self._config_class.add_configuration_for_field(field, field_rules, self.evaluated_entries[sheet].values(), - name_index, level_index, None, target) + columns, None, target) + for field in self._config_class.conditions_to_check: + condition = self._config_class.conditions_to_check[field] + + + diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 3b1a383..260f33e 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -6,9 +6,6 @@ def _worker(self, sheets_to_check): if not self._config_class.output_file(): raise ValueError("No output file path provided") columns = ["name", "level", "condition", "guidelineName"] - name_index = columns.index("name") - level_index = columns.index("level") - guideline_index = columns.index("guidelineName") conf_mapping = self._configuration_mapping for field in conf_mapping: if not self._output_dict.get(field): @@ -26,10 +23,8 @@ def _worker(self, sheets_to_check): # Only the first guideline of each sheet is the interesting one guideline = list(sheets_to_check[sheet].keys())[0] table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) - # Get the guideline name saved in the database for this field - guideline = guideline_index self._database_instance.input([table_name], other_filter=query_filter) data = self._database_instance.output(columns) field_rules = self._configuration_rules.get(field, {}) - self._config_class.add_configuration_for_field(field, field_rules, data, name_index, level_index, guideline) + self._config_class.add_configuration_for_field(field, field_rules, data, columns, guideline) From ca90f04567e58f008555934eef4a3da9b89caecc Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 31 Mar 2023 15:15:49 +0200 Subject: [PATCH 079/209] Renamed compliance_many.py and compliance_one.py to compare_many.py and compare_one.py --- modules/compliance/{compliance_many.py => compare_many.py} | 2 +- modules/compliance/{compliance_one.py => compare_one.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename modules/compliance/{compliance_many.py => compare_many.py} (97%) rename modules/compliance/{compliance_one.py => compare_one.py} (98%) diff --git a/modules/compliance/compliance_many.py b/modules/compliance/compare_many.py similarity index 97% rename from modules/compliance/compliance_many.py rename to modules/compliance/compare_many.py index 901af67..3342b02 100644 --- a/modules/compliance/compliance_many.py +++ b/modules/compliance/compare_many.py @@ -1,7 +1,7 @@ from modules.compliance.compliance_base import Compliance -class ComplianceMany(Compliance): +class CompareMany(Compliance): def _worker(self, sheets_to_check): """ :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol: version_of_protocol} diff --git a/modules/compliance/compliance_one.py b/modules/compliance/compare_one.py similarity index 98% rename from modules/compliance/compliance_one.py rename to modules/compliance/compare_one.py index dd71d30..24ae8f0 100644 --- a/modules/compliance/compliance_one.py +++ b/modules/compliance/compare_one.py @@ -1,7 +1,7 @@ from modules.compliance.compliance_base import Compliance -class ComplianceOne(Compliance): +class CompareOne(Compliance): def _worker(self, sheets_to_check): """ :param sheets_to_check: dict of sheets that should be checked in the form: sheet:{protocol, version_of_protocol} From 34362adce6c41c8581dcaf791876e78ccc67286a Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 31 Mar 2023 15:20:45 +0200 Subject: [PATCH 080/209] Added a way to fill the _user_configuration field inside the generator classes --- .../compliance/generate/user_conf_types.json | 12 ++++++ modules/compliance/compliance_base.py | 38 ++++++++++++++++-- requirements.db | Bin 1040384 -> 1050867 bytes 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 configs/compliance/generate/user_conf_types.json diff --git a/configs/compliance/generate/user_conf_types.json b/configs/compliance/generate/user_conf_types.json new file mode 100644 index 0000000..51a6138 --- /dev/null +++ b/configs/compliance/generate/user_conf_types.json @@ -0,0 +1,12 @@ +{ + "Protocol": "dict", + "CipherSuite": "set", + "Groups": "list", + "CertificateSignature": "set", + "Hash": "set", + "Signature": "set", + "Extension": "dict", + "KeyLengths": "list", + "TrustedCerts": "dict", + "Transparency": "dict" +} \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 00371b1..6afca76 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -65,7 +65,7 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match :param partial_match: Default to false, if True the :return: """ - field_value = user_configuration[config_field] + field_value = user_configuration.get(config_field, None) enabled = False if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): # Protocols case @@ -544,7 +544,6 @@ def _evaluate_entries(self, sheets_to_check, columns): valid_condition = self._condition_parser.run(condition, enabled) if self._condition_parser.entry_updates.get("levels"): potential_levels = self._condition_parser.entry_updates.get("levels") - potential_levels.insert(0, level) level = potential_levels[self.level_to_use(potential_levels)] has_alternative = self._condition_parser.entry_updates.get("has_alternative") if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: @@ -604,13 +603,46 @@ def __init__(self): super().__init__() self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/generate/") self._configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/generate/") + self._user_configuration_types = load_configuration("user_conf_types", "configs/compliance/generate/") + self._type_converter = { + "dict": dict, + "list": list, + "set": set, + } def _get_config_name(self, field): - name = self._configuration_mapping.get(field, field) + name = self._configuration_mapping.get(field, None) if isinstance(name, dict): name = list(name.keys())[0] return name + def _fill_user_configuration(self): + assert self._config_class is not None + output_dict = self._config_class.output_dict + for field in output_dict: + config_field = self._get_config_name(field) + save_in = self._user_configuration_types.get(config_field) + save_in = self._type_converter.get(save_in) + current_field = output_dict[field] + if config_field and save_in: + if self._user_configuration.get(config_field) is None: + self._user_configuration[config_field] = save_in() + user_conf_field = self._user_configuration[config_field] + if isinstance(user_conf_field, list): + values = [val for val in current_field if current_field[val]["added"]] + user_conf_field.extend(values) + elif isinstance(user_conf_field, set): + values = [val for val in current_field if current_field[val]["added"]] + user_conf_field.update(values) + elif isinstance(user_conf_field, dict): + for val in current_field: + if config_field == "Protocol": + user_conf_field[val] = current_field[val]["added"] + elif config_field == "Extension": + self._database_instance.input(["Extension"], other_filter=f'WHERE name=="{val}"') + iana_code = self._database_instance.output(["iana_code"])[0][0] + user_conf_field[str(iana_code)] = val + # To override def _worker(self, sheets_to_check): """ diff --git a/requirements.db b/requirements.db index d38281af750d2bf427f4e6399fe5087a4a361fbb..b21045f18fb650e23c8bb98e572f29204328d332 100644 GIT binary patch delta 46456 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 z�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 MzkhSxFIxEj01pkmnE(I) From 1acb76b4dfa9c8770a728a9975d0a3a63418c194 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 3 Apr 2023 11:57:04 +0200 Subject: [PATCH 081/209] Added support for conditions to ConfigurationMaker --- modules/compliance/compliance_base.py | 29 ++++++++++++++++++ .../configuration/apache_configuration.py | 22 +++++++++++-- .../configuration/nginx_configuration.py | 21 +++++++++++-- requirements.db | Bin 1050867 -> 1040384 bytes 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 6afca76..ae01454 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -618,6 +618,8 @@ def _get_config_name(self, field): def _fill_user_configuration(self): assert self._config_class is not None + # reset user_configuration to avoid issues + self._user_configuration = {} output_dict = self._config_class.output_dict for field in output_dict: config_field = self._get_config_name(field) @@ -656,7 +658,34 @@ def _worker(self, sheets_to_check): """ raise NotImplementedError("This method should be reimplemented") + def _check_conditions(self): + """ + Checks the conditions and removes/adds fields if needed + """ + conditions_to_check = self._config_class.conditions_to_check + for index in conditions_to_check: + expression = conditions_to_check[index]["expression"] + level = get_standardized_level(conditions_to_check[index]["level"]) + data = conditions_to_check[index]["data"] + columns = conditions_to_check[index]["columns"] + guideline = conditions_to_check[index]["guideline"] + field = conditions_to_check[index]["field"] + enabled = level in ["recommended", "must"] + valid_condition = self._condition_parser.run(expression, enabled) + field_rules = self._configuration_rules.get(field, {}) + + if self._condition_parser.entry_updates.get("levels"): + potential_levels = self._condition_parser.entry_updates.get("levels") + level = potential_levels[self.level_to_use(potential_levels)] + if not valid_condition and enabled: + self._config_class.remove_field(field) + elif level in ["not recommended", "must not"] and valid_condition: + self._config_class.remove_field(field) + elif enabled and valid_condition: + self._config_class.add_configuration_for_field(field, field_rules, data, columns, guideline) + def output(self): + self._check_conditions() return self._config_class.configuration_output() diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index 957007a..ee1d233 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -66,10 +66,18 @@ def add_configuration_for_field(self, field, field_rules, data, columns, guideli replacements = field_rules.get("replacements", []) for replacement in replacements: name = name.replace(replacement, replacements[replacement]) - tmp_string += self._get_string_to_add(field_rules, name, level, field) + tmp_string += self._get_string_to_add(field_rules, name, level, config_field) if self._output_dict[field].get(name): if condition: - self.conditions_to_check[name] = condition + index = len(self.conditions_to_check) + self.conditions_to_check[index] = { + "columns": columns, + "data": data, + "expression": condition, + "field": config_field, + "guideline": guideline, + "level": level + } self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": @@ -78,6 +86,16 @@ def add_configuration_for_field(self, field, field_rules, data, columns, guideli if len(tmp_string) != len(config_field) + 1: self._string_to_add += "\n" + tmp_string + def remove_field(self, field): + lines = self._string_to_add.splitlines() + to_remove = [] + for line in lines: + if line.strip().startswith(field): + to_remove.append(line) + for line in to_remove: + lines.remove(line) + self._string_to_add = os.sep.join(lines) + def _write_to_file(self): if not os.path.isfile(self._config_template_path): raise FileNotFoundError("Invalid template file") diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 3d6d9c6..4cc60ac 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -64,13 +64,22 @@ def add_configuration_for_field(self, field, field_rules, data, columns, guideli tmp_string += self._get_string_to_add(field_rules, name, level, field) if self._output_dict[field].get(name): if condition: - self.conditions_to_check[name] = condition + index = len(self.conditions_to_check) + self.conditions_to_check[index] = { + "columns": columns, + "data": data, + "expression": condition, + "field": config_field, + "guideline": guideline, + "level": level + } self._output_dict[field][name]["guideline"] = guideline if tmp_string and tmp_string[-1] == ":": tmp_string = tmp_string[:-1] tmp_string = tmp_string.strip() - if tmp_string: # this is to prevent adding a field without any value + # this is to prevent adding a field without any value + if tmp_string: # The directive gets added at the beginning of the http directive # the breakdown of the below instruction is: # loaded_template: dictionary @@ -89,6 +98,14 @@ def add_configuration_for_field(self, field, field_rules, data, columns, guideli directive_to_add = {"directive": "#", "comment": comment} self._template["config"][0]["parsed"][1]["block"].insert(0, directive_to_add) + def remove_field(self, field): + to_remove = [] + for directive in self._template["config"][0]["parsed"][1]["block"]: + if directive.get("directive") == field: + to_remove.append(directive) + for directive in to_remove: + del self._template["config"][0]["parsed"][1]["block"][directive] + def _load_template(self): self._load_conf(Path(self._config_template_path)) self._template = self.configuration diff --git a/requirements.db b/requirements.db index b21045f18fb650e23c8bb98e572f29204328d332..a160c443d47abbfa24002b8077efb1ab271ef540 100644 GIT binary patch delta 32836 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 delta 44906 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 Date: Mon, 3 Apr 2023 12:02:24 +0200 Subject: [PATCH 082/209] Added fill_user_configuration call to output --- modules/compliance/compliance_base.py | 1 + modules/compliance/configuration/configuration_base.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index ae01454..05a63b4 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -685,6 +685,7 @@ def _check_conditions(self): self._config_class.add_configuration_for_field(field, field_rules, data, columns, guideline) def output(self): + self._fill_user_configuration() self._check_conditions() return self._config_class.configuration_output() diff --git a/modules/compliance/configuration/configuration_base.py b/modules/compliance/configuration/configuration_base.py index 6ce69b8..0ac0548 100644 --- a/modules/compliance/configuration/configuration_base.py +++ b/modules/compliance/configuration/configuration_base.py @@ -106,3 +106,7 @@ def _get_string_to_add(self, field_rules, name, level, field): string_to_add += separator return string_to_add + + @property + def output_dict(self): + return self._output_dict From 539d5891f6a966d3ea36f6e5955d1a3c294556f2 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 4 Apr 2023 13:44:53 +0200 Subject: [PATCH 083/209] Implemented alias parser and added "run" method to database wrapper --- configs/compliance/alias/alias_mapping.json | 13 +++ configs/compliance/alias/default_levels.json | 62 ++++++++++++ configs/compliance/alias/user_defined.json | 62 ++++++++++++ modules/compliance/compliance_base.py | 97 +++++++++++++++++++ modules/compliance/wrappers/db_reader.py | 22 ++++- requirements.db | Bin 1040384 -> 1056768 bytes 6 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 configs/compliance/alias/alias_mapping.json create mode 100644 configs/compliance/alias/default_levels.json create mode 100644 configs/compliance/alias/user_defined.json diff --git a/configs/compliance/alias/alias_mapping.json b/configs/compliance/alias/alias_mapping.json new file mode 100644 index 0000000..afffd09 --- /dev/null +++ b/configs/compliance/alias/alias_mapping.json @@ -0,0 +1,13 @@ +{ + "1": "PREFERRED1", + "2": "PREFERRED2", + "3": "PREFERRED3", + "GOV": "GOVERNMENTONLY", + "CUST": "CUSTOMERFACING", + "FED": "FEDERALAPPLICATIONS", + "FEDR": "FEDERALREQ", + "SIG": "CLIENT_SERVER_SIGNATURES", + "CERT": "INCERTIFICATES", + "INT": "INTERMEDIATE", + "MOD": "MODERN" +} \ No newline at end of file diff --git a/configs/compliance/alias/default_levels.json b/configs/compliance/alias/default_levels.json new file mode 100644 index 0000000..ae0ce78 --- /dev/null +++ b/configs/compliance/alias/default_levels.json @@ -0,0 +1,62 @@ +{ + "Protocol": { + "NIST": "GOVERNMENTONLY", + "BSI": "", + "ANSSI": "", + "AGID": "", + "MOZILLA": "" + }, + "CipherSuite": { + "NIST": "1", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Extension": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "AGID": "" + }, + "CertificateSignature": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Hash": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Signature": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Groups": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "KeyLengths": { + "ANSSI": "", + "MOZILLA": "", + "AGID": "", + "NIST": "", + "BSI": "" + }, + "Misc": { + "NIST": "", + "BSI": "", + "AGID": "" + } +} \ No newline at end of file diff --git a/configs/compliance/alias/user_defined.json b/configs/compliance/alias/user_defined.json new file mode 100644 index 0000000..54fadc4 --- /dev/null +++ b/configs/compliance/alias/user_defined.json @@ -0,0 +1,62 @@ +{ + "Protocol": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "AGID": "", + "MOZILLA": "" + }, + "CipherSuite": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Extension": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "AGID": "" + }, + "CertificateSignature": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Hash": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Signature": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "Groups": { + "NIST": "", + "BSI": "", + "ANSSI": "", + "MOZILLA": "", + "AGID": "" + }, + "KeyLengths": { + "ANSSI": "", + "MOZILLA": "", + "AGID": "", + "NIST": "", + "BSI": "" + }, + "Misc": { + "NIST": "", + "BSI": "", + "AGID": "" + } +} \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 05a63b4..262d62c 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -518,6 +518,9 @@ def _evaluate_entries(self, sheets_to_check, columns): condition_index = columns.index("condition") # this variable is needed to get the relativa position of the condition in respect of the level level_to_condition_index = condition_index - level_index + # The entry is composed of all the columns repeated n times, with n being the number of guidelines. + # The step is the number of columns. This allows easy data retrieval by doing something like: + # "value_index * step * guideline_index" to retrieve data for a specific guideline step = len(columns) for sheet in self.entries: entries = self.entries[sheet] @@ -539,6 +542,7 @@ def _evaluate_entries(self, sheets_to_check, columns): level = entry[pos] condition = entry[pos + level_to_condition_index] valid_condition = True + # Add an empty string to the notes so that all the notes are in the same position of their entry notes.append("") if condition: valid_condition = self._condition_parser.run(condition, enabled) @@ -700,6 +704,7 @@ def __init__(self, user_configuration): "<": lambda op1, op2: op1 < op2, ">=": lambda op1, op2: op1 >= op2, "<=": lambda op1, op2: op1 <= op2, + "==": lambda op1, op2: op1 == op2 } # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: @@ -718,6 +723,9 @@ def check_year(self, **kwargs): if not year: raise ValueError("No year provided") self._validator.string(year) + # This means that the guideline document didn't define a limit to this condition + if year[-1] == "+": + return True actual_date = datetime.date.today() parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") @@ -781,3 +789,92 @@ def entry_updates(self): def reset(self): self._entry_updates = {"levels": []} + + +class AliasParser: + def __init__(self): + self.__logging = Logger("Compliance module") + self._database_instance = Database() + self._guidelines = [name[0].upper() for name in self._database_instance.run(["Guideline"])] + # simple regex to find and capture all occurrences of the guidelines + self._splitting_regex = "(" + ")|(".join(self._guidelines) + ")" + self._sheets_versions_dict = {} + self._fill_sheets_dict() + self._guidelines_versions = {} + self._fill_guidelines_versions() + self._aliases = load_configuration("alias_mapping", "configs/compliance/alias/") + self._default_versions = load_configuration("default_levels", "configs/compliance/alias/") + + def _fill_sheets_dict(self): + for table in self._database_instance.table_names: + tokens = re.split(self._splitting_regex, table, flags=re.IGNORECASE) + tokens = [t for t in tokens if t] + # If the length is one or less the table is a general data table. + if len(tokens) > 1: + sheet = tokens[0] + guideline = tokens[1] + version = tokens[2] if len(tokens) == 3 else "" + if self._sheets_versions_dict.get(sheet) is None: + self._sheets_versions_dict[sheet] = {guideline: set()} + if self._sheets_versions_dict[sheet].get(guideline) is None: + self._sheets_versions_dict[sheet][guideline] = set() + self._sheets_versions_dict[sheet][guideline].add(version) + + def _fill_guidelines_versions(self): + for i, sheet in enumerate(self._sheets_versions_dict): + for guideline in self._sheets_versions_dict[sheet]: + if self._guidelines_versions.get(guideline) is None: + self._guidelines_versions[guideline] = {} + guideline_dict = self._guidelines_versions[guideline] + for version in self._sheets_versions_dict[sheet][guideline]: + new_set = True + for index in guideline_dict: + if version.upper() in guideline_dict[index]: + new_set = False + if new_set and version: + if guideline_dict.get(i) is None: + guideline_dict[i] = set() + guideline_dict[i].add(version.upper()) + + def is_valid(self, alias): + if "_" not in alias and alias.upper() not in self._guidelines: + raise ValueError(f"Alias {alias} not valid") + tokens = alias.split("_") + guideline = tokens[0] + if guideline not in self._guidelines_versions: + raise ValueError(f"Invalid guideline in alias: {alias}") + used_sets = set() + for token in tokens[1:]: + found = False + for index in self._guidelines_versions[guideline]: + # If it is an abbreviation get the complete name. + token = self._aliases.get(token.upper(), token.upper()) + if token in self._guidelines_versions[guideline][index] and index not in used_sets: + found = True + used_sets.add(index) + if not found: + raise ValueError(f"Invalid version {token} for alias: {alias}") + + def get_sheets_to_check(self, aliases): + sheets_to_check = {} + for alias in aliases: + self.is_valid(alias) + tokens = alias.split("_") + guideline = tokens[0] + tokens.append("") + for i, sheet in enumerate(self._sheets_versions_dict): + if sheets_to_check.get(sheet) is None: + sheets_to_check[sheet] = {} + for token in tokens[1:]: + token = token.upper() + # If it is an abbreviation get the complete name. + token = self._aliases.get(token, token) + if sheet + guideline + token in self._database_instance.table_names: + sheets_to_check[sheet][guideline] = token + if sheets_to_check[sheet].get(guideline) is None: + version = self._default_versions[sheet].get(guideline) + if version is not None: + sheets_to_check[sheet][guideline] = version + else: + self.__logging.info(f"Skipping {guideline} in {sheet} because no version is available.") + return sheets_to_check diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index febc368..4dd4422 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -13,9 +13,9 @@ def __init__(self, file: str = database_file): self.cursor = self.connection.cursor() self.sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") - # # Retrieve the list of tables from the database - # self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") - # self.table_names = [table[0] for table in self.cursor.fetchall()] + # Retrieve the list of tables from the database + self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") + self.table_names = [table[0] for table in self.cursor.fetchall()] self.__input_dict = {} def get_table_name(self, sheet, standard_name, version=""): @@ -85,3 +85,19 @@ def output(self, columns="*"): query += " " + other_filter self.cursor.execute(query) return self.cursor.fetchall() + + def run(self, tables, join_condition="1==1", other_filter="", columns="*"): + """ + Retrieve data from the database + + :param: tables -- List of tables from which data should be retrieved + :type tables: list + :param join_condition: Default to 1==1, the condition to apply to the join in case of multiple tables + :type join_condition: str + :param: other_filter -- (Optional) A filter to add to the query the WHERE/AND part will be handled automatically + :type other_filter: str + :param: tables -- List of tables from which data should be retrieved + :type columns: list + """ + self.input(tables, join_condition, other_filter) + return self.output(columns) diff --git a/requirements.db b/requirements.db index a160c443d47abbfa24002b8077efb1ab271ef540..a12646c899fc23e7867e0e972bab2a71dec251f9 100644 GIT binary patch delta 3765 zcmeHKe{fXA9p8O#-@RY^-d@NY7m>m-5Mvr?&VykZ3?&yyBv3=j(Q)XEp0GUGbN1E!G^}k!_hXW(1zAh5w%sInzmGF0gX_o0`~gd_m0aW zg!W(mFq4c{SgNo4N+WRj`n5Z;x& zbzp=osdLOI`GDd7Y`@&P<8mu@F1Jc0!^#@Om)C*Y(_?PbyYM31pl84wa zr=(XNVd}w3f)jWtFS(!1cJe=yKVehfbMe3S<4214-}vx6zgVWq2fnF}yVS#e>hFrw z-}~a&m$e|XR~mh39w~Bv@8f#Y{%M@5y_;n~{ln6}ZaJ7cDx1oiFnFo3 zeDueyF!S|9c8BK55ssF(cIzYFSL{nXP%>iaiP z|Lv##>pMXGaFK7;^F`{IkLu)C5ZJ^ke(tE4_N0H>cZ%FU`?x$W7JJc8{YX&%;isN0 zQvZ1ab;wWsRAl_IKjW)K>SZ5wM}COFqrek@M}!o8 zcgs2uJIMxEFZ(9Do6WF4vEQ`&?H}451GA#bZT`qs<9Nkwii*Rf((IWe-keA@txUu< zv^lkIQ)g!i)Yq?Wh_8&qqVxEJ8Kdd;3Q5(&VF0^W_Ya;7?VQLMmC-T-A3*d&BW+Ao zOZ6AXiojuOhgFLYIMe%$r?Jzy-{6w7;(6ngrqggb?k>ow?KBTdd&#h|dxnM1BBNP7 zrS4L?)phdMl*PD8o?-1lXU!>AJ3l&RPJcWaeNL{;MtOg?I-Rfhg<*u6n-Z&9?_8dG z=&Px&^p^GqL?1g}Y%@0oBx8^BWZJZ30xos(Z|yfa?s#}*_D(fiQ6crTZE0J(F*Q!C zPc&WE!I=N5(=5aFeD@jsBCgL>D?53l!!#fgJ*Ph;EKnC_V}bC!6+E8Q%G`YFLB^jB zbg}gCec5U?Tv;jgq$f-&vK^mQ^ibZNmdBq9qXDix4Uga++=OlPI?91V$T=CMNljnS5x}8_ zD%8%|LV61ye#2DDx;AZz_|EBqb`O#kkPsiMq2@$LO&TD}tI&pmN)0vmORri?ui4-Y zq>5ad_vbC9)}-74ZY`wcy!T}hS?@*SA~N?SAkSY1k$4qa>#FARrZREf`D;Byn4S%hcPUF$p)OPMW zL7yeLC?%AzO2iJb9(FIQgbRKP;~O^7r}(ZYUGx(>nCq9wUQ$QJaR3){-4y%`+E-a$ z&<+mnwh5SBCme4Pj*9_Re1$&u`Lp8lPP%Un+(6-2k_Cgpod4nYU<)PSV+5@Jf6%@< z78H_$K^q4h+(PBx=hyo)bG`2>TyKZ8z`H#cvBWJF_tgEfjlq2m!aKdMjt2W6RPg;8 z)*+*&U{PW#tP!!d*nakXmS&Ccf|$>$XTrxnR3cR&U3^HEEY(nLGrU*f)4!!-Q&(#J zS7s3agaDulKrUm1GUYOa+=4-bZdNZu%cTNLa-|cANz01pZCKSpqM{I}hP#&};%LqWQZ!1af=eY#+zg?@ z6CG&@rrs@)_P{1RSiSCpK z3d)KHxJj0Cz-6(RnFL@rF)S7<2dmvB%LTE_>niouj8E<#p{U8prmk?+~b4bzbYNhl8|OdqKX!l^g{Cg E1!u(lNdN!< delta 2726 zcmeH|YfKbZ6vyXr=XGc9+y$}=2sB&k7edtyd^5m8DhRbM$kN!Rjl0TY)y8JO^n-08 z(AHoY6V@i>HdKYAwnS4RbS;y zch3L*?m1`9y)%umqQ;mvvLKo1Vi@KIwOiMkoe$=6Ojl1Zh^2+p+1`{M?Z?ZDgpcuc zeAP$_f(w{q`d*k0G*1UuV>-Yk#e{yUO(itfCryZAhgNX(&f;Nn-B0nbDfVOOa5iB5 zY#B)^zz>i)FRuwlZRn9WI$%c!E%dW_=*iotJQPQdt#~uWJ9-;=)ggR&TXlUNSBFb& zIi0c={=&AfL36r>U0*k0n)X*^w)v;6`QO;)ley!xhEW>o=Bsfr-)^Poh}VXmwb1Y8 zp+DNu@1LdUxqKUX-a>zvhhDOyznSPoJ34NmV}D1ZYUMa~UVV-tQw6qYe_F^NHpG~# z0GdPi)gJC=Gu%)1aKBjW1sj`qKfl}2NfVv0qgO2S@;vm09ldU5JZ;Z-)xwIhlV&c$-!EQgm5;R9$VjN0qaXihL=(fd6ioow6+Pp78|5}TUBr8A(|=l7Ra`HN*H*OTX2LN+zP@&)Ofq@<=& zIj+WUY(h8AG{9_6ngS{qd%;rOW%hP z@<9|PkqJN8PTU(+oy4NBh2(z-eMZ$mcuod+si9(09F)^YVGq>Ba$j{x)he^B-CS=B zf{sj$&0$b8%7<@2pLAYo7s65vzgqCJIsARJD3vvG)~Qd4bnh6ihE&9p%{_4C|4721 zNepnEAgqQBa01WZ-U$`B4bVu3t)mziz@gDm2LgO4(@vxa%5As?kHX#XX}AoE%CNFW z*{tL%P(C7Wm&;`+9g`wbg`|te#faz^UBVfmLkI~=z$7>bwtzw)@Pqsgeie_nA+Ck< zaflsaTi9wgof%=;>2|txEo(v|=X^YiVRrb+{ngcv_yZ*mEiWqxkOL8GFl@j zC@V`himbCFi`hVSwjn1Ol{JBQQZ!vzx*;KiOkn|uoKi(nIjAdgi27G%G4(be+^Hog zx%CO5E&-f)-SpKZe6`kOKq_ZdNcvtzvXWbyP-{#zop|?Z$;#py>MPD-)+dN?8&XK+ zIFhBs>*E4xX+v%j9HV&7I_fFNVuIG<5-GfB3c-XBFojG~bXKEv2B?qLxt5&VMawZu zG}h$ceW2H^0XO7vJ%bP9mvBAZ5RYM!eE_w!P9WOCqI)fj_xmnw;S+k-qJQaL_+Iz! z=I*_Q{`&xSm@Rbc^eD!3`iV7k3F+h+G_aaAvsH6|AK@M~Rw??TV xR=1C#OTd!^}H6! Date: Tue, 4 Apr 2023 13:48:12 +0200 Subject: [PATCH 084/209] Added logging to condition parser --- modules/compliance/compliance_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 262d62c..fdceec2 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -22,6 +22,7 @@ def convert_signature_algorithm(sig_alg: str) -> str: class ConditionParser: def __init__(self, user_configuration): + self.__logging = Logger("Condition parser") self.expression = "" self._logical_separators = ["and", "or"] # simple regex to find all occurrences of the separators @@ -150,8 +151,7 @@ def _evaluate_condition(self, condition): return condition if condition not in self.instructions and \ (" " not in condition and condition.split(" ")[0] not in self.instructions): - # TODO use logging module - print("Invalid condition: ", condition, " returning False") + self.__logging.warning("Invalid condition: " + condition + " returning False") return "False" tokens = condition.split(" ") field = tokens[0] From 0072d1ea8373df284161d4998274893563b12be5 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 5 Apr 2023 14:38:46 +0200 Subject: [PATCH 085/209] Added "NOTE" keyword to conditions. Replaced sheets_to_check in the input with aliases --- .../compliance/condition_instructions.json | 3 +- modules/compliance/compare_one.py | 12 +++-- modules/compliance/compliance_base.py | 44 ++++++++++++------ requirements.db | Bin 1056768 -> 1056768 bytes 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index 1d0da86..421a6e2 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -7,5 +7,6 @@ "YEAR": "FUNCTION check_year", "VLP": "FUNCTION check_vlp", "CA": "FUNCTION check_ca", - "THIS": "FUNCTION check_this" + "THIS": "FUNCTION check_this", + "NOTE": "FUNCTION add_notes" } \ No newline at end of file diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 24ae8f0..fd2e5c6 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -41,15 +41,19 @@ def _worker(self, sheets_to_check): to_use = self.level_to_use(levels) level = levels[to_use] has_alternative = self._condition_parser.entry_updates.get("has_alternative") - if has_alternative: + additional_notes = self._condition_parser.entry_updates.get("notes", "") + if has_alternative or additional_notes: # This is to trigger the output condition. This works because I'm assuming that "THIS" is only # used in a positive (recommended, must) context. valid_condition = True self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) - - if has_alternative and self._output_dict[sheet].get(name) and \ - isinstance(condition, str) and condition.count(" ") > 1: + note = "" + if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: parts = entry[condition_index].split(" ") # Tokens[1] is the logical operator note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + if additional_notes: + note += "\nNOTE:" + note += "\n".join(additional_notes) + if self._output_dict[sheet].get(name): self._output_dict[sheet][name] += note diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index fdceec2..d3ba91a 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -151,7 +151,7 @@ def _evaluate_condition(self, condition): return condition if condition not in self.instructions and \ (" " not in condition and condition.split(" ")[0] not in self.instructions): - self.__logging.warning("Invalid condition: " + condition + " returning False") + self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") return "False" tokens = condition.split(" ") field = tokens[0] @@ -161,7 +161,8 @@ def _evaluate_condition(self, condition): assert config_field[8] == " " args = { "data": to_search, - "enabled": self._enabled + "enabled": self._enabled, + "tokens": tokens[1:] } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) else: @@ -211,6 +212,7 @@ def __init__(self): self._config_class = None self._database_instance.input(["Guideline"]) self._guidelines = [name[0].upper() for name in self._database_instance.output()] + self._alias_parser = AliasParser() def level_to_use(self, levels, security: bool = True): """ @@ -245,7 +247,7 @@ def input(self, **kwargs): :Keyword Arguments: * *standard* (``list``) -- Guidelines to check against - * *sheets_to_check* (``dict``) -- dictionary of sheets that should be checked in the form: sheet:version_of_protocol + * *guidelines_to_check* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used * *apache* (``bool``) -- Default to True, if false nginx will be used @@ -296,9 +298,11 @@ def _worker(self, sheets_to_check): def run(self, **kwargs): self.input(**kwargs) - sheets_to_check = kwargs.get("sheets_to_check") - val = Validator() - val.dict(sheets_to_check) + guidelines_string = kwargs.get("guidelines_to_check") + self._validator.string(guidelines_string) + guidelines_list = guidelines_string.split(",") if "," in guidelines_string else [guidelines_string] + sheets_to_check = self._alias_parser.get_sheets_to_check(guidelines_list) + self._validator.dict(sheets_to_check) self._worker(sheets_to_check) return self.output() @@ -451,7 +455,7 @@ def prepare_testssl_output(self, test_ssl_output): def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None action = None - entry_level = get_standardized_level(entry_level) + entry_level = get_standardized_level(entry_level) if entry_level else None if entry_level == "must" and valid_condition and not enabled: information_level = "ERROR" action = "has to be enabled" @@ -550,13 +554,17 @@ def _evaluate_entries(self, sheets_to_check, columns): potential_levels = self._condition_parser.entry_updates.get("levels") level = potential_levels[self.level_to_use(potential_levels)] has_alternative = self._condition_parser.entry_updates.get("has_alternative") + additional_notes = self._condition_parser.entry_updates.get("notes", "") if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: parts = entry[condition_index].split(" ") # Tokens[1] is the logical operator - notes[-1] = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + notes[-1] += f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" # This is to trigger the output condition. This works because I'm assuming that "THIS" # is only used in a positive (recommended, must) context. valid_condition = True + if additional_notes: + notes[-1] += "\nNOTE:" + notes[-1] += "\n".join(additional_notes) conditions.append(valid_condition) levels.append(level) @@ -698,7 +706,7 @@ class CustomFunctions: def __init__(self, user_configuration): self._user_configuration = user_configuration self._validator = Validator() - self._entry_updates = {"levels": []} + self._entry_updates = {"levels": [], "notes": []} self._operators = { ">": lambda op1, op2: op1 > op2, "<": lambda op1, op2: op1 < op2, @@ -732,13 +740,15 @@ def check_year(self, **kwargs): return parsed_date.date() > actual_date def check_vlp(self, **kwargs): + status = kwargs.get("data", "").lower() == "true" result = False for version in range(3): enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) - if enabled: + if enabled and not status: result = True self._entry_updates["levels"].append("must not") - return result + # This final operation should give True only if both status and result are True or both are False + return (result and status) or not (result or status) def check_ca(self, **kwargs): to_check = kwargs.get("data", None) @@ -783,12 +793,17 @@ def check_this(self, **kwargs): self._entry_updates["has_alternative"] = True return enabled + def add_notes(self, **kwargs): + note = " ".join(kwargs.get("tokens", [])) + self._entry_updates["notes"].append(note) + return True + @property def entry_updates(self): return self._entry_updates def reset(self): - self._entry_updates = {"levels": []} + self._entry_updates = {"levels": [], "notes": []} class AliasParser: @@ -840,7 +855,7 @@ def is_valid(self, alias): if "_" not in alias and alias.upper() not in self._guidelines: raise ValueError(f"Alias {alias} not valid") tokens = alias.split("_") - guideline = tokens[0] + guideline = tokens[0].upper() if guideline not in self._guidelines_versions: raise ValueError(f"Invalid guideline in alias: {alias}") used_sets = set() @@ -858,9 +873,10 @@ def is_valid(self, alias): def get_sheets_to_check(self, aliases): sheets_to_check = {} for alias in aliases: + alias = alias.strip() self.is_valid(alias) tokens = alias.split("_") - guideline = tokens[0] + guideline = tokens[0].upper() tokens.append("") for i, sheet in enumerate(self._sheets_versions_dict): if sheets_to_check.get(sheet) is None: diff --git a/requirements.db b/requirements.db index a12646c899fc23e7867e0e972bab2a71dec251f9..6dcb30f291bce521ff3f5c204902c0a0b0fd4d86 100644 GIT binary patch delta 3937 zcmeHJU2IfE6ux(N?*81LId{7(TPf1pA5eZ<3zcAiw9qE4ffiUa;b~jCx3JRgHr*B# zftDDBM3UBk+~`Y5ls4)^AvL{;iL?&{B^rv2LirIIBZ?YnwQ6hu*E4q)x)9r3Y!FOdFrZ>FQhCL61 z*mIX*kfru1IKNAYG2=ic!1wDbi1=k$3ViGq{OWD+N+PSK)QWpJF0nn%neiyWM9$xa z%ELt+zcK&!X1tC0e@^jRi3&&Y>u%9`gC+308SiG;@!PO{#8K%Q zsC%({s%xw#E5E~KmY*PB3gWIb!O8n(KfbHp$Q~ebs-IuAMysu^i|JZ*B&1bTt}oSM zo1@KjAuSrIds*A4Yt1n|q=jp>rp>w*(;K(yjap5#zP>pUuBmR)W7>=1rp;)H#F`qb z!x23+KcsIB*XUYJU09Da#mo%@WN|xLUsfJ4_6LP$tkLelpn%PR&56y0&20__#YL(= z#=!;Xfp(~eQpl!pdY+o}E!wlTwM*{F&+q0vn&jmD9@|__EUpLy%1g?O9XrK^-fTCT zeQ0)B=F*kuIJQ$PEX;DE!;21wft`GupH^71T45+dA$7zHijlC&(6 zwA&L~1p-D(i?~9-#RJ90@fNWty@7)fcn=Q19@q{INcuW3`r_ay6-&xw zB1r=8vGMMbG8ZQ*0`If&E|w$Dxf3%dn{mS~S>U}k-eINbOiTov7@uUS{uw2KfJ{ij zYQu?(vnztzZ{rggjkbwX(>HN&4nBf5cmWyI=r#H^?WS+gO_)(R^=2b`85Vo1quauD zb=62y{YkOVmy7&mSo|d&EFa_JtK!l{7I{hQ-RR3f`Z5?um;@OMI>e`^?`K^;U@+jI0Wf$C8)+;+gX_?#$;pjcadu!BNEfbLG)GDV|o1T-dHe3o=!xP+>@1DWMA!XuEiO2aq8 zXXzORy_=Ysg<=K~IaxvyA(NCRWJ783I;|jo;Zt*G3dY_Mm^o(_N*oJsw~}N6?e2KH z(S!KePoVJe`EmVZu7P4c^^Ka`z!(~VdDA3#_!MR>2N$6iHF_9U!gN%aXXz2Tm)4>h zDx;8#r{?wG2Z}2!b-n;pztKk3nci7=PF^2+TPGtN^r&(1`y0_qHFfI-jN vQQ#UlNH_KYi$>zSjJQNF<1G<=5!5FitY}>D?>e04Y_j0;+uW8a=Z60QxD4Mp delta 3975 zcmeHKTWl0n7@o_S-JRR{2iVdLMc8I76@kz|xu`{3N=fU5f-5HQ;1+F>#==&FXll|$ zQBe%G5W@IGjfwPu5I{^155}$!1dv#?YDEgVQMqYo7ZKw^Vf|-j3$xo-6HIKfb7tp! z-}%q?pMTEGH=E++o8sl|69h72`^1IV9<1A2E2ZED{Kdx9k&EEvZf)c^?z7G5$d+`3 zXX%Kb#iieoaLpm`S6OPNIM$H*U47?Ss@CcoaP^&Lsi-(MDE;c{yUbD@7VrDvzDv-V z>K221AG`jONP_&gES(nnziW4%z?O$zY`KfjYiT+P z-V0gum_`5dRb&h^=wl^JzH^-%xfnb)-=9k?u6zhcBAUoU?Z$XtF{GczIE^k5e4T zQOa|&F7bH+@2#p_RLv!P4(Ny_oY%Q@=GKTullXB0Po4}{_&CYI9Gw~=4kj!Lhkcx= z>XMA}+c}uYHf4NZ83(7~W7r1u@Cb~jm+4Wuo3_wpD0emu6ywqs6j)39M6{)`scBW_ zWVZGwa{|S?(e~RFhZ8Qfw-k~fAth7q;3-rMeI1HVz3*Ls8@fKQ%xs^f8koW>D zMP^`5F&uC>uMy>N;NiruAWp|{0=Xq|(BTA}qU2-6GF1UdMkN^`EV*A9Zv>X#sY@m9 z%q#Q+{gAfO<*0q$-LA|0lgh0BU0s~*;wp=^UH-MQl+7;?Lfr5TmAMrNx4z-I=vv$< z^$qo-yTrXqMju}P$%og7O)~oIy7Pj+d+(GHWf^^TDR-|LEE!S#&5R_}A=vYkYP^+H zyOJ6yXJn~91iKO{=_KX4(Md{lgOO(aCNMszi9tpV=tGlt0po zD^fdxU3EIa&JUoRO0bwiZ+j57!t<~g%}6N-lO=QO00enr_6$IwP$ZfU4MLGnB$!+A zY@Ql~={T<2GGpiA2RH%UIBp-9&25Tjy6t_XEVG-jtd5-(UH;X)(x&7E)^PA6`netG z+k=Wjc@tA#|9|g=7cFHvA4^i5+40B^h0&6&g4}lYIGtjNcRC H3NG;vH Date: Fri, 7 Apr 2023 12:04:58 +0200 Subject: [PATCH 086/209] Added configuration files for the new modules, started working on a way to pass arbitrary arguments to the core and made preanalysis complete for compare modules --- configs/modules/server/compare_many.json | 40 ++++++++++++++++++++ configs/modules/server/compare_one.json | 45 +++++++++++++++++++++++ configs/modules/server/generate_many.json | 35 ++++++++++++++++++ configs/modules/server/generate_one.json | 35 ++++++++++++++++++ modules/compliance/compliance_base.py | 26 ++++++++++++- modules/core.py | 20 +++++++++- run.py | 40 +++++++++++++++++++- tlsa/tlsa.py | 1 + 8 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 configs/modules/server/compare_many.json create mode 100644 configs/modules/server/compare_one.json create mode 100644 configs/modules/server/generate_many.json create mode 100644 configs/modules/server/generate_one.json diff --git a/configs/modules/server/compare_many.json b/configs/modules/server/compare_many.json new file mode 100644 index 0000000..081d8e8 --- /dev/null +++ b/configs/modules/server/compare_many.json @@ -0,0 +1,40 @@ +{ + "input": [ + { + "name": "guidelines_to_check", + "type": "str", + "description": "string containing the names of the guidelines that should be checked in the form: guideline1_version1_version2, guideline2_version1", + "required": "True" + }, { + "name": "actual_configuration_path", + "type": "str", + "description": "The configuration to check", + "required": "True" + }, { + "name": "hostname", + "type": "str", + "description": "Hostname on which testssl should be used to retrieve the information", + "required": "True" + }, { + "name": "config_output", + "type": "str", + "description": "The path and name of the output file", + "required": "False" + }, { + "name": "custom_guidelines", + "type": "dict", + "description": "dictionary with form: { sheet : {guideline: name: {\"level\":level}}", + "required": "False" + } + ], + "description": "This modules runs the compliance check against multiple guidelines.", + "path": "modules/compliance/compare_many.py", + "class_name": "CompareMany", + "output": [ + { + "name": "results", + "type": "dict", + "description": "results of the scan" + } + ] +} \ No newline at end of file diff --git a/configs/modules/server/compare_one.json b/configs/modules/server/compare_one.json new file mode 100644 index 0000000..3746d88 --- /dev/null +++ b/configs/modules/server/compare_one.json @@ -0,0 +1,45 @@ +{ + "input": [ + { + "name": "guidelines_to_check", + "type": "str", + "description": "string containing the name of the guideline that should be checked in the form: guideline_version1_version2", + "required": "True" + }, { + "name": "actual_configuration_path", + "type": "str", + "description": "The configuration to check, not needed if generating", + "required": "False" + }, { + "name": "hostname", + "type": "str", + "description": "Hostname on which testssl should be used to retrieve the information", + "required": "True" + }, { + "name": "apache", + "type": "str", + "description": "Configuration type for generator. Default to True, if false nginx will be used", + "required": "False" + }, { + "name": "config_output", + "type": "str", + "description": "The path and name of the output file", + "required": "False" + }, { + "name": "custom_guidelines", + "type": "dict", + "description": "dictionary with form: { sheet : {guideline: name: {\"level\":level}}", + "required": "False" + } + ], + "description": "This modules runs the compliance check against a single guideline.", + "path": "modules/compliance/compare_one.py", + "class_name": "CompareOne", + "output": [ + { + "name": "results", + "type": "dict", + "description": "results of the scan" + } + ] +} \ No newline at end of file diff --git a/configs/modules/server/generate_many.json b/configs/modules/server/generate_many.json new file mode 100644 index 0000000..07404e9 --- /dev/null +++ b/configs/modules/server/generate_many.json @@ -0,0 +1,35 @@ +{ + "input": [ + { + "name": "guidelines_to_check", + "type": "str", + "description": "string containing the name of the guideline that should be used to generate in the form: guideline_version1_version2", + "required": "True" + }, { + "name": "apache", + "type": "str", + "description": "Configuration type for generator. Default to True (apache), if false nginx will be used", + "required": "False" + }, { + "name": "config_output", + "type": "str", + "description": "The path and name of the output file", + "required": "False" + }, { + "name": "custom_guidelines", + "type": "dict", + "description": "dictionary with form: { sheet : {guideline: name: {\"level\":level}}", + "required": "False" + } + ], + "description": "This modules generates a configuration file after multiple guidelines.", + "path": "modules/compliance/generate_many.py", + "class_name": "GenerateMany", + "output": [ + { + "name": "results", + "type": "dict", + "description": "results of the scan" + } + ] +} \ No newline at end of file diff --git a/configs/modules/server/generate_one.json b/configs/modules/server/generate_one.json new file mode 100644 index 0000000..7097bc6 --- /dev/null +++ b/configs/modules/server/generate_one.json @@ -0,0 +1,35 @@ +{ + "input": [ + { + "name": "guidelines_to_check", + "type": "str", + "description": "string containing the name of the guideline that should be used to generate in the form: guideline_version1_version2", + "required": "True" + }, { + "name": "apache", + "type": "str", + "description": "Configuration type for generator. Default to True (apache), if false nginx will be used", + "required": "False" + }, { + "name": "config_output", + "type": "str", + "description": "The path and name of the output file", + "required": "False" + }, { + "name": "custom_guidelines", + "type": "dict", + "description": "dictionary with form: { sheet : {guideline: name: {\"level\":level}}", + "required": "False" + } + ], + "description": "This modules generates a configuration file after one guideline.", + "path": "modules/compliance/generate_one.py", + "class_name": "GenerateOne", + "output": [ + { + "name": "results", + "type": "dict", + "description": "results of the scan" + } + ] +} \ No newline at end of file diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index d3ba91a..e955040 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,4 +1,5 @@ import datetime +import itertools import json import re from pathlib import Path @@ -246,7 +247,6 @@ def input(self, **kwargs): :type kwargs: dict :Keyword Arguments: - * *standard* (``list``) -- Guidelines to check against * *guidelines_to_check* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used @@ -820,6 +820,30 @@ def __init__(self): self._aliases = load_configuration("alias_mapping", "configs/compliance/alias/") self._default_versions = load_configuration("default_levels", "configs/compliance/alias/") + def list_aliases(self): + print("Alias mapping:") + for el in self._aliases: + print(el, ":", self._aliases[el]) + import sys + sys.exit(0) + + def list_strings(self): + print("Valid strings:") + for guideline in self._guidelines_versions: + sets = [self._guidelines_versions[guideline][k] for k in self._guidelines_versions[guideline]] + # this first list comprehension is needed to later check if there are any versions. + combinations = [combination for combination in itertools.product(*sets)] + if combinations and combinations[0]: + print("Strings for guideline: ", guideline) + # First I join the output from itertools.product using "_" then I prepend guideline_ to it and in the + # end I join all the versions using "," + result = ",".join([guideline + "_" + "_".join(combination) for combination in combinations]) + print(result) + else: + print("Guideline ", guideline, " doesn't have any special version") + print("") + print("NOTE: if a version is omitted the default one will be used.") + def _fill_sheets_dict(self): for table in self._database_instance.table_names: tokens = re.split(self._splitting_regex, table, flags=re.IGNORECASE) diff --git a/modules/core.py b/modules/core.py index 1284cc0..6526db1 100644 --- a/modules/core.py +++ b/modules/core.py @@ -59,6 +59,7 @@ def __init__( openssl_version=None, ignore_openssl=False, stix=False, + compliance_args=None ): """ :param hostname_or_path: hostname or path to scan @@ -83,6 +84,8 @@ def __init__( :type ignore_openssl: bool :param stix: generate stix report :type stix: bool + :param compliance_args: arguments for compliance module + :type compliance_args: dict """ if to_exclude is None: to_exclude = [] @@ -105,6 +108,7 @@ def __init__( openssl_version=openssl_version, ignore_openssl=ignore_openssl, stix=stix, + compliance_args=compliance_args ) self.__cache[configuration] = self.__load_configuration(modules) self.__exec( @@ -193,6 +197,9 @@ def input(self, **kwargs): else: kwargs["output_type"] = self.Report.HTML # or default HTML + if kwargs["compliance_args"] is None: + kwargs["compliance_args"] = {} + ext = self.__string_output_type(kwargs["output_type"]) # tostring kwargs["output"] = f"{file_name}.{ext}" # final file name @@ -319,7 +326,7 @@ def __conf_analysis( return results def __preanalysis_testssl( - self, testssl_args: list, type_of_analysis: Analysis, hostname: str, port: str + self, testssl_args: list, type_of_analysis: Analysis, hostname: str, port: str, full_analysis: bool ): """ Preanalysis of testssl @@ -332,6 +339,8 @@ def __preanalysis_testssl( :type hostname: str :param port: port to use :type port: str + :param full_analysis: if true a complete analysis is performed + :type full_analysis: bool :return: preanalysis :rtype: dict """ @@ -339,6 +348,8 @@ def __preanalysis_testssl( type_of_analysis == self.Analysis.HOST or type_of_analysis == self.Analysis.DOMAINS ): + if full_analysis: + testssl_args = [] self.__logging.debug( f"Starting preanalysis testssl with args {testssl_args}..." ) @@ -614,8 +625,13 @@ def __exec_anaylsis( ignore_openssl=self.__input_dict["ignore_openssl"], ) # TODO: better output report else: + full_analysis = False + for module in loaded_modules: + if module.startswith("compare"): + # A full analysis is needed with these modules + full_analysis = True self.__preanalysis_testssl( - testssl_args, type_of_analysis, hostname_or_path, port + testssl_args, type_of_analysis, hostname_or_path, port, full_analysis ) self.__preanalysis_tls_scanner( diff --git a/run.py b/run.py index b900f59..6a54fa2 100644 --- a/run.py +++ b/run.py @@ -1,8 +1,21 @@ import argparse from argparse import RawTextHelpFormatter + from tlsa.tlsa import Tlsa from utils.globals import version + +class ComplianceAction(argparse.Action): + def __init__(self, option_strings, dest, nargs=None, **kwargs): + super().__init__(option_strings, dest, nargs, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + if not isinstance(namespace.__getattribute__(self.dest), dict): + namespace.__setattr__(self.dest, {}) + dictionary = namespace.__getattribute__(self.dest) + dictionary[option_string.strip("-")] = values + + if __name__ == "__main__": parser = argparse.ArgumentParser( prog="TLSAssistant", @@ -41,7 +54,7 @@ choices=["pdf", "html"], default=None, help="The type of the report output.\nOutput type can be omitted and can be obtained" - " by --output extension.", + " by --output extension.", ) parser.add_argument( "-o", @@ -101,7 +114,7 @@ nargs="?", default="", help="Apply fix in the current configuration.\n Give a path if using -s.\ni.e." - "\n\tpython3 run.py -s fbk.eu --apply-fix myconf.conf", + "\n\tpython3 run.py -s fbk.eu --apply-fix myconf.conf", ) configurations = parser.add_mutually_exclusive_group() configurations.add_argument( @@ -135,7 +148,30 @@ help="Generate STIX2 compliant output.", default=False, ) + + parser.add_argument( + "--guideline", + type=str, + nargs=1, + action=ComplianceAction, + dest="compliance_args", + help="A string containing the names of the guidelines that should be checked in the form: " + "guideline_version1_version2 in the case of multiple guidelines they should be comma separated. " + "Use \"list\" for a list of valid strings and \"aliases\" for a list of aliases." + ) + + parser.add_argument( + "--apache", + type=bool, + nargs=1, + action="append", + default=True, + dest="compliance_args", + help="Default to True. If True the output configuration will have apache syntax, if false nginx will be used." + ) + # todo add default aliases configurations for analysis # configurations.add_argument() args = parser.parse_args() + print(args) tlsa = Tlsa(args) diff --git a/tlsa/tlsa.py b/tlsa/tlsa.py index 4daf72f..e66784e 100644 --- a/tlsa/tlsa.py +++ b/tlsa/tlsa.py @@ -124,6 +124,7 @@ def __start_analysis(self, args): group_by=args.group_by, apply_fix=args.apply_fix, stix=args.stix, + compliance_args = args.compliance_args ) elif args.apk: Core( From 4171071a95c542051b6d16fa51c747d87f209097 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 12 Apr 2023 10:13:16 +0200 Subject: [PATCH 087/209] Added database generation and filling scripts to the tool in a different folder --- DatabaseFiller/database_filler.py | 280 +++++ DatabaseFiller/guidelines.xlsx | Bin 0 -> 151488 bytes DatabaseFiller/output.prisma | 1018 +++++++++++++++++ DatabaseFiller/requirements.db | Bin 0 -> 1056768 bytes DatabaseFiller/schema_creator.py | 89 ++ .../schema_generator/template.prisma | 118 ++ DatabaseFiller/utils/__init__.py | 0 DatabaseFiller/utils/configs.py | 60 + DatabaseFiller/utils/filler_utils.py | 132 +++ .../compliance/requirements.db | Bin out.config | 12 + 11 files changed, 1709 insertions(+) create mode 100755 DatabaseFiller/database_filler.py create mode 100644 DatabaseFiller/guidelines.xlsx create mode 100644 DatabaseFiller/output.prisma create mode 100644 DatabaseFiller/requirements.db create mode 100644 DatabaseFiller/schema_creator.py create mode 100644 DatabaseFiller/schema_generator/template.prisma create mode 100644 DatabaseFiller/utils/__init__.py create mode 100644 DatabaseFiller/utils/configs.py create mode 100644 DatabaseFiller/utils/filler_utils.py rename requirements.db => configs/compliance/requirements.db (100%) create mode 100644 out.config diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py new file mode 100755 index 0000000..8883d62 --- /dev/null +++ b/DatabaseFiller/database_filler.py @@ -0,0 +1,280 @@ +import sqlite3 +from typing import Tuple + +import pandas as pd + +from utils.configs import sheets_mapping, different_names_pos, sheet_columns, guidelines, converters, has_merged_names +from utils.filler_utils import get_requirements_columns, get_columns_count_for_guideline, split_sheet, \ + get_version_name_for_database, get_guideline_name_for_database, is_double_guideline, get_first_col_for_guideline, \ + get_column + +dataframe = pd.read_excel("guidelines.xlsx", header=[0, 1], sheet_name=None, converters=converters, dtype=str) + +sheet_with_extra_table = { + "TLS extensions": ("applies to version", "TlsVersionExtension") +} + +conn = sqlite3.connect("requirements.db") +cur = conn.cursor() + + +def prepare_database(): + cur.execute("SELECT name FROM sqlite_master WHERE type='table'") + for table in cur.fetchall(): + cur.execute("DELETE FROM " + table[0]) + conn.commit() + + +def insert_guideline_info(): + cur.executemany("INSERT OR REPLACE INTO Guideline VALUES (?, ?)", + [(guideline, guidelines[guideline]) for guideline in guidelines]) + + +def get_cell_for_df(df: pd.DataFrame, row_index: int, header): + col_index = 0 + for col_index, col in enumerate(df.columns): + if col[0] == header[0]: + break + return df.iloc[row_index: row_index + 1, col_index:col_index + 1].iat[0, 0] + + +def get_name_from_index_for_sheet(index, sheet_name: str) -> str: + """ + Gets the name of the item for that row. Some sheets have the name column in a different position, for that case + see the different_names_pos dictionary + :param index: row index + :param sheet_name: sheet in which the search should be done + :return: item_name: the name for the row at index in the sheet + """ + column = different_names_pos.get(sheet_name, (0, 1))[0] + return dataframe[sheet_name].iloc[index:index + 1, column:column + 1].iat[0, 0] + + +def get_additional_info(index, sheet_name: str): + column, lengths = different_names_pos.get(sheet_name, (0, 1)) + return_vals = [] + tmp_df = dataframe[sheet_name].iloc[index:index + 1, column:column + lengths] + if lengths > 1: + for i in range(1, lengths): + val = tmp_df.iat[0, i] + return_vals.append(val) + return return_vals + + +def already_parsed(col_name: str) -> bool: + for _, c2 in sheet_with_extra_table.items(): + if c2[0] == col_name.strip(): + return True + return False + + +def values_to_add(r: pd.Series, columns: pd.Index) -> Tuple: + """Given a series of values checks if those values belong to columns that were already parsed + :param r The row (Series) containing the values that need to be checked + :param columns: The columns of the dataframe from which the row is taken + """ + val_list = r.to_list() + i = 0 + for c in columns: + if already_parsed(c[0]): + val_list.pop(i) + else: + i += 1 + return tuple(val_list) + + +def has_extra_table(sheet_name: str) -> Tuple: + return sheet_with_extra_table.get(sheet_name, ()) + + +def fill_extra_table(sheet_name: str) -> bool: + """ + This function takes the name of a sheet as a param, uses it to get the column names from which it should get data + and the table in which to insert the data using the sheet_with_extra_table dictionary and then adds this data to the + database. + + :param sheet_name: the sheet that has an extra table + :return: False if the sheet doesn't have an extra table, True if it committed to the database + """ + column, table = sheet_with_extra_table.get(sheet_name, (None, None)) + if not column or not table: + return False + file_sheet: pd.DataFrame = dataframe[sheet_name] + # The first column is almost always the names column + names: pd.Series = get_column(file_sheet, 0) + # Get only the columns that must be inserted in the extra table + versions = file_sheet.filter(like=column) + versions_names = {} + insertion_query = f"INSERT OR REPLACE INTO {table} VALUES (?, ?)" + values_to_insert = [] + # prepare the mapping from index to column + for pos, version in enumerate(versions.columns.to_list()): + versions_names[pos] = version[1] + + for pos, content in versions.iterrows(): + name = names[pos] + # This variable i is used to cycle through the column's name without having to add it to the dataframe + # It can probably be avoided by using the join in pandas, but I can't get it to work + i = 0 + for c in content: + if pd.notna(c): + values_to_insert.append( + (versions_names[i % len(versions.columns)], name)) + i += 1 + cur.executemany(insertion_query, values_to_insert) + conn.commit() + return True + + +if __name__ == "__main__": + prepare_database() + insert_guideline_info() + guidelines_mapping = {} + for guideline in guidelines: + guidelines_mapping[guideline.upper()] = guideline + for sheet in dataframe: + sheet_mapped = sheets_mapping.get(sheet.strip()) + if isinstance(sheet, str) and sheet_mapped: + done = False + values = [] + if has_extra_table(sheet): + fill_extra_table(sheet) + general_dataframe, guidelines_dataframe = split_sheet(dataframe[sheet]) + values_tuple = () + # old_values is needed for some strange cases like key_signature + old_values = [] + for row in general_dataframe.iterrows(): + # row[0] is the index, row[1] is the actual content of the line + values_tuple = values_to_add(row[1], general_dataframe.columns) + if not len(old_values): + old_values = [v for v in values_tuple] + else: + tmp_list = [] + for i, v in enumerate(values_tuple): + if pd.isna(v) and v != old_values[i]: + tmp_list.append(old_values[i]) + else: + tmp_list.append(v) + values_tuple = tuple(tmp_list) + old_values = tmp_list + if values_tuple[0] != "Certificate Type": + values.append(values_tuple) + values_string = "(" + values_string += "?," * len(values_tuple) + # Remove last ',' and replace it with ')' + values_string = values_string[:-1] + ")" + sql_query = f"INSERT OR REPLACE INTO {sheet_mapped} VALUES " + values_string + cur.executemany(sql_query, values) + conn.commit() + values = [] + # Start of guideline specific part + requirements_columns = get_requirements_columns(guidelines_dataframe, sheet) + guidelines_columns_count = get_columns_count_for_guideline(guidelines_dataframe) + + values_dict = {} + last_item = "" + + # maybe this whole part can be rewritten using iloc + old_name = "" + for row in guidelines_dataframe.iterrows(): + row_dictionary = row[1].to_dict() + for header in row_dictionary: + # header[0] is guideline_name + item_name = get_name_from_index_for_sheet(row[0], sheet) + if pd.isna(item_name) and sheet in has_merged_names: + item_name = old_name + else: + old_name = item_name + guideline = get_guideline_name_for_database(header[0]) + version_name = get_version_name_for_database(header[1]) + table_name = sheet_mapped + guideline + version_name + content = row_dictionary[header] + if header[1] in requirements_columns[header[0]]: + # This is the case for sheets like cipher suite + if sheet_columns.get(sheet, {}).get(header[0]): + level_column = get_first_col_for_guideline(guidelines_dataframe, guideline) + level = get_cell_for_df(guidelines_dataframe, row[0], (guideline, level_column)) + # If the cell is empty and the level isn’t negative (must not, not recommended) + # then "must not" is used as the level. + if pd.notna(content) or level in ["not recommended", "must not"]: + content = level + else: + content = "must not" + + # this block is to prepare the dictionary + if not values_dict.get(table_name): + values_dict[table_name] = {row[0]: []} + if not values_dict[table_name].get(row[0]): + values_dict[table_name][row[0]] = [] + # end of the block + + # Vertically merged cells contain the value only in the first cell + if pd.isna(item_name) and not pd.isna(content): + item_name = values_dict[table_name][row[0] - 1][0] + + # First the guideline name is added + values_dict[table_name][row[0]].append(guidelines_mapping.get(guideline, guideline)) + + # Then the name of the row is added + values_dict[table_name][row[0]].append(item_name) + # If this table needs extra data it gets added here + for el in get_additional_info(row[0], sheet): + values_dict[table_name][row[0]].append(el) + + values_dict[table_name][row[0]].append(content) + + elif pd.notna(header[1]) and \ + get_first_col_for_guideline(guidelines_dataframe, header[0]) != header[1]: + # update all the lists of the same guideline + for t_name in values_dict: + if t_name.startswith(sheet_mapped + header[0]): + values_dict[t_name][row[0]].append(content) + if is_double_guideline(header[0]): + for other_guideline in header[0].split("+")[1:]: + other_name = get_guideline_name_for_database( + other_guideline) + other_table = sheet_mapped + other_name + version_name + values_dict[other_table] = values_dict[table_name] + + # Convert all the data into tuples to add them to the database and group them by guideline name + values_groups = {} + for table in values_dict: + # Get the number of columns for the actual table + table_columns_count = len(cur.execute(f"PRAGMA table_info({table})").fetchall()) + entries = values_dict[table] + + # This is to prevent the "this or X" condition to appear in tables that don't need it + # this "if" checks if the guideline has multiple versions for this sheet + if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: + for entry in entries: + entry = entries[entry] + # Since the problem is a condition it only verifies if there are four elements. + # Last element is the condition + # Second to last is the level + if len(entry) > 3 and pd.notna(entry[-1]): + if entry[-2][-1] != "°": + entry[-1] = None + + if not values_groups.get(table): + values_groups[table] = [] + for index in entries: + entry = entries[index] + if pd.notna(entry[1]) and entry[1] != "Certificate Type" and entry[1] != "NIST": + # The double check is needed because of the case Mozilla + AGID which share the same pointer to + # the list of values + if len(entry) < table_columns_count: + entry.insert(0, index) + # Every remaining column is filled with None + if len(entry) < table_columns_count: + entry.append(None) + values_groups[table].append(tuple(entry)) + for table in values_groups: + values = values_groups[table] + values_string = "(" + # The values list should contain tuples that are all the same size + values_string += "?," * (len(values[0])) + # Remove last ',' and replace it with ')' + values_string = values_string[:-1] + ")" + sql_query = f"INSERT OR REPLACE INTO {table} VALUES " + values_string + cur.executemany(sql_query, values) + conn.commit() diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9fb9302081ecc8e7b35d3d4e19a332cec2b18931 GIT binary patch literal 151488 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 CbE&)l literal 0 HcmV?d00001 diff --git a/DatabaseFiller/schema_creator.py b/DatabaseFiller/schema_creator.py new file mode 100644 index 0000000..5c0f326 --- /dev/null +++ b/DatabaseFiller/schema_creator.py @@ -0,0 +1,89 @@ +import os +import re +import subprocess +from typing import TextIO, Dict + +import pandas as pd + +from utils.configs import sheets_mapping, additional_keys, different_templates +from utils.filler_utils import split_sheet, get_requirements_columns, get_version_name_for_database, \ + get_guideline_name_for_database + +# IMPORTANT NOTE "sheet" means the original file name, "sheet_name" is the mapped one + +TEMPLATE_FILE = "schema_generator/template.prisma" +GUIDELINE_BLOCKS = 13 +RANDOM_STRING = "7WJsEz" + +# This dict is filled during the initialization +additional_templates = {} + + +# End of configurations + +def get_block(f: TextIO): + text = "" + last_line = "a" + while last_line != "}" + os.linesep and last_line != "": + last_line = f.readline() + text += last_line + return text + + +def get_template_for(sheet: str, template: str): + template = additional_templates.get(sheet, template) + original_key_ref = "" + lines = template.splitlines() + for index, line in enumerate(lines): + if "@relation" in line: + original_key_ref = template.splitlines()[index] + for var_name in additional_keys.get(sheet, []): + new_key_ref = original_key_ref.replace("[name", f"[name, {var_name},") + new_key_ref = new_key_ref.replace(",]", "]") + template = template.replace(original_key_ref, new_key_ref) + + return template.replace("Sheet", sheet) + + +def generate_template(df: Dict[str, pd.DataFrame]): + file = open(TEMPLATE_FILE, "r") + general_part = "" + for _ in range(GUIDELINE_BLOCKS): + general_part += get_block(file) + # this part is only needed to make the template compliant to prisma guideline. + # this two regexes are only needed because the number of spaces/tabs between type and name is variable. + general_part = re.sub(rf"Sheet{RANDOM_STRING} *Sheet{RANDOM_STRING}\[]", "", general_part) + for sheet in different_templates: + general_part = re.sub(rf"{sheet}{RANDOM_STRING} *{sheet}{RANDOM_STRING}\[]", "", general_part) + general_part += "\n// Guidelines tables" + with open("output.prisma", "w") as f: + f.write(general_part) + # Remove the "Sheet" part that is useless + get_block(file) + # Get the general template + general_template = get_block(file) + for table in different_templates: + additional_templates[table] = get_block(file) + for sheet in dataframe: + # sheet_name is the name that will be used in the database + sheet_name = sheets_mapping.get(sheet) + if not sheet_name: + continue + actual_template = get_template_for(sheet_name, general_template) + _, protocols_dataframe = split_sheet(df[sheet]) + requirements_columns = get_requirements_columns(protocols_dataframe, sheet) + # prepare the guidelines for the next step + for guideline in requirements_columns: + for column_name in requirements_columns[guideline]: + version_name = get_version_name_for_database(column_name) + guideline = get_guideline_name_for_database(guideline) + new_name = (guideline + version_name).upper() + with open("output.prisma", "a") as f: + f.write(actual_template.replace("7WJsEz", new_name)) + subprocess.call(["prisma", "format", "--schema=output.prisma"]) + subprocess.call(["prisma", "db", "push", "--schema=output.prisma"]) + + +if __name__ == "__main__": + dataframe = pd.read_excel("./guidelines.xlsx", header=[0, 1], sheet_name=None) + generate_template(dataframe) diff --git a/DatabaseFiller/schema_generator/template.prisma b/DatabaseFiller/schema_generator/template.prisma new file mode 100644 index 0000000..71bdf51 --- /dev/null +++ b/DatabaseFiller/schema_generator/template.prisma @@ -0,0 +1,118 @@ +datasource db { + provider = "sqlite" + url = "file:requirements.db" +} + +// COMMON DATA +model TlsVersion { + version Float @id + ExtTls TlsVersionExtension[] + Hash Hash[] +} + +model Guideline { + name String @id + updated_at String + KeyLengths7WJsEz KeyLengths7WJsEz[] + Sheet7WJsEz Sheet7WJsEz[] +} + +// GENERAL DATA +model CipherSuite { + name String @id + iana_code String + applicability String @default("") + signature String? @default("") +} + +model Extension { + name String @id + iana_code String + TlsVersionExtension TlsVersionExtension[] +} + +model Groups { + name String @id + iana_code String + supported_security String? +} + +model Protocol { + name String @id +} + +model Hash { + name String @id + iana_code String + tls_version Float + TlsVersion TlsVersion @relation(fields: [tls_version], references: [version]) +} + +model CertificateSignature { + name String @id + iana_code String + tls_version String +} + +model Misc { + name String @id +} + +model KeyLengths { + field_name String + name String + length Int + KeyLengths7WJsEz KeyLengths7WJsEz[] + + @@id([name, length]) +} + +model Signature { + name String @id + iana_code String + version String +} + +// SPECIAL TABLES +model TlsVersionExtension { + TlsVersion TlsVersion @relation(fields: [version], references: [version]) + version Float + extension Extension @relation(fields: [extension_name], references: [name]) + extension_name String + + @@id([version, extension_name]) +} + +// TEMPLATE DATA + +model Sheet { + name String @id + iana_code String + version String + Sheet7WJsEz Sheet7WJsEz[] +} + +model Sheet7WJsEz { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Sheet Sheet @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") + + @@id([id, name]) +} + +model KeyLengths7WJsEz { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") + + @@id([id, name, length]) +} diff --git a/DatabaseFiller/utils/__init__.py b/DatabaseFiller/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DatabaseFiller/utils/configs.py b/DatabaseFiller/utils/configs.py new file mode 100644 index 0000000..1b81222 --- /dev/null +++ b/DatabaseFiller/utils/configs.py @@ -0,0 +1,60 @@ +guidelines = { + "NIST": "", + "ANSSI": "", + "AgID": "", + "BSI": "", + "Mozilla": "" +} + +levels_mapping = { + "1": "must", + "2": "must not", + "3": "recommended", + "4": "not recommended", + "5": "optional", + "6": "" +} + +sheets_mapping = { + "Protocols": "Protocol", + "Cipher Suites": "CipherSuite", + "TLS extensions": "Extension", + "Supported groups": "Groups", + "Signature algorithms": "Signature", + "Hash Algorithm": "Hash", + "Certificate Signature": "CertificateSignature", + "Key lengths": "KeyLengths", + # "Certificate": None, at the moment the certificate table doesn't exist + "Misc": "Misc" +} + +# give both pos to start and number of columns to get +different_names_pos = { + "Key lengths": (1, 2) +} + +# If a sheet has some columns that should be considered as guideline "versions" but don't use the usual syntax they +# can be set in a list in a dictionary with "guideline": [col_index1, col_index2] that appears as a value for the +# respective sheet +sheet_columns = { + "Cipher Suites": { + "NIST": [1, 2, 3], + "BSI": [1, 2] + }, +} + +converters = { + # this function is needed to avoid having iana codes in the form 0.0 + ('IANA', 'Unnamed: 1_level_1'): lambda x: str(int(x)) if isinstance(x, float) else str(x) +} + +# If the sheet has vertically merged cells in the name column add it here +has_merged_names = ["Key lengths"] + +# list of sheet_names that need a different template, order is important +different_templates = ["KeyLengths"] + +# The syntax for this is: Sheet: list of keys +# it is assumed that a field with the same name of the key was added using the additional_fields dict +additional_keys = { +} diff --git a/DatabaseFiller/utils/filler_utils.py b/DatabaseFiller/utils/filler_utils.py new file mode 100644 index 0000000..6de8a5f --- /dev/null +++ b/DatabaseFiller/utils/filler_utils.py @@ -0,0 +1,132 @@ +from typing import Dict, List + +import pandas as pd + +from .configs import guidelines, levels_mapping, sheet_columns + + +def split_sheet(sheet: pd.DataFrame): + """ + Splits the sheet at the first guideline column to have two dataframes one with general data and the other with the + data of the guidelines. + :param sheet: the sheet that needs to be split + :return: the two dataframes + """ + guideline_column_index = get_first_guideline_column_pos(sheet) + # This split is done to make things more organised + general_dataframe: pd.DataFrame = sheet.iloc[:, :guideline_column_index] + protocols_dataframe: pd.DataFrame = sheet.iloc[:, guideline_column_index:] + return general_dataframe, protocols_dataframe + + +def get_requirements_columns(requirements_df: pd.DataFrame, sheet_name: str) -> Dict[str, List[str]]: + """Returns a dictionary containing Guideline_name as key and a list composed of columns that contain evaluations""" + # Get the first row (after the header) of the dataframe and convert it to a dict for easier access + row_dict = requirements_df.iloc[0:1, :].to_dict() + result_dict = {} + # col is the header so col[0] is the guideline_name and col[1] is the actual column name + for col in row_dict: + also_add = [] + if " " in col[0]: + parts = col[0].split(" ") + if "+" in parts[1]: + for el in parts[1].split("+")[1:]: + also_add.append(el.strip(")")) + if not result_dict.get(col[0]): + result_dict[col[0]] = [] + val = row_dict[col] + if val[0] in levels_mapping.values(): + result_dict[col[0]].append(col[1]) + for val in also_add: + if not result_dict.get(val): + result_dict[val] = [] + result_dict[val].append(col[1]) + for guideline in result_dict: + valid_indexes = sheet_columns.get(sheet_name, {}).get(guideline) + if valid_indexes: + result_dict[guideline] = get_column_names_from_indexes(requirements_df, guideline, valid_indexes) + return result_dict + + +def get_columns_count_for_guideline(df: pd.DataFrame) -> Dict: + """The header may have the same value at index 0 (the guideline) with different values at index 1. So it is useful to + know how many columns each guideline uses. + :param df Pandas dataframe. Specifically the guideline specific one. + """ + results = {} + values_count_dict = df.columns.value_counts() + for col in values_count_dict.keys(): + if results.get(col[0]): + results[col[0]] += values_count_dict[col] + else: + results[col[0]] = values_count_dict[col] + return results + + +def get_first_guideline_column_pos(s: pd.DataFrame): + """Searches the first column in the dataframe that contains guideline specific data and returns its index. + :param s: The dataframe in which the column should be searched + """ + for i, c in enumerate(s.columns): + first_row = c[0] + c_name = first_row.split(" ")[0] if " " in first_row else first_row + if c_name.lower() in [g.lower() for g in guidelines]: + return i + # Maybe should raise an exception + return -1 + + +def get_column_names_from_indexes(requirements_df: pd.DataFrame, guideline_name: str, valid_indexes: List[int]) -> \ + List[str]: + """ + This function returns the names of the columns from its indexes. + At the moment it is only used to get the column names for the sheets that have requirements columns that don't + contain guideline values such as "Cipher Suites" + :param requirements_df: The dataframe in which the research should be done + :param guideline_name: The name of the guideline to search + :param valid_indexes: The indexes to search the name for + :return: + """ + row_dict = requirements_df.iloc[0:1, :].to_dict() + columns = [] + i = 0 + for col in row_dict: + if col[0] == guideline_name: + if i in valid_indexes: + columns.append(col[1]) + i += 1 + return columns + + +def get_version_name_for_database(version_name: str): + """This function prepares the version_name to be usable in the database as art of a table's name""" + version_name = version_name if "Unnamed" not in version_name else "" + version_name = version_name.strip().title().replace(" ", "").replace("-", "").replace("/", "_").replace("#", "") \ + .strip(".") + return version_name.upper() + + +def get_guideline_name_for_database(guideline: str): + """This function prepares the guideline_name to be usable in the database as part of a table's name""" + if " " in guideline: + tokens = guideline.split(" ") + if "+" in tokens[1]: + # The "added" entries are already present in the dict + guideline = tokens[0] + elif len(tokens) > 2 and "/" in tokens[-1]: + guideline = tokens[0] + tokens[-1].replace("/", "_") + return guideline.strip(")").upper() + + +def is_double_guideline(guideline: str): + return " " in guideline and guideline.split(" ")[1][1] == "+" + + +def get_first_col_for_guideline(df: pd.DataFrame, guideline: str): + for col in df.columns: + if col[0] == guideline: + return col[1] + + +def get_column(df: pd.DataFrame, index: int): + return df.iloc[:, index] diff --git a/requirements.db b/configs/compliance/requirements.db similarity index 100% rename from requirements.db rename to configs/compliance/requirements.db diff --git a/out.config b/out.config new file mode 100644 index 0000000..11910c6 --- /dev/null +++ b/out.config @@ -0,0 +1,12 @@ +# Template based off the Figure 4 of the compliance_paper + + SSLEngine on + SSLCertificateFile /path/to/cert_chain + SSLCertificateKeyFile /path/to/private_key + Header always set Strict-Transport-Security "max-age=63072000" + + +SSLHonorCipherOrder off +SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 TLSv1.2 TLSv1.3 +SSLCipherSuite !ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256:!ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256:!DHE-RSA-WITH-CHACHA20-POLY1305-SHA256:!DH-DSS-WITH-AES-128-GCM-SHA256:!DH-DSS-WITH-AES-256-GCM-SHA384:!DH-DSS-WITH-AES-128-CBC-SHA256:!DH-DSS-WITH-AES-256-CBC-SHA256:!DH-DSS-WITH-AES-128-CBC-SHA:!DH-DSS-WITH-AES-256-CBC-SHA:!DH-RSA-WITH-AES-128-GCM-SHA256:!DH-RSA-WITH-AES-256-GCM-SHA384:!DH-RSA-WITH-AES-128-CBC-SHA256:!DH-RSA-WITH-AES-256-CBC-SHA256:!DH-RSA-WITH-AES-128-CBC-SHA:!DH-RSA-WITH-AES-256-CBC-SHA:!ECDH-ECDSA-WITH-AES-128-GCM-SHA256:!ECDH-ECDSA-WITH-AES-256-GCM-SHA384:!ECDH-ECDSA-WITH-AES-128-CBC-SHA256:!ECDH-ECDSA-WITH-AES-256-CBC-SHA384:!ECDH-ECDSA-WITH-AES-128-CBC-SHA:!ECDH-ECDSA-WITH-AES-256-CBC-SHA:!ECDH-RSA-WITH-AES-128-GCM-SHA256:!ECDH-RSA-WITH-AES-256-GCM-SHA384:!ECDH-RSA-WITH-AES-128-CBC-SHA256:!ECDH-RSA-WITH-AES-256-CBC-SHA384:!ECDH-RSA-WITH-AES-128-CBC-SHA:!ECDH-RSA-WITH-AES-256-CBC-SHA:!AES-128-GCM-SHA256:!AES-256-GCM-SHA384:!CHACHA20-POLY1305-SHA256:!AES-128-CCM-SHA256:!AES-128-CCM-8-SHA256:!ECDHE-PSK-WITH-AES-128-GCM-SHA256:!ECDHE-PSK-WITH-AES-256-GCM-SHA384:!ECDHE-PSK-WITH-AES-128-CCM-SHA256:!RSA-PSK-WITH-AES-128-CBC-SHA256:!RSA-PSK-WITH-AES-256-CBC-SHA384:!RSA-PSK-WITH-AES-128-GCM-SHA256:!RSA-PSK-WITH-AES-256-GCM-SHA384:!PSK-WITH-AES-128-GCM-SHA256:!PSK-WITH-AES-256-GCM-SHA384:!PSK-WITH-AES-128-CCM:!PSK-WITH-AES-256-CCM:!PSK-WITH-AES-128-CCM-8:!PSK-WITH-AES-256-CCM-8:!PSK-WITH-AES-128-CBC-SHA256:!PSK-WITH-AES-256-CBC-SHA384:!PSK-WITH-AES-128-CBC-SHA:!PSK-WITH-AES-256-CBC-SHA:!RSA-WITH-AES-128-CCM:!RSA-WITH-AES-256-CCM:!RSA-WITH-AES-128-CCM-8:!RSA-WITH-AES-256-CCM-8:!RSA-WITH-AES-128-GCM-SHA256:!RSA-WITH-AES-256-GCM-SHA384:!RSA-WITH-AES-128-CBC-SHA256:!RSA-WITH-AES-256-CBC-SHA256:!RSA-WITH-AES-128-CBC-SHA:!RSA-WITH-AES-256-CBC-SHA:!RSA-WITH-3DES-EDE-CBC-SHA:!ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256:!ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384:!ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256:!ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384:!DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256:!DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256:!ECDHE-RSA-WITH-ARIA-256-GCM-SHA384:!ECDHE-RSA-WITH-ARIA-128-GCM-SHA256:!DHE-RSA-WITH-ARIA-256-GCM-SHA384:!DHE-RSA-WITH-ARIA-128-GCM-SHA256:!ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384:!ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256:!ECDHE-RSA-WITH-ARIA-256-CBC-SHA384:!ECDHE-RSA-WITH-ARIA-128-CBC-SHA256:!DHE-RSA-WITH-ARIA-256-CBC-SHA384:!DHE-RSA-WITH-ARIA-128-CBC-SHA256:!DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256:!DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256:!DHE-PSK-WITH-CHACHA20-POLY1305-SHA256:!DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384:!DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384:!ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256:!DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384:!DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256:!DHE-PSK-WITH-ARIA-256-GCM-SHA384:!DHE-PSK-WITH-ARIA-128-GCM-SHA256:!ECDHE-PSK-WITH-ARIA-256-CBC-SHA384:!ECDHE-PSK-WITH-ARIA-128-CBC-SHA256:!DHE-PSK-WITH-ARIA-256-CBC-SHA384:!DHE-PSK-WITH-ARIA-128-CBC-SHA256:FALLBACK-SCSV +SSLUseStapling on \ No newline at end of file From b7c72adae2ed0fd8cddc8155010c91a7cb3884e3 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 12 Apr 2023 10:15:46 +0200 Subject: [PATCH 088/209] fixed small issue with database --- modules/compliance/wrappers/db_reader.py | 2 +- utils/database.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index 4dd4422..f36b469 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -5,7 +5,7 @@ class Database: - database_file = "requirements.db" + database_file = "configs/compliance/requirements.db" def __init__(self, file: str = database_file): self.database_file = file diff --git a/utils/database.py b/utils/database.py index eab1e42..36c0c89 100644 --- a/utils/database.py +++ b/utils/database.py @@ -29,4 +29,7 @@ def get_standardized_level(level): :type level: str :return: """ - return level.replace("*", "").replace("°", "").strip() + if isinstance(level, str): + return level.replace("*", "").replace("°", "").strip() + else: + return "" From 32f4edf68f4eaef9c9381af1833a62ff1779ac51 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 14 Apr 2023 10:41:10 +0200 Subject: [PATCH 089/209] fixed small issue with function condition and finished filling default_levels.json --- configs/compliance/alias/default_levels.json | 28 ++++++++++---------- modules/compliance/compliance_base.py | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/configs/compliance/alias/default_levels.json b/configs/compliance/alias/default_levels.json index ae0ce78..844008e 100644 --- a/configs/compliance/alias/default_levels.json +++ b/configs/compliance/alias/default_levels.json @@ -1,17 +1,17 @@ { "Protocol": { - "NIST": "GOVERNMENTONLY", - "BSI": "", + "NIST": "CUSTOMERFACING", + "BSI": "CUSTOMERFACING", "ANSSI": "", "AGID": "", - "MOZILLA": "" + "MOZILLA": "INTERMEDIATE" }, "CipherSuite": { - "NIST": "1", - "BSI": "", + "NIST": "PREFERRED1", + "BSI": "PREFERRED1", "ANSSI": "", - "MOZILLA": "", - "AGID": "" + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE" }, "Extension": { "NIST": "", @@ -23,8 +23,8 @@ "NIST": "", "BSI": "", "ANSSI": "", - "MOZILLA": "", - "AGID": "" + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE" }, "Hash": { "NIST": "", @@ -42,15 +42,15 @@ }, "Groups": { "NIST": "", - "BSI": "", + "BSI": "CUSTOMERFACING", "ANSSI": "", - "MOZILLA": "", - "AGID": "" + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE" }, "KeyLengths": { "ANSSI": "", - "MOZILLA": "", - "AGID": "", + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE", "NIST": "", "BSI": "" }, diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index e955040..dde7847 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -158,7 +158,7 @@ def _evaluate_condition(self, condition): field = tokens[0] to_search = self._prepare_to_search(field, tokens[-1]) config_field = self.instructions.get(field) - if config_field.startswith("FUNCTION"): + if config_field and config_field.startswith("FUNCTION"): assert config_field[8] == " " args = { "data": to_search, From 4e0f837774c4038aa0f5c826d937edc5bcb893c0 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 14 Apr 2023 10:41:42 +0200 Subject: [PATCH 090/209] updated database and added DatabaseFiller to the tool --- DatabaseFiller/database_filler.py | 7 +- DatabaseFiller/guidelines.xlsx | Bin 151488 -> 121886 bytes DatabaseFiller/output.prisma | 1555 ++++++++++------- DatabaseFiller/requirements.db | Bin 1056768 -> 1101824 bytes DatabaseFiller/schema_creator.py | 7 +- .../schema_generator/template.prisma | 145 +- DatabaseFiller/utils/configs.py | 29 +- configs/compliance/requirements.db | Bin 1056768 -> 1101824 bytes 8 files changed, 992 insertions(+), 751 deletions(-) diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py index 8883d62..6f56a83 100755 --- a/DatabaseFiller/database_filler.py +++ b/DatabaseFiller/database_filler.py @@ -167,6 +167,7 @@ def fill_extra_table(sheet_name: str) -> bool: cur.executemany(sql_query, values) conn.commit() values = [] + # Start of guideline specific part requirements_columns = get_requirements_columns(guidelines_dataframe, sheet) guidelines_columns_count = get_columns_count_for_guideline(guidelines_dataframe) @@ -244,11 +245,11 @@ def fill_extra_table(sheet_name: str) -> bool: entries = values_dict[table] # This is to prevent the "this or X" condition to appear in tables that don't need it - # this "if" checks if the guideline has multiple versions for this sheet + # this condition checks if the guideline has multiple versions for this sheet if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: for entry in entries: entry = entries[entry] - # Since the problem is a condition it only verifies if there are four elements. + # Since the problem is a condition, and it only verifies if there are four elements. # Last element is the condition # Second to last is the level if len(entry) > 3 and pd.notna(entry[-1]): @@ -265,7 +266,7 @@ def fill_extra_table(sheet_name: str) -> bool: if len(entry) < table_columns_count: entry.insert(0, index) # Every remaining column is filled with None - if len(entry) < table_columns_count: + while len(entry) < table_columns_count: entry.append(None) values_groups[table].append(tuple(entry)) for table in values_groups: diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index 9fb9302081ecc8e7b35d3d4e19a332cec2b18931..b166acb9970c7e0c4965f5f44d1ebbf3eb4e8dec 100644 GIT binary patch delta 60007 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 delta 89802 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

ZBK=2l=c&-yN^_}ZnuFc4Ybv`=D25jSG74_i&b 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 I3!$+81HqS+T>t<8 diff --git a/DatabaseFiller/output.prisma b/DatabaseFiller/output.prisma index 3f3ae07..6938b5e 100644 --- a/DatabaseFiller/output.prisma +++ b/DatabaseFiller/output.prisma @@ -1,1018 +1,1237 @@ datasource db { - provider = "sqlite" - url = "file:requirements.db" + provider = "sqlite" + url = "file:requirements.db" } // COMMON DATA model TlsVersion { - version Float @id - ExtTls TlsVersionExtension[] - Hash Hash[] + version Float @id + ExtTls TlsVersionExtension[] + Hash Hash[] } model Guideline { - name String @id - updated_at String - - + name String @id + updated_at String + + + + ProtocolNISTGOVERNMENTONLY ProtocolNISTGOVERNMENTONLY[] + ProtocolNISTCUSTOMERFACING ProtocolNISTCUSTOMERFACING[] + ProtocolNIST1_1_2024GOVERNMENTONLY ProtocolNIST1_1_2024GOVERNMENTONLY[] + ProtocolNIST1_1_2024CUSTOMERFACING ProtocolNIST1_1_2024CUSTOMERFACING[] + ProtocolBSIFEDERALAPPLICATIONS ProtocolBSIFEDERALAPPLICATIONS[] + ProtocolBSICUSTOMERFACING ProtocolBSICUSTOMERFACING[] + ProtocolANSSI ProtocolANSSI[] + ProtocolAGID ProtocolAGID[] + ProtocolMOZILLAMODERN ProtocolMOZILLAMODERN[] + ProtocolMOZILLAINTERMEDIATE ProtocolMOZILLAINTERMEDIATE[] + ProtocolMOZILLAOLD ProtocolMOZILLAOLD[] + CipherSuiteNISTPREFERRED1 CipherSuiteNISTPREFERRED1[] + CipherSuiteNISTPREFERRED2 CipherSuiteNISTPREFERRED2[] + CipherSuiteNISTPREFERRED3 CipherSuiteNISTPREFERRED3[] + CipherSuiteBSIPREFERRED1 CipherSuiteBSIPREFERRED1[] + CipherSuiteBSIFEDERALREQ CipherSuiteBSIFEDERALREQ[] + CipherSuiteANSSI CipherSuiteANSSI[] + CipherSuiteMOZILLAMODERN CipherSuiteMOZILLAMODERN[] + CipherSuiteMOZILLAINTERMEDIATE CipherSuiteMOZILLAINTERMEDIATE[] + CipherSuiteMOZILLAOLD CipherSuiteMOZILLAOLD[] + CipherSuiteAGIDMODERN CipherSuiteAGIDMODERN[] + CipherSuiteAGIDINTERMEDIATE CipherSuiteAGIDINTERMEDIATE[] + CipherSuiteAGIDOLD CipherSuiteAGIDOLD[] + ExtensionNIST ExtensionNIST[] + ExtensionBSI ExtensionBSI[] + ExtensionANSSI ExtensionANSSI[] + ExtensionAGID ExtensionAGID[] + CertificateSignatureNIST CertificateSignatureNIST[] + CertificateSignatureBSI CertificateSignatureBSI[] + CertificateSignatureANSSI CertificateSignatureANSSI[] + CertificateSignatureMOZILLAMODERN CertificateSignatureMOZILLAMODERN[] + CertificateSignatureMOZILLAINTERMEDIATE CertificateSignatureMOZILLAINTERMEDIATE[] + CertificateSignatureMOZILLAOLD CertificateSignatureMOZILLAOLD[] + CertificateSignatureAGIDMODERN CertificateSignatureAGIDMODERN[] + CertificateSignatureAGIDINTERMEDIATE CertificateSignatureAGIDINTERMEDIATE[] + CertificateSignatureAGIDOLD CertificateSignatureAGIDOLD[] + HashNIST HashNIST[] + HashBSI HashBSI[] + HashANSSI HashANSSI[] + HashMOZILLAMODERN HashMOZILLAMODERN[] + HashMOZILLAINTERMEDIATE HashMOZILLAINTERMEDIATE[] + HashMOZILLAOLD HashMOZILLAOLD[] + HashAGIDMODERN HashAGIDMODERN[] + HashAGIDINTERMEDIATE HashAGIDINTERMEDIATE[] + HashAGIDOLD HashAGIDOLD[] + SignatureNIST SignatureNIST[] + SignatureBSICLIENT_SERVERSIGNATURES SignatureBSICLIENT_SERVERSIGNATURES[] + SignatureBSIINCERTIFICATES SignatureBSIINCERTIFICATES[] + SignatureANSSI SignatureANSSI[] + SignatureMOZILLAMODERN SignatureMOZILLAMODERN[] + SignatureMOZILLAINTERMEDIATE SignatureMOZILLAINTERMEDIATE[] + SignatureMOZILLAOLD SignatureMOZILLAOLD[] + SignatureAGIDMODERN SignatureAGIDMODERN[] + SignatureAGIDINTERMEDIATE SignatureAGIDINTERMEDIATE[] + SignatureAGIDOLD SignatureAGIDOLD[] + GroupsNIST GroupsNIST[] + GroupsBSIFEDERALAPPLICATIONS GroupsBSIFEDERALAPPLICATIONS[] + GroupsBSICUSTOMERFACING GroupsBSICUSTOMERFACING[] + GroupsANSSI GroupsANSSI[] + GroupsMOZILLAMODERN GroupsMOZILLAMODERN[] + GroupsMOZILLAINTERMEDIATE GroupsMOZILLAINTERMEDIATE[] + GroupsMOZILLAOLD GroupsMOZILLAOLD[] + GroupsAGIDMODERN GroupsAGIDMODERN[] + GroupsAGIDINTERMEDIATE GroupsAGIDINTERMEDIATE[] + GroupsAGIDOLD GroupsAGIDOLD[] + KeyLengthsNIST KeyLengthsNIST[] + KeyLengthsBSI KeyLengthsBSI[] + KeyLengthsANSSI KeyLengthsANSSI[] + KeyLengthsMOZILLAMODERN KeyLengthsMOZILLAMODERN[] + KeyLengthsMOZILLAINTERMEDIATE KeyLengthsMOZILLAINTERMEDIATE[] + KeyLengthsMOZILLAOLD KeyLengthsMOZILLAOLD[] + KeyLengthsAGIDMODERN KeyLengthsAGIDMODERN[] + KeyLengthsAGIDINTERMEDIATE KeyLengthsAGIDINTERMEDIATE[] + KeyLengthsAGIDOLD KeyLengthsAGIDOLD[] + CertificateNIST CertificateNIST[] + CertificateBSI CertificateBSI[] + CertificateANSSI CertificateANSSI[] + CertificateMOZILLA CertificateMOZILLA[] + MiscNIST MiscNIST[] + MiscBSI MiscBSI[] + MiscAGID MiscAGID[] } // GENERAL DATA model CipherSuite { - name String @id - iana_code String - applicability String @default("") - signature String? @default("") + name String @id + iana_code String + applicability String @default("") + signature String? @default("") + CipherSuiteNISTPREFERRED1 CipherSuiteNISTPREFERRED1[] + CipherSuiteNISTPREFERRED2 CipherSuiteNISTPREFERRED2[] + CipherSuiteNISTPREFERRED3 CipherSuiteNISTPREFERRED3[] + CipherSuiteBSIPREFERRED1 CipherSuiteBSIPREFERRED1[] + CipherSuiteBSIFEDERALREQ CipherSuiteBSIFEDERALREQ[] + CipherSuiteANSSI CipherSuiteANSSI[] + CipherSuiteMOZILLAMODERN CipherSuiteMOZILLAMODERN[] + CipherSuiteMOZILLAINTERMEDIATE CipherSuiteMOZILLAINTERMEDIATE[] + CipherSuiteMOZILLAOLD CipherSuiteMOZILLAOLD[] + CipherSuiteAGIDMODERN CipherSuiteAGIDMODERN[] + CipherSuiteAGIDINTERMEDIATE CipherSuiteAGIDINTERMEDIATE[] + CipherSuiteAGIDOLD CipherSuiteAGIDOLD[] } model Extension { - name String @id - iana_code String - TlsVersionExtension TlsVersionExtension[] + name String @id + iana_code String + TlsVersionExtension TlsVersionExtension[] + ExtensionNIST ExtensionNIST[] + ExtensionBSI ExtensionBSI[] + ExtensionANSSI ExtensionANSSI[] + ExtensionAGID ExtensionAGID[] } model Groups { - name String @id - iana_code String - supported_security String? + name String @id + iana_code String + supported_security String? + GroupsNIST GroupsNIST[] + GroupsBSIFEDERALAPPLICATIONS GroupsBSIFEDERALAPPLICATIONS[] + GroupsBSICUSTOMERFACING GroupsBSICUSTOMERFACING[] + GroupsANSSI GroupsANSSI[] + GroupsMOZILLAMODERN GroupsMOZILLAMODERN[] + GroupsMOZILLAINTERMEDIATE GroupsMOZILLAINTERMEDIATE[] + GroupsMOZILLAOLD GroupsMOZILLAOLD[] + GroupsAGIDMODERN GroupsAGIDMODERN[] + GroupsAGIDINTERMEDIATE GroupsAGIDINTERMEDIATE[] + GroupsAGIDOLD GroupsAGIDOLD[] } model Protocol { - name String @id + name String @id + ProtocolNISTGOVERNMENTONLY ProtocolNISTGOVERNMENTONLY[] + ProtocolNISTCUSTOMERFACING ProtocolNISTCUSTOMERFACING[] + ProtocolNIST1_1_2024GOVERNMENTONLY ProtocolNIST1_1_2024GOVERNMENTONLY[] + ProtocolNIST1_1_2024CUSTOMERFACING ProtocolNIST1_1_2024CUSTOMERFACING[] + ProtocolBSIFEDERALAPPLICATIONS ProtocolBSIFEDERALAPPLICATIONS[] + ProtocolBSICUSTOMERFACING ProtocolBSICUSTOMERFACING[] + ProtocolANSSI ProtocolANSSI[] + ProtocolAGID ProtocolAGID[] + ProtocolMOZILLAMODERN ProtocolMOZILLAMODERN[] + ProtocolMOZILLAINTERMEDIATE ProtocolMOZILLAINTERMEDIATE[] + ProtocolMOZILLAOLD ProtocolMOZILLAOLD[] } model Hash { - name String @id - iana_code String - tls_version Float - TlsVersion TlsVersion @relation(fields: [tls_version], references: [version]) + name String @id + iana_code String + tls_version Float + TlsVersion TlsVersion @relation(fields: [tls_version], references: [version]) + HashNIST HashNIST[] + HashBSI HashBSI[] + HashANSSI HashANSSI[] + HashMOZILLAMODERN HashMOZILLAMODERN[] + HashMOZILLAINTERMEDIATE HashMOZILLAINTERMEDIATE[] + HashMOZILLAOLD HashMOZILLAOLD[] + HashAGIDMODERN HashAGIDMODERN[] + HashAGIDINTERMEDIATE HashAGIDINTERMEDIATE[] + HashAGIDOLD HashAGIDOLD[] } model CertificateSignature { - name String @id - iana_code String - tls_version String + name String @id + iana_code String + tls_version String + CertificateSignatureNIST CertificateSignatureNIST[] + CertificateSignatureBSI CertificateSignatureBSI[] + CertificateSignatureANSSI CertificateSignatureANSSI[] + CertificateSignatureMOZILLAMODERN CertificateSignatureMOZILLAMODERN[] + CertificateSignatureMOZILLAINTERMEDIATE CertificateSignatureMOZILLAINTERMEDIATE[] + CertificateSignatureMOZILLAOLD CertificateSignatureMOZILLAOLD[] + CertificateSignatureAGIDMODERN CertificateSignatureAGIDMODERN[] + CertificateSignatureAGIDINTERMEDIATE CertificateSignatureAGIDINTERMEDIATE[] + CertificateSignatureAGIDOLD CertificateSignatureAGIDOLD[] +} + +model Certificate { + name String @id + CertificateNIST CertificateNIST[] + CertificateBSI CertificateBSI[] + CertificateANSSI CertificateANSSI[] + CertificateMOZILLA CertificateMOZILLA[] } model Misc { - name String @id + name String @id + MiscNIST MiscNIST[] + MiscBSI MiscBSI[] + MiscAGID MiscAGID[] } model KeyLengths { - field_name String - name String - length Int - + field_name String + name String + length Int - @@id([name, length]) + + @@id([name, length]) + KeyLengthsNIST KeyLengthsNIST[] + KeyLengthsBSI KeyLengthsBSI[] + KeyLengthsANSSI KeyLengthsANSSI[] + KeyLengthsMOZILLAMODERN KeyLengthsMOZILLAMODERN[] + KeyLengthsMOZILLAINTERMEDIATE KeyLengthsMOZILLAINTERMEDIATE[] + KeyLengthsMOZILLAOLD KeyLengthsMOZILLAOLD[] + KeyLengthsAGIDMODERN KeyLengthsAGIDMODERN[] + KeyLengthsAGIDINTERMEDIATE KeyLengthsAGIDINTERMEDIATE[] + KeyLengthsAGIDOLD KeyLengthsAGIDOLD[] } model Signature { - name String @id - iana_code String - version String + name String @id + iana_code String + version String + SignatureNIST SignatureNIST[] + SignatureBSICLIENT_SERVERSIGNATURES SignatureBSICLIENT_SERVERSIGNATURES[] + SignatureBSIINCERTIFICATES SignatureBSIINCERTIFICATES[] + SignatureANSSI SignatureANSSI[] + SignatureMOZILLAMODERN SignatureMOZILLAMODERN[] + SignatureMOZILLAINTERMEDIATE SignatureMOZILLAINTERMEDIATE[] + SignatureMOZILLAOLD SignatureMOZILLAOLD[] + SignatureAGIDMODERN SignatureAGIDMODERN[] + SignatureAGIDINTERMEDIATE SignatureAGIDINTERMEDIATE[] + SignatureAGIDOLD SignatureAGIDOLD[] } // SPECIAL TABLES model TlsVersionExtension { - TlsVersion TlsVersion @relation(fields: [version], references: [version]) - version Float - extension Extension @relation(fields: [extension_name], references: [name]) - extension_name String + TlsVersion TlsVersion @relation(fields: [version], references: [version]) + version Float + extension Extension @relation(fields: [extension_name], references: [name]) + extension_name String - @@id([version, extension_name]) + @@id([version, extension_name]) } // Guidelines tables model ProtocolNISTGOVERNMENTONLY { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolNISTCUSTOMERFACING { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolNIST1_1_2024GOVERNMENTONLY { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolNIST1_1_2024CUSTOMERFACING { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolBSIFEDERALAPPLICATIONS { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolBSICUSTOMERFACING { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolAGID { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ProtocolMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Protocol Protocol @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Protocol Protocol @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteNISTPREFERRED1 { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteNISTPREFERRED2 { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteNISTPREFERRED3 { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteBSIPREFERRED1 { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteBSIFEDERALREQ { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CipherSuiteAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CipherSuite CipherSuite @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CipherSuite CipherSuite @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ExtensionNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Extension Extension @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ExtensionBSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Extension Extension @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ExtensionANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Extension Extension @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model ExtensionAGID { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Extension Extension @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureBSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model CertificateSignatureAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateSignature CertificateSignature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashBSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model HashAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Hash Hash @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Hash Hash @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureBSICLIENT_SERVERSIGNATURES { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureBSIINCERTIFICATES { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model SignatureAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Signature Signature @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Signature Signature @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsBSIFEDERALAPPLICATIONS { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsBSICUSTOMERFACING { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model GroupsAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Groups Groups @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Groups Groups @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model KeyLengthsNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsBSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsANSSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsMOZILLAMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsMOZILLAINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsMOZILLAOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsAGIDMODERN { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsAGIDINTERMEDIATE { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") - @@id([id, name, length]) + @@id([id, name, length]) } model KeyLengthsAGIDOLD { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") - - @@id([id, name, length]) + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") + + @@id([id, name, length]) +} + +model CertificateNIST { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Certificate Certificate @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateBSI { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Certificate Certificate @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateANSSI { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Certificate Certificate @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateMOZILLA { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Certificate Certificate @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) } model MiscNIST { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Misc Misc @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Misc Misc @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model MiscBSI { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Misc Misc @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Misc Misc @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model MiscAGID { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Misc Misc @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") - - @@id([id, name]) + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Misc Misc @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") + + @@id([id, name]) } diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index e965afe8d3eed6cf1a46cf730431d4adea0621c2..150d2e8f913e1d57613d07c109f43fc119a899fe 100644 GIT binary patch delta 11642 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 CvqcyH delta 1010 zcmZWlTSyd97(TOeX2+d5mr+SVL`S4T6nBUoH8<=bwbU|-dQrPzTH->~L%pm72@$9i zPGF!f>%N5SChai_FE7~?M3ytimsaaa9tJ9ez9cbV`aK79BoeA`m1crU>=_#}f z0Ko+|zhfQwJ8iU}_?5&_Odr@B>+q+$JS&mwxfJPDhe{9Z;ozN?ZT%y(%qK0=6}rTd zW8!BjgS88s8oFZDv|2R+vua#uSo+EZi|Yd6H;a446is^Tz`n3}rH#!yus1B;ET-tv zCkHmi;@!6Hr30G?#fL;)c+Qq5#AHo+?r?=JB!W6ZL|F)F{vg+jHk&rHTNhglRN zP5eMdS+Y-jOywyDX!#eA*GOLp3N=utw8BE$G zUNfs1@)F|?@%o+>r}n$8{m85~TRZ)qc5qXh23GA(Kb@Tt8=c|~YpXQ``AB@+d}L+~ zCCRl$7og*`pI)P<=x(}|ERd(9mjuXhvWp-*i(~jMzKJj5a;&38^ayqJXB(#xJD=~F zb0zH%-NF`c4lobVaq z-A`~UE))i^!7{^2iGcQkC^lTv{3!sK0}-X1@ne5qCI&M=sgS4SZpkm76c0+pLY|n3 zgJ4EY!Dr0ayqa;tF#5#87Q-BA&@#+GL{ZGPCN+)a)v4#)LXh36SEDYoE}|TTJiN(# z7*QISc2+&&GJ6`eA{J~?YfglqpK1)o)C9dcuSVw)^o3v*)`9G=3FID~@M98dK}d8412mBpHIPkcgohaTVq(#?z!n24RIk4^7G){0M$Buw#G-f XSO4b?^IKG>P!B@;VPTDvlhpSI%=b=G diff --git a/DatabaseFiller/schema_creator.py b/DatabaseFiller/schema_creator.py index 5c0f326..b592bb3 100644 --- a/DatabaseFiller/schema_creator.py +++ b/DatabaseFiller/schema_creator.py @@ -5,16 +5,13 @@ import pandas as pd -from utils.configs import sheets_mapping, additional_keys, different_templates +from utils.configs import sheets_mapping, additional_keys, different_templates, RANDOM_STRING, GUIDELINE_BLOCKS, \ + TEMPLATE_FILE from utils.filler_utils import split_sheet, get_requirements_columns, get_version_name_for_database, \ get_guideline_name_for_database # IMPORTANT NOTE "sheet" means the original file name, "sheet_name" is the mapped one -TEMPLATE_FILE = "schema_generator/template.prisma" -GUIDELINE_BLOCKS = 13 -RANDOM_STRING = "7WJsEz" - # This dict is filled during the initialization additional_templates = {} diff --git a/DatabaseFiller/schema_generator/template.prisma b/DatabaseFiller/schema_generator/template.prisma index 71bdf51..e3da6ac 100644 --- a/DatabaseFiller/schema_generator/template.prisma +++ b/DatabaseFiller/schema_generator/template.prisma @@ -1,118 +1,137 @@ datasource db { - provider = "sqlite" - url = "file:requirements.db" + provider = "sqlite" + url = "file:requirements.db" } // COMMON DATA model TlsVersion { - version Float @id - ExtTls TlsVersionExtension[] - Hash Hash[] + version Float @id + ExtTls TlsVersionExtension[] + Hash Hash[] } model Guideline { - name String @id - updated_at String - KeyLengths7WJsEz KeyLengths7WJsEz[] - Sheet7WJsEz Sheet7WJsEz[] + name String @id + updated_at String + KeyLengths7WJsEz KeyLengths7WJsEz[] + Sheet7WJsEz Sheet7WJsEz[] + Certificate7WJsEz Certificate7WJsEz[] } // GENERAL DATA model CipherSuite { - name String @id - iana_code String - applicability String @default("") - signature String? @default("") + name String @id + iana_code String + applicability String @default("") + signature String? @default("") } model Extension { - name String @id - iana_code String - TlsVersionExtension TlsVersionExtension[] + name String @id + iana_code String + TlsVersionExtension TlsVersionExtension[] } model Groups { - name String @id - iana_code String - supported_security String? + name String @id + iana_code String + supported_security String? } model Protocol { - name String @id + name String @id } model Hash { - name String @id - iana_code String - tls_version Float - TlsVersion TlsVersion @relation(fields: [tls_version], references: [version]) + name String @id + iana_code String + tls_version Float + TlsVersion TlsVersion @relation(fields: [tls_version], references: [version]) } model CertificateSignature { - name String @id - iana_code String - tls_version String + name String @id + iana_code String + tls_version String +} + +model Certificate { + name String @id } model Misc { - name String @id + name String @id } model KeyLengths { - field_name String - name String - length Int - KeyLengths7WJsEz KeyLengths7WJsEz[] + field_name String + name String + length Int + KeyLengths7WJsEz KeyLengths7WJsEz[] - @@id([name, length]) + @@id([name, length]) } model Signature { - name String @id - iana_code String - version String + name String @id + iana_code String + version String } // SPECIAL TABLES model TlsVersionExtension { - TlsVersion TlsVersion @relation(fields: [version], references: [version]) - version Float - extension Extension @relation(fields: [extension_name], references: [name]) - extension_name String + TlsVersion TlsVersion @relation(fields: [version], references: [version]) + version Float + extension Extension @relation(fields: [extension_name], references: [name]) + extension_name String - @@id([version, extension_name]) + @@id([version, extension_name]) } // TEMPLATE DATA model Sheet { - name String @id - iana_code String - version String - Sheet7WJsEz Sheet7WJsEz[] + name String @id + iana_code String + version String + Sheet7WJsEz Sheet7WJsEz[] + Certificate7WJsEz Certificate7WJsEz[] } model Sheet7WJsEz { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Sheet Sheet @relation(fields: [name], references: [name]) - name String - level String? // put level as Nullable (it would be considered not mentioned) - condition String? @default("") + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Sheet Sheet @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") - @@id([id, name]) + @@id([id, name]) } model KeyLengths7WJsEz { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) - name String - length Int - level String? - condition String? @default("") - - @@id([id, name, length]) + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + KeyLengths KeyLengths @relation(fields: [name, length], references: [name, length]) + name String + length Int + level String? + condition String? @default("") + + @@id([id, name, length]) +} + +model Certificate7WJsEz { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Sheet Sheet @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) } diff --git a/DatabaseFiller/utils/configs.py b/DatabaseFiller/utils/configs.py index 1b81222..5f4fb6e 100644 --- a/DatabaseFiller/utils/configs.py +++ b/DatabaseFiller/utils/configs.py @@ -1,3 +1,19 @@ +# schema creator configs +TEMPLATE_FILE = "schema_generator/template.prisma" +GUIDELINE_BLOCKS = 14 +RANDOM_STRING = "7WJsEz" + +# If the sheet has vertically merged cells in the name column add it here +has_merged_names = ["Key lengths"] + +# list of sheet_names that need a different template, order is important +different_templates = ["KeyLengths", "Certificate"] + +# The syntax for this is: Sheet: list of keys +# it is assumed that a field with the same name of the key was added using the additional_fields dict +additional_keys = { +} + guidelines = { "NIST": "", "ANSSI": "", @@ -24,7 +40,7 @@ "Hash Algorithm": "Hash", "Certificate Signature": "CertificateSignature", "Key lengths": "KeyLengths", - # "Certificate": None, at the moment the certificate table doesn't exist + "Certificate": "Certificate", # at the moment the certificate table doesn't exist "Misc": "Misc" } @@ -47,14 +63,3 @@ # this function is needed to avoid having iana codes in the form 0.0 ('IANA', 'Unnamed: 1_level_1'): lambda x: str(int(x)) if isinstance(x, float) else str(x) } - -# If the sheet has vertically merged cells in the name column add it here -has_merged_names = ["Key lengths"] - -# list of sheet_names that need a different template, order is important -different_templates = ["KeyLengths"] - -# The syntax for this is: Sheet: list of keys -# it is assumed that a field with the same name of the key was added using the additional_fields dict -additional_keys = { -} diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 6dcb30f291bce521ff3f5c204902c0a0b0fd4d86..150d2e8f913e1d57613d07c109f43fc119a899fe 100644 GIT binary patch delta 11629 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 z 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 Vf7u|f!a9YzAKDKIYTTTlp5N(uOp*Wq From fb5eb9b85911389dfaff9c0916ddeccd01d81e22 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 17 Apr 2023 18:14:36 +0200 Subject: [PATCH 091/209] Added certificate signature algorithm and key check for NIST guideline --- DatabaseFiller/guidelines.xlsx | Bin 121886 -> 119123 bytes DatabaseFiller/requirements.db | Bin 1101824 -> 1101824 bytes .../compliance/condition_instructions.json | 3 +- configs/compliance/requirements.db | Bin 1101824 -> 1101824 bytes modules/compliance/compare_many.py | 2 +- modules/compliance/compare_one.py | 2 +- modules/compliance/compliance_base.py | 69 +++++++++++++++--- 7 files changed, 61 insertions(+), 15 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index b166acb9970c7e0c4965f5f44d1ebbf3eb4e8dec..f263f9f89310e24ffc20c3262e41fe9c2286e877 100644 GIT binary patch delta 60208 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%0DXTun8(w#M<$hEheT!@K~&{pRYZXTbE7~ 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

NW2^U9J}n~ z@p+8Y+g*qoFG5WhNa2i*7_hkCKEs1>gTmC&Ka{L$x3(fZ?O42`T7t8_FIAll+) z)u+8r^@f%Afc$wzvc2ky#RsE(ZbYSFFDAAmb_(62>+@)N+DzsYhNJQift<8t=4>Kj zj5e^Pbi4wI-AjUTG>>SZP7Z3E*37e4APBBctkm|loccUMU-J|1QvqhjCzq6`ZihOx zEK@Yg+$QUAl@I}OR}$#8GUMZ5W?w`5G_`ga+mi%t9=dZHn(N5{Q(OOUjf^SP=5R1t z%Uq?8>(}i4c4IY#OpA4PbhVryBvk~sXu-?Es&!ph+F|5O@nbVeq{@LJ9heSJUo5~E%? zUr>U9WOk}xa6Dpou{xu>kI`&J8=wS^u#LQXnzBMKX5tE_nK z%qwGUuqiEs*jNgg!*hKxpXAApPeFGJMdM9#26$&JkT43NMkD6|BQO0bSPxn4?PHQo z8f1ueDW2p~`V^^qCqJfw3y{MFAe{RrGG2+FPTX6nW6W^Z%@>V zFO2cw*Rd;ul+PSpeL1aAYYux2WU<~w4qNlW@SGu_yt&j{rdf67@6H-R?%JI^f9$~t z$Lz_M``dU6`|Z<>*8wmgpiv{dw|2m*?vMuewzYRd;7%zuN43EWy~NGYk_Y@{&zT$o#mU1(#$W^{``e$&nW*Qgo=U1#PP@i#}DEp)43 z)z9~=dL;CZYrQ4NZbv!(o}fmu0tnn)VxoCrzTmFoA@Kt`8vxk0qOl+BJ{hLopku+? zR7W1#G0vJpNtY|sh7V8j#j4Zo2FNsyg${1>h;o@wv53C3Afd|Qs~zoXwt5*SSgo2j zJEHSSR?hBg2j|Q(GUn!dJy6NR+@p&q<0Y3&z+-h^!oZ!?N)cCRKXb(2NXUfPl#$4J zGRqf(Hw0qA0ktC0Skl~{UtMXB(r|uIs5sHn|cSx0u&s?xIg%Q+ieUS7=IMXvEkz@ zU^h^-P5hLY4Ux|{jzi;h%CR(uPd}boZ3edvd}T=VSNqFl5N9&hQ+qV)d=T{4VbMBh$~Qr&h7q5ci>}mrBP0PbZr~BGo=uU zt9A$LQ@V}^(PaskGp{W&GEp(U^{@RQ`!{>3iz@xBPkdGLS* z6n!13uvw#f3tzS`%nkwxKw>QSV867D4_HBr@Xz8hCC7&niPQ5Jy_+EUG1`z6?YS@J zyajjo;4j!Dojxs$KZkh@q};@<>@uGurr6PqeAC6{$+-|SZcldF5`?}ehAz-go@Pd~ z{S`%g!Af^hg|1h+Hr#?g+zRj{bPk|U(GgQ7Jj!ytu;tV0&Ed;vo`muy~2>Y^fyU6 z_@2WjA_|X_-*R-Oqb%TPNFB$6;6u5$_-%z+zW3E0aGMWtxC+SAI~F^u9mc0|6Z|e{ zi5O%pE?GkH-3!OqpMolfzPm6w77iF6jn@MO~lC+srBu6P&4D>5?5xqvgD}~ zBq>T#VL}gT8dd41NFqIqD)||dN9VZE#K=a)x!AfcYeQXlCi6Hb+*`kE?W(DNNsVwi|zn@tts5+k?m&(K-GACpD|N1`FyQSHb%7Cc#}fGa5%f$^7B zL88CVzHE^ZcbaI=YP;Ipjy5yL+nB>!Fyn_HU*D89UUi<#7=hwfupfnc{QIHWbW-YO zAKj?4E4i2xKuQb5U!;1bVDnUCzz>Hy(lbP5?;HN zyv%JkOFHZ3s2F!ovUD8qfHOi1y9P?llM|<^6SUi)YPv+WO8Yt{d{II?_{-|c?}8Jz z3PL$^anA?}<6KyTvk$UNBUEdCzcBnod4)r9VLb5EOlo`_Jo(KIU4kw!yIX0|YZeaU zpDEgjz68Yy)=~zyCva_pkMUXSn13IU z9(?V$jpd*EV}GoSIXaKAOPT1K6hcIV!(kVGWR^f<&K2gsAVs&wo6nqhMra(?lMp*? zO4H}JgvR)GjSxH}i@tRPp@%yh>JX>UO?H#(8%sIjQD`}yR0D~*Tziog2s~k7MjMve zv#WooatMWMKj9F5gsA(2SW$S#(;9}`E8!L|a^(!)3B>;H1}4GcZ#F;WD_){4RS2PQ zg7T^rCbR>&imz(DA75_+O>!@`E1G>TnWRij~#EkJr76c9<2$yD)=6 z8~U}81xa89%Kk&jry9SUMI_Zwx!(?Sj&%V8 z?|abM2G1*xWB1E~Geg55;FFb?1jNb&$z{Ksc9$M69~xr$)RvyIy?bia)16`b#rRYW zd);(1s`E)oY6I%M70aNB#BD&h+=zrw<7c%E$Hu&m&n|&iZ}Zl zW&nmF!WX*{JDpaEuSaat$FXv>@N6B22{O5b7|Zq}JPmobe~jR)uW-<CZ0z=2$D|f1rojM%1M?cavfrG*$K- z?f3fnM890UT92{jf_y48E@f<^N#_}C0x1~aI-tfPWX!HEMJMuR<6{YjC&Ej=34w$( zW0-^D{1<|lMP8@@EZC7jFZ_v^ftKs2T}v1R&N`Z<%pXpZa9wACvvT?qGS~||p+xI#z0@PmOL%QE`qZw556EQn^-Ag&t0Qz0vB`$9N3CypkTyQ%j zE(T&kECWIxmKuk|SbEaHKtcsBAC^G|M-m^7e%E?=B?QWe_7_A4i?8Rl&z(dgG+Wns zP153IF+BW_{@51dM8=tz?p6CeS%5~>R#%@aN$B33yZV$70q=B@#o;np#M<4*zPJ(+ zo**{MH7Qg&aJVKPP-X*bG=3_8ti=34!hB}dV|ee9EH1{1&P$oeo@qy5NI zt8xBF-T6+PF@!+!#MYL zQbU^8dV8XbGWTBVRhShCP#=lBK!jDKG$fJBAdD#yqvJ1)91-ZfAKU1@1Lt5dN}Il-WaaA&B-Tu^YjRu5JUi!0$eizC^0M84Szeq8psxC%A4Q)nW0NbdU?fL>7oK8PbynvoxUzDUw_>>z6IbS~_^)^a}~hVcc2l{Q^e zbB3|L@?XpB4O0v(0^NMsB&Ss@sD&{I>>zzL_cmNuKqRtgE7|4Ocg7qWy3kh9S~&l$ zzh0gBOe@5B;Z0>`bUEAg{<{Y2+#Rp?r*WwHz~|yL>nQ+h{@_QX7qH+fUPnIlmrEC# zwWAP1T_>}q@Oax#0zAkR?pUk~i#Ya>;k#1Iw8hh=@!gbrc>dIH0r8SJ{18ph4mw*2 z<)iRaalzW^`9NtR3F#vJOR%*{0zyJ?hesaUFBddPXQ~sOaCy-HGQrSl!HTr90r)oG z_D3ZOtXSR1?Im9x`Z}(&Hd7$9Xo|Fx%;%asQZ~ViqB&py)NEngC ziFw5_z-Fe?y}xA2f`iKy?lRy~~Hd;)%@e^dl~{`5N~c3+<-fKpN1 zP=W}|=a(yMDqIf@yC22l@N(lYlEXAWuBG{F$hM*0ne?!dwm^PzZ@BC8Z*An%Skp1( z^~aC|WUc-SAPvJZfCVXEKVyG!UC){O(>P^+RfY z2E-dDPfMcO>&m3NbZ72Nb*JRXNSVn&*5{!zS{u6)02ox&JUz+P-)==bctLS<%Dujb zyaV5sMUzCeeQnV5biZgN9B-_8#OQP}o`iN(sn|Dgb6t@;k18In9r z8BduM z{dLU8+bO}Fr;}=?i0k#O8F2s^U*pYE9Dl=^BI?qqbp>fb`0^&VSF>pEex5jj!?D}>mT)cPaTgSE%}q!tF4QW8W=u8;dSm~ z{aZZXE?Z(N=M>()6_u-hs)^!%f&kd{N4+x~+a|89XSSc1&#V*I6sr<7FPL;gVA7g6 z3NuTx$6P_gf4O;gvtPA8n)jO;S)7W7W6%ts&uht!(qso#s?^0gmxluRNqZBS#s2GP z+>g!3^8N|Mng2^H{@2kYkGsGFu8<)~A4M&3P^jf1*8AF+OYIs~W8N}p%7?x`zr{rm zX`lfy)vlp0Is(u8A5n)RTPAiTmU8R;2}@UHhIKmUGCW+V3Wd$}L#sEKSAVWap(QD& zYdXLE>hbeEj}jE00#%vd2XQlzosD@wfOTF7CqwG#h$x?6YHkT}K00Oq;B1Bv4|>Xy zHQjt%7t#&GLukM68OnTxe>ASo9W&!esR#*LhpoF&FJ(1YG-RT{4*~ZIV`_s}0;zt2 z$QXRuaAIPUMQ#pjo?ttLr>}p57$;Kn#YIqsW1!)|U$(QVIHC)JS{V%qGB2-V5hWE7A*Tz@nTN}=|hrf;mafol)B=yCT#Ty-ob-V`@ zjQm-egfP&jFk`u^&}U6zt`PN()SkV;8!%#D;!O5SADB81%!Cx zo?jLwf`4x+@=eM1_EidJpEQ3n$7@FTwTC~1n@@kk)%$LNTqr*e#AqG3C|9RpOs8xgR)J|##GmGil*tmL zw^joMaM7b9sRLI1fj##r7`%FZoD23k#mX}@4ddz;-OGb{X&%Ls7~ZJFYPwzKR#cO9 zfWJE5C)#S+LSBQuW_aMx(%cM@<61wu!$IMr=oS9^b}Efy^{R^TZ&3H<^j||mmZ#lo zU5{P<5K13_%GYNjYM#~%G_37}vO15iBS<&Lv%P>CDTvzNbTl~RR6@Tzgx+xS8$2y5 zG1G^vmM{OhK|61??VZl`M)^2!-z*ocx4>j%4` zt#Wutp8_a^^Wp*_p#m(bl3$s1JqCyyv>_Qb1vbRMMIE)?vTVayCDiYoo?xeOlgr`x z{-#GC{JVtd=M@Ff_S6-<{L(H^UW5#3uGx~uFmW|EjBxDE0eVz25yV9!2ttGA{tli- z;}mmsP?(=lU`qrfJzY;dwwY_}wWGPKKN02A!=ZbRLlBwlc!nK|@>j|VL1#{i#fmd6 zhkdz#jjzQ|+4+yNNfG`EPHgw}nB)D{-3d8(mG+>c%)HXe zU~z4cBB9;|@K|FF0%F;Q^@x04-N@$owy@sCQ$dIgCLIF6LEc8~aS5~3W-qElqwA$uO z%_^pK(wPz`Td#4Rl&OS$TYeMcoNM(;pu%vJD9^C>(acwp zO^SxuDQ_I zp6|pFq<`6ArLGvwkw(Xl{c-03)Ld$T{7LrSMWu$^xE;FID@?eYYJS~olMOd1ebwLd zi~MI2>U(1iM86C{9LW~I%OY&l1SL!Mvguw{?M;O{o0-`~HIwoJ!iW&Uvxms^ax6*-8_(VZs^1?17X%ec{M%EBcqiMDql&ipM_X4g!$^sNJF5RZgBEe(P zQNwn5A2mt^+7@e%D&*(d3l@UGFJHKJZ^68MSXTsMq!tY2A}(mWdTSVTt!WBH*z__d z&ag^zCQG^cEn>aj1DAwA>(WWjW_>>V{=Tkr9QVtLQ=S=Ym#TPj=#O74zpCO35BlL= z`5vmxQ9cCM%V&2=tNE=L;f?x{dRMG2-StoEd;X3`DW}AwP=yteNJx>4nkZlefGl9w zRYQezk0rV`4qqh@w%+s(A4C3dqPD`Es3(dx5HzimWsccM&`0A2+R!6Haaj#!;v2sQ z#dbs{ddn1^aGRE;d*+LlhT{*}+9)XG?HXq_}$blC94&}iOZ$MjS zfwp(MHC-mKv}$w1zM})1_mP!*^I^I^SGHqQV3}RCw;Vi_fX@rTIk<@X6@+H_9L_{D zG~@<7zV6R1ayx|xh{Q)g0KACHa}M`BKP0X|rX4IZxFncH zgCRui5bNHdk{atR5duH|3`}5vdm7-*y+1_TcVb_rm+*Ow-WhdHET4l;5Y~;o_q%#fyP9|Mj=qgLuS}gvIkoRA5EQ(0^F1(N3mBZKRTjSQBLFPuHa)~ zDN?%EUfnpjIo6k6XTeVNrW~jayI(e{D^vf)HcGoTPe3TPLMjjSx7;2h$-eca7yhg> zgnKy-so{?1u-;jde@Q5QcnQPU>j!rb?? zsh(0a4ETSNYm&Ik&}1b^rKb@#Go-I#_ow#pVW6@F*A=%QZUHe40|(Yxrh zW1YhH{qb#rF|+7fS=;hN8{YwWz|#bd&=B_*9gz9M8-vnuuHkq^HnI7vx*GCnWfA4< zBL5Nt2~APd<@xD_YWo}^r94WU^9sYlyVn|1 zD?-s|H4>)^PT~yJX1d06V$0Ppi{claNir>3qib@TsuGjGnjmB%`Jo#0E+f;<{HOE< zA%ITL&Bc-8ZN-`-T`*+?14Gj-Nabb$b8x1&%~8^NoV9ktYidUx|CNSE<^}jS@t-mc z$vdl?$J;uU>e86`EOO)1yXj^|ZkVN&yw*mj4KS6AZ;o0M?w`J~r$_R#+qQ?UQodOn z?8e9!S0}pQoK;eM`$0d_D7;Hf4o}f8lz?V;=%_Rtz^e%)*&`zOH80Zw6?=-Oo;VVz zh(8;1OB&}v+A&lIy}k)^ zZ5xZ$nMq9Y2kt^3eZ(nTHpK8k0iclf)h#O0{=eWB0Ad7jkjtBh7DvQ2xl1!myC`&22dg-oWo2eppWR zR)-*lYSI=$<_+C!#yiGM#)8>71jD_flxd7qUq0X!Nj!QPuUk{r)ytBR^>)ZvPpF7e zR?nL9ulUlhK%7eMaq|{2Zc^^#_78h$19#9{jk1AojfAx`D$5&?crIq94*@wI)32at zLU|xOsmWGK;X)#*zPfzR3J9&;v>O@Zlb~_6zMF=wDPiC!0JCz~=v$>x&oQz)+xr>^ zEq3ah%5LMP9NO%!GQ?iU>H6iDTfsP?Mdwc*4~;5eQRWFZ-WT6aeNPA-r+5Z>Z(6HT z8;vz}YepIN3Rhl$vQxg0in~M;zR<@gM?Pg=E4^yMNVDD}@aXmZ8h{?y=0R$I8%7E^ zxi+L;c*(c3S3kdI1A3N6?b&`l&w_)Xu8mL@%454^FXG}17^=9s)~YD!6OOT~GcIp^ zcB8c*>#N!ylEUN7AwCzeGuGRDOfabpjFjbfx$4NH!8rMJk;3Mh&>0|K5t>@rJp7s!6v?d6!3U^beC;O6f z%1}Pz&XehEMs_kTsw5bUUETIExzv`~otC8nmh~PP+-vzN?t5+DcpB&GVj_PP@s?Jj zd?zms7o9-lWfg_*udJXH4qh?73_B5r4^i@MSKs|T8HU-r13V;b^5E!=HY0dn&JkP+Oi1}R2e_yiNK2|Y&;lgQ0L(SItog{sAfiyZf1CCF z%OB6=N&4DLhI{%4D+Pvo0rbv!B>YQ>Zmi=Qv5ju}T5Z10gZP^*MZL7ziF3RBRS74J z8t6@(n(i!Xfd6+gEuxu-?Vh5nQ1QzTUo+p$p)hcRvIomnGqN?(JqB({IKr(US< zZ(o1S9@Xb|!6Yw{kdCIdqc@1zXBvmNV_J6c6Q7HBiuZlNO789tFrjcM{qlAfk0=H2 zrFy~4g~b}ZHysFn6THjDj;7Z0J+TRtXsxFCnS_-hh$0Dm*-|Y?afHA~?oSM`Feaw3 z;)#(&f;++;b|ec$)1!f*?8E&hW#rsF$Ho1-6dn1m{+BtEjOYXp7=|ODPvzWt*4Z{l zlIimAy}(7G%n-Rb9HT{ie1E5P^fIge30uo;eb@CsmIbuK%@j>`t`nfNBpHGpS**f& zH zVxi|xqoIodE9cCA0^Kkw#F(Lj0f7QB3aKS{AE)z15-6_37DKhhVyR})0jn?IM@m{;v_O(|53>M#CLIFIYw zmT>&is%-mt0dLYHh-99h59(0#4yh=2 z6a%Su2AXKST^5o=)Cf9fK}a1hZ8u+|Y*Qk_vm#hf-@wnteaYwJTZACB4FZD(GU=J9 z*Gr?afL)VWCa#+K982$CwjgM|6A44%ZpWeIeQ~=()Y^~bU>FNALh&40l9(pNC-@#M zt&0~GM1saU;9v_yWRkOrLgc4hcx@>NIP!{9u2;x;d~SzB@AQ!}UBf~kI$cCMKQBWw zyP#n%M_TX7;}2<^S$JtmG0s!=+AgB(j>Goxg6=<}U>FPfz{M?mu|E&6&F^ zL!fv7D=Rw_V$Mf527`sZ-{PEIU#YEzLW-;oPz~#Sz!ayl!Hal`ZggXX-?*}xMa)e| zKv&(K|=2%We{h$&4bN>43)n_OS;yDg#4q19)UPy5lTr^T1V(^ZQAbHdr*tx#2 zenCtdw$LLP>*DK*iFQL5erCB>#8Ft{iG{9&um1AGDT4+iE=3T%SD~)(pd^J){C|HZ zBykLumj6G_g!li?nn?c7JLCp^fr0wJ-XX|jEfL@CK;{uvTXFfVm1uEZU4@XM!9^T= zIuQwcm~p(zyX)JOW2uN0%Hh{Cl1{4eXa4liR*4I4&Y`r4DvZvleWHz8?LHZk9!_kBK5!_6ZTtX~@uviW8pYRF9OSHr9j1uFs* z`l19c#+ZESy0|KFC4t$>eFu)lb^0x9zhUGb5@{_4lW67L{l+)+_6uuW(fz8V5%io#4w8Aqd(GfAje&0%<%;$Msmhn zZ+@nq_10PbC+*aXYzOcZcgJ!5Jc!d8Sa;*%8(QN!GtKW-$;8WGk1D8OV-z6Ib{t}# zI=!u0emnyX5Tm~U6y_G+xiTyJ_CoF}qPrImLF?<&3_CblED8f!M|E0Ta0^U%F&XI; z+^q-RC|0mrA&@;=hlo5sI32acEo z{O?SX!}#h&)+a^PD?dmmuX!a($}jvx{CHDz+^_){W7qzNgdfSInC?LX7j?9{Bf%kL z^gr$_h{EDJmnTW#+LUzk=}2suGkXTD6`fk|F#*Qj71uvY~)&Hx}c53oHyoZ!q-B!JLEk?^|LVj zZe0g7oBlN9yLUqEPgP$?`G`tOWZJlE=L}TF5tmf`Y}|V2QTx7ZF-aq7Wngqi?=FAp z_snjW@NiTdS77tl6MF9u;X>*>7838L_1gYNx1t#iBCW@)kygYyM*mUPrW{1gG^1OF z3`&3WCbl%r&ADUB2W&^HFHO!3_^PY_o9GFYMshI52L4r4^75YBRfO@v;O8Ms`_%xZ z@U$?!lW#su;`8r=f=cLL`;gs+FxM?6SUc5eyvSJ&6g?52K&1hiwWmsb_`fH0Hb>c_ z%94gj$lZPkh`_w{Jx1sETTX)h00bk{drSDS|aXfp~`4r5Df~eb9AnibN z{YUAzPmMu7Y@lus_I_VwX!k|@gjUvVCTAHCk+q)Pjtri_H~>8*9k z%zo0`u%s`?e#t=}scAy?a&xFxpP)Kv=+7`yB~H_4mjBVgb@uA zYpJ(q+S5rzF_>Llj!H%@E}GG7p;8CH_(A%1m1fU!&zi0~e5S#1od1wRwzg*HO6&B* zgM64)-V>LGHdhO_>p+WlNO>@CWlb5U(kC=rbeH6kv+Qzbau>&n{0HMI;+1ebZbEHy z2IX#@;jR$jM;h}iR`T~w8r!y@MT^zNtlfPruD|%SPWg6+?AqMO5S76S(XS53R(Ce5 zwUr982~Eu0YHQ!wIG`ck6@qx|af8N>u;-&H7Aw1w&}^~P&r>%ZpQPY!JeZtuSX+4SW8Rv4EFMyK7i_AMiW&B9Ol68q!}Zhg6vfrA zwQHGfjcyYR4Ch43PqVm1)&BrsuyRsfW^4;waa34{*qU!a2)__rmbNJ!q@Q?_Oi`8@ zm?Ce@;0nVTzEw}an(OE3}aj)0E6srM{&uq~`ow}~q zx4wq`iYO``uKj8<$#6Qq^=C%@Nu0OVej)oJ^!n8rqeP4W3YlIx_oNsR_D#MI1)Da9 z+wfky-@Z#hD`l94xcw(x_T+e?TGb8C-2d8oZl~&upByNe-f)Zq$s_6#r+)u}(wW1U z;aGD&a3FDfyI!-T`munzGVhVuR~78)?S@c8Fm2>SgLYyK$dUmmRm>G}d|^HWp)j|l zCUWf#IQ?36PxY@Xra`_8rjoaif>N>O19zwINwslZx?@uc1^Ud{ah6MII|-r-pM% zQk|n2!GJeIlg`A>u5w@dTyhxKG-o?VtcMuHzPA35IxdXRW-OL3oPcquwDST~-0<4q z`bb`Sk*pND>%?N^kL_H!@IE69ll=BjYOVl~U<1|7a2;F2ghy7ueX7!PuQ8b;W@~S6 z_q(3G-ki73^Dbc5sQwpW-I)eK+t4fBj^*g!if^deH)uNQWfnBXf082pP*YFf@qR_5 zm$Vz6cB3mnb9_<{#t6RVm1T@(N|SAu>tw;oEGui1wsW00y{1yBxeTInft9!PVLPWh z3xfGJFYFDjpv4;9Ck>Sx|JD*7v3!W~u6V;;dDY*WffMkYCKsJ~?2$Zm1=Y^94~kAbI@R8@(Z{e%{4r)qm9x~ywEK5`^#|m-dw5-V%RU58gM%GQ3cgBu zKK5WBJ@3x1?iehXKn+=iU{Px6MS~eRa$+g%{y6m0=YC09gv_bUFEo}Tv=)s*A`ZvEusd!XF=KQJ}>TlsuTNS|gN*yF|!)_WmwXhHo+ zZKq?6c3{|e1uVJf-`v+sWHf1~AC@~s#V!)tE+cCoSn z?2mclteQl8Dww}31jc`rcV~Qs54<&FXXva?yf*my`Q5uk3C&X`q#9moiq~e%9Rfh_?Ji-swA=z z0E{oF5fJ(0;+%nKTO<&i<#f|(L1r9L5cW6q?<;;hUaAWI&GV2_zh*hr_`7B#Z=jNp zc%r>aZnu6-`WpbL+ic)EVX?J;6V?#DK;G3}#BX>cv~}$*{m8Ow%Y;B2Kt4*3jw7se zj4^KTdO(PeExkY44ppb3)E+5=MmzJ{G~1X4Q5A1W0eyyvxI=cuknYZNMzL+eqaJ*I zsd-bIOF4M=7^FdW{>iLH+H8IjN0qJuswVZ*E>)id_zg(>)=vt`-~;8>sv!x5bkYf- z-=5%x3B_HPh54tDBv`d8InxGH2!gW&`m8ItL9}lbgD# z-7fdnj0m7YxDnE+A7lete9J?^IUd5Kg!txzti7=I6i{?s&MX^xl5%8lCI=^(BeoET z!i)7j1&>)6Z;!!UnM8ciP6S9EvcSNPMp`1=+GIXY=+ALvJsr=n^W@_`tu0puNk-nf_fTLA z%N~e&ol3;vn z74A<@JRIF4lw99S5QSX2QK*&Ogj>aQJr68hR@hGPqvFSA&WH1svRyhAqva}{lcP%W znBhKIe1Qsc>68{o6r^0+?IpJ;cY><(YypoKl>s^y?9 zBjR#;aV*P!)NU$vy7v{thc84mr2X}egm>S#f?hN&QVGMr{$cxzqQ3f6D4e{&7&m!4 zH^Mwea#wBy<##-nYSmr1y!lz}IiMNCN#>Emq(bn46YdkcT%LMcBw}k`-+D(^v}2nh z=O*elmp<)2(W7_LKcvAtoJBJ1w6OP$iib?GZbt9|1wjI~pk#qF1Ix9^fHd8dXA9oO z;8SbA?+IGqX)E-<=Wk3QJMJ$82*~Qc`hNtC{{i$Rhp|!u5xfKe-!9nfIwY6l@@)QX zVGXrI>{?tO&?%1Yl_~oxP}ga3@;T+}5fT=|&e=NfQjq3xV0n_)eBW@=tB3UKbC+5C z168;cWw=l^UHLUaQzq2;6Qgyk+eVN_HOG1iUI;|kj#*`iymtw6iVZ(qY(|rLO-;hW z;xCRQ8!2x#aI$bc8CX6+?|Cck>&S+#6*RQWYvai9??$-ILQ92}=|h4EF~S+66<a{p1 zJu_L4y-x*Q(jNxN_y*EAe7vH9CM3f+^Y1~E=AW7cFc=4d9!2e6E+fp8`FPgWq%>$M zhtDesBxRR66prsspME-smdbfytz)jv^jnptWxzC2Alu@VVw(TRV|GT;d^BF=eUy#~ zzOTn78_g781Sal58-(71Z>^vZTvu0~e9z)JciSJJzXXN|;}|#eV?t_|bn_z9%V4>& zer{<3e^BTXU_xa>k4%1b>dw64d^J$FKQZa6zh?+-U3ENr&^CtKrP`|Y!`)TeOG+DD zaINFt^}4v1&0xBUo;f)Q`4ZD6jPt3f5S!T{w*X7kXq9N9-n{5Vc7ZEX;co_8;^N## zW;f}f$H_AoAlcO|0cN|)ajPDY0ez~(OnHa^L(~J6z6aH(h%EoE8|zHAj^WpJ_6!2h zDJVEvaaM01fGzh=UqdYU`+F;-rp2$~1%jD*dFHb>X&Lg}!fPkPEv@#OX{ZU&mXaZ-r^1uYB z=36cF^RS+&kSilZ`_;TbHoHSIk@00)zKfUY1+93TIV{`_n0+w~Zo*?~y+l$iFIws1 zGKx7#OnO)rPc@SMm=UZUS-77rRGv5w-ya+j7b@UbQ!)tTX@$MR7chm74Xp*k4BL-EJ>ChR zkd)ATHF-a7bBC?X4+@>m($rDm^!|dWH9-~n=6mYB`@QCvr!Uw6qF@M<$qZFGD)h*p z=X^(R5`>+vG@nWOEEzl=HWgBwMW;nNCbdm|J{yMfJNcsmf|Uqk7iXU$*AN_#sBTUb z=qqa|A^zhktFJG}QNkiqqA*|>9EPaBgMLK^OqL2K4%;Wt3(WRU)0e$;y+N-qy44ne zh%!OfkFCHr{g*%V{H@hL`oJd+k4&d)Z6!_}#O;e83q?!U%`g*FbP?c582il{wZLWo>7jl`HjN|7ls1&&F6PAzk|Tg@tkus{Fh8 zs&eTNUdAxH{>dy-ii*md>iLGPE8X@?b7Ay6x~D@TNhfIvj)75<3Er8QHuvyRAU8%& z0{-9>Mf3&P=bHeM9!JbQ{DiAZLX!7ObxHoNbW^hXhkT(zsabwE4@27?!QTCEEdg(N zlND9qWB>aNDT6t{5b^G3`%o zvE65!=kL&EK2@=KhuL_8-YC>!(?NB*cl<1e=%ApmBl%D@w7?!%O^GQhqFInAi8=xO zLO)9`ld8t5#jM?EauGU}4D;PyuY?|?p>WK3Fk-=|v(2|b3KJyY;jm~3fdGRRT&*ef z&crc^Y5#A(_9pt@Yse?L&y7N8rIaWImjRkO)E~wC^2`q($;B0}m~)SU=zXS>d_a*+ zn_g8#jAIEC)5USiMjwqvUdh=BKYo7y=CV6I-BbKP&ZI*C`;h_@P5Di-_Gb=b`=B0u z0cRLH<~o-`o#Pj9)giORHjzr`lZQjC;q+tj>}|r#fxTJlcKyD0cN4v-&|Q(dHPTVW zlltlg*1|a_cB3*tvtWwde8r*N#|l3S6daj&3y6k@Z&L;-UPSTbD6XXH8sq)pwbbLbrv!X9-8*;enzbx>S8hj zF0QHQ@5=kr!$aoSO;`f@qd>VonYoSkAqAPhxGZAscMYfwiCG70}Bk` zoi0RDW_#d1>mp{G0R#mcE7u<5{_gTk%)Cy9*Fju#|5#!uhWPd8fdDoJ6jm^|BHb^o zH1KIR>H#3m>-gf}%7$S&-()s^6B~M7+Hs+DIUpB`<))|qDl(eBs#CC6la6N#TzYbF zLT!zO?O4`EplmixURq!s#*)Op3#nyINbbfP_CUHga!`}d5*E%H^DY_o)f#t$)0*{9 zjDiINNau!gM+oa3>qc8!=B~$vYru$z#n1M5Wny3<68q!y_MtDpoaHp=F3VikE2XE| zJS+@jmz@>v&b=_fjiQW@&%6t6)&8eg^K7IfXYM&wHcS%a1ay3qte-Z>d_kS`FxaC=YAfO|6U!4-$PPfbVbfVQB(_M|?;f<;Vy&)A8dtWYdNvW( z9k;sFSlUu#^fO08+r7Z#?R&|D5=ctB*VtyK%Zj9_uW%38&}_X2vC)uiG|p%VZ1Kk> zv}vJEc;TLv#FZcVPo$aFEbL~&ZU@heDeBROY0QK3$xcV#S=5@qigVC&4a&1C@T-CD zj0sbG*eMF@3yLM6ot$4Au`-h3pnwVB+I`6Du$2sbp;M>+bRJOb!nU3`MLB?-)uN^O zqgMmk>s@sxG6;rS_WdE@sH?E0e_BN3tJ%@p^NX@JQu&17T4uPak0<%)3Y`qPOJZBk==^{8q-3M7kk zuwTAdp(Fjb+eYjJm_d%%yKZj7aITLKSghJxX1>3aihZ^5b*2avOG@l9IGzBV&*s_# z@~DNOA6UmODa+5_FG3~Jc{j^ujob4zf<+=6F-%RiwS*X`PKx~{WLH*Gx0gvTZ3~Zw zzBjZimvbN*TFHG3_uz5VB&TKEDaQ6-INbB&y@*0rFj3%Bf--{D>D}`B9;#^k%badU zRH(Pz{jJ{cHF=-Z#WT$f`p^MX??7W-pl66G%-B_DS_`A=6ALif7_@2m!+xFOKisi1 za=CltQu5lYTDtd|8MJI*K0db~Z|x}XVj4mG*7P8w^0sucSj#36pIp>tM3Ofi(^ugL zH^f&9@f}e9z`&=Bp_@sZCZpa~k8sSGBQa#XR9=o?>{dOQ=asWS`~?DNb*!%AIpa*; z9i){L?+BsErc%X*A&E$EohJ!Od#n}2SQdj6y`;z5g3=V3>2ltlx4bkWDA8X2j_qu8 zb>$^-bzJh6k5$Lm8^0*Lzfqxq1l-8JOE9m*jSotmHZZN7WK#a5XZL4d=@wSDCB|~! zIoJ+?2W*33ruSPUJCXp?YFc?LRFbRA-&HtD(Nw{1nNi}K^5Y;C7Ja4R(4(ou7Q3ke zTPpV%Sqt?65yb0DQ8 zNF_OqkwA>bRU#mcI%*o3zym+GV*nAW0T3D}zGTz=P~!z==Ydg9&al<$UNSv zB_$OKcj{jL#kkKw~*0YT|J~*4H z5X*Js$ZJ<-x-H=tjDnOtV%3^s$N6V>p>KC_c^DY)xGk~U5V}Y?xksvX_GqlplhOa{sk{N zE>o(c8!teN(JOSoxQk!UmGK6-6;bUJ8{~hYcu}q=+(X>T%1;FIkd<}L0#+;&yHAHs zce|$t@2zQIYdLhUDid!e-elg2=DEzWR;6*v#a93#GQ4pP+=fy+jVRzX#`37_KC{E_ zL{`l8HW0HUEt8rH0DEq@(TiRxpBLNpsM6ve|+HbAfv1CS@_4ld9OEO37 zA|5T@A21<2gJW5f*8vK+7e7dC7rf-+hZTyaU(<&c!I2d2O?`7Tdkk=Q)89+G zC;Rg3S>=*)OC_6?JoHsksT*!AtKQNH|M^e^l^KP1z!jGdDx=SRL&BOBM~8D|y` zW@0u-LdVj?UwVvVJ0AB}4Y8}Cp7Eh^3NjQ%&Z95g8qU2C4FWRBfX;E3{J5~2qqSai z*;5I@OvKhJ*#gWBbrO2tD-Z<|Hi1VUdtenV;Qd~_?ivSX4U$N5&rk$y_~<8|DUxxq zOB}EOL-QSZl2i)H1d& z=o5*dd>6InT?B9*YNJyTcBpKIS90%ELcK87jY3*m!w&Tu;iY2~#-mZ`>rY(~FyJ6! zd-kjlX@x4SB%cw8Pe6sGuMw$Qcta;wu$tey)W#v_s~Ei9wMAU)#d(elb`>2at6bGC zCat7*ZEVf1io;?^37Nz_wXqkeHDgs;sN;)&LzOxD$~CYocuc~+&s0nXZ&|yCVoq3B zs~8dpa{rn>5}AO$M=8xwj%w}r1c-`Lu^A%yB*aRQ>Tv6#=dVV3Dk#(~Kndbk=$UtEO|o}=R(@S))uhSeTM zh-gLdnA3m#eab0-3UwSSvu%qN06Is=67E=))*kYX0XNK%Y&eJm+j!R&C_A$%m2V$> z$o<3nsTO<82I^;}xxpSQBQER;JX|TrC;hV@<_d4TgLN2Gkty4)r5iZ`+3nC4dF$6N zwbf}nNz@~LqA_hjYA+ye9cRgL6AtnJ)eFYK_-<{(f~R^eZJuNb+P&xMoU0kGI# zrQ$%pqfNR_be;HU$2dVs&j>-wZ>pL?{iZf0%$D*AJPX*7vCJ7-H_ibKUCq+pJ|5Wj z>mCX_?1Hi*2#T7I@?JKS;um&CoG<|@h?V5CTX!wTbKrN z1fDgwC|h8KOC~#As82~x0yRavhs80>U&->!*&$K?v`8UHN0U0(acFeW#&qp!y6m=>=$`RBImfis(Ki z2btYzvffb(o}5(A@A0?|g`du50Exa9kJrmOBd{BJ4liGYqup6$yZzgN0sHVW73-1X zo)G%sGYM{;p(jODDEy1mD=s?N7xO=mgfXrGGOD?~dT7daZ%<4D-!r3-eT`2R*_h<9 zuvs?wQ#>hTBQkp8k%4=k`hi&9hSg$(Lr`nkJGQqq!57*FF~Rs!&4hPo-x9-2N~Gie9*!aeF5RW@tywN_B7I){QRf-;Z%hC1s3+0ioe&*# zHS`J`wNaiw>F`t=^K!Bt?Y*AUBr2^+kyp@J-V%~zQ~tasH2uLiB#dS`OUC-}kDr`@ znWSR&l5SaNK#E|9u*GN_*jh*d1$kG}#y!ACwyR!EdEm z)q#I_BA8vK0NXf)W}Fl7b`!_<$;fv1nP+yh5o@~y;V8bUQ@y1Ks;%wDYbiAr#W17P zNm*>1Ga#;4pd0ktGoCH@c44BgI%w`Djg!01r>|8>Lki4cOt@x2VEfg0%A;y97U*P5 ziGvCGeag_f@*g7yoG|tGT^rXW7K5k)UM;bDNRsvx_LQ{=mGULsJ?Xqd-hHKtEyPU8 z!m&&f`&2zD8xrPatdC0Ut)ap+s?%RK)gYjl_KCRyOw05<0G?z5>9~ch(YPA;i*$!TE z$82bndH7a9$4L3ao-S{<4+;-6Ub(y>;L=Ps@oIva+W5EoQcq$lq<`ElCmpmXNT9rF z9u~;VE&8dPYMQynXXOSCJp2nTo|ERph4rB0lmlVdAss>$vW6Sl7iA!|F+yctDtPo$ z84MhA_3?){uKNqI-}^1xdB3EUNp^*D94sJkX&_2t5)%?nnDESJK22pwiYsK4&ArCR z`S>rj^_*Nx^rEBSihQjYGl|^{#^!D`(5WWW8R(oBw`+i0c6_1hjq+)|_KwQKm^$;X zTfpS`z5!;pCOre`7y4<$av5t&@e19c%3SGkycSZ6rL*%o)1J?TiAqNWJz$>B(oGBk znIr|e;nKbQ0?ySKl;zYU1-24V;+a+vnsXm%6H~9lK1WEG}SbZ zJfxwh`*4YJO0}T6DBN&;O)H}=^UOdA?@u&K3?jx3Bs;DR%(+g*&Vfi3z$Rt5%JQ3C8)zgL ziAA848iMUEo*J{e$h87Ps11TUoxU#<5vcm-++I#za1u-pPRx)$C9U!k;EtW%`#YiZ z1!kbgQnPZbrC3LC!f}BQ2WPa6U$PR>`0>928z|L+T@a|`fMdXJN=Dk~W@ENmZ zS`)m6Mqh9d#((0`5xZh!OM$HW1d=Pm?--I}@Pu{(4M!12M#`{hF!WeApR}MB#p2?} zHnTvidKD?o2T-x+$cr$&iV9|(<$p@3G9>gPc=^$UoBYuQUN?KScAdzn!u6 z(Kh!{EXkaTmbXJY)=O_mJ55WJ)i+w8o4fm_*56)L#4`V%wyry#s`roIW6S0m*&}3T z&#!BQvNNxh5fULQ*%uihgmfqhDUvd8R%B-HSw_e#l%18|antKZb?)o_^SPh*`}2P0 zIS=P~K5`{PF&(a@dRxq?cNW$)&h1wx?o4ubaGB@Ls=dIoa+8fjB}q|_g)fin*e~9* z>0TR}ozpK%v+`<|&rHj5lit9C%>@<>QL4 z;a$(H+p2i2ceZ^OS#P4r6<4*Z+Uo7(z5Bj3-ja{jm-bDaRZj+dSSELxC)VTNDeXBt)a!ObnlJ8ojuCYFBV=J&aoh{Hn<^%;IV+^+?_&TC{{7{9F#Z zu=KNXGF`5YBhKqWsP9g8mhT9ksvFlU9}DoYj6~RmCE=>C84A6Z8qA1ZHm}{$7SG?B zj|y#zHN7lHPR*VfCMAI#UoSkL9K=$bq^Lc-y468KV3F%5v8-4=rCQ$MMJ`|kw^`aQtD@SQc@0$4KZijEmaS2cO<)19)j@#&U)ok@Lf?=xcs}-(k z^9StaRF)PpE7*ocZ3WSj-MB^zvSG6i^py)$M)=+mF~W!mSQ}Z}x@JB#qPk@xx949g zs>pHY476>|-J_D5SXCqPD<^p>w3R0o8!V?8O_pLj^ce-KeEtfxf2P?jF1$uqr*8&j z?6#;z3#OHp@yNBtWHz0tO=VZz<5a})t<>GPn#E5#{a>U0F1;#?gW1-cp^Kqwl?tu6 zpk4FaBR9ai2Byd1UwZd#-zr||ME)0+JPRcXX>G8|HJre~0-b&_#@=Ab^<{7N7makp zDekg!KUi67L+>3vB#N#PRWC#7_!gk=Uk{0i4wr~fEo3bwVyA`DuVj!DZr#R@6jX{2 zfj`|x-2E1C-H|ZPgG2UAOVFygHv9axub36u_md16Y9BZw5(^Uxb@Jw^Gu#fb_6iSd zNdiRuE?)U;K#$A6)e})vEKvN#`1v|J%OkY#Re3pF(u*re6dvJs52z>|(^lyX;oB5C zYMFj=C|ZgyV>&7JCiOx~;kzQG_gsANaE*;s1IFiAn8wQn79XAoUbg6a{nMd2$9*vI z-K{z1(FzIOXD=%i9t%8*@<@z)U}&ul|LjFy)}*SD(Y!W8(^`0{hSrRrkZj82#gKL= z7xCHLHhYZ(r3&TfL|Ous2+59}=^udx%B274U52Als-*Yd-_@Y)=frIWXpCqJFTPcdj_veX2G~Hg~7zL z)mmGX5hi0;=P6}0T4wvQhN>f{viHvXypA*2z8Da+l2win^UfJK^T@^Xk(IML@|`)N z9AUY3Gdf9~FkV)^&9We$fo5?xGT-3N=j&NG4vMG&&TO}ZM^!=PeHvITJn9lHhWE>> zyG9eSa;z=}GJKrWrvA=-o3-+-A?}+~25_BQK}yAf78kRnXwC$o35N?70=tldzF0(O zEU^}^(z(Kl!8;K5(MN*H%nx>-E_~H3KQ|g`j4xZ5 zuee|GCH5M+luF^QMG$f{A7`RVuqyESWL?pdGNgr|L8wZ;iei|;o}4M^sP8pedXzb3 zWB+d0&HjlIalTJCPf5`@a+E#GE76R3xev2PrBs-?Kank~YA1@s+s84;U`3^V8z|)E zX|-*`@@I^3X~Zj(E?%1qZVzPl{5++Rvk3YT zDmnfA0CB|4cXOudHhPlLh~f|>3D&%8r=T~k+&;v6$ak8#D@dg-B`C~th4L=@n-R43 zUKou0ads5tUmMare5OqleXYO}KX?RfYoBmxgwIsrSXN6Z}#fo>%P1*UFn1Y->ImWAEBCR1bM6z_0VXUEJiZxM%XbWT(KoOScp% zP*_0qK}L1%)(gVz<{}G(8Lq7FGi3*zfoPY9HBrm!wlV8C)f*a{EHWlRJuf@Oztx&Y z*zM%S`?nN`b-d)DyDURHYk9TMD!-jz5665tt)b)HB>9|4RO#?@tx)qsACKt%_km#4 zdA)jqT!n9JNW+Ug26kKNoh#4xHXSWRQLzF4QB<%aMFgxS0;xGs=XBFud=$Ro+i2Zj z8lYXQphH}s8g3-({`q>V=0(!d+l*^TNV>6NkvDV0BFgYt(*Xu_1hc5cReT|y3$6=S zTe>DX&*2RV+aUv&eY^N6#A{`AWmjQpwib1cDs^2p$RVV?(<=+&{#UxQT1Z#Q1=M<~ z2c2cct8qoelDyTdPxQTh*KUm}I4So;FrPckWW(H?N3~_1ML8~BAkzc?OrI%{h8Cbr zyU$_XSQ4EoqSo+*Nt|BD^eMG6lSBCe16O!!*@uUN9!Z~K_(m9srasKFln633>ei7_ zX|7@syW}ZfEi!q5#|e%Mim)ah1`n%CnFuK1>PbomJIkB}!D=jL%zLjDv$}c-Lh|uT` z=vNB-w75-7qpmyqIh;V$B4NKQC7#o0bR?eM@?l4O(;78HI**erIf>g{wbwI*s9v_3 zIq%43e~0^>D-EYr2^n{$WEi8DR|_VUsN?lE^E|bp&srVfE#~hvm{t(I9pVfKBe&DsxNVG?r_e= zmssD{!FN|^!*cHrthZC1rsc7`x{VrsDs*GBy278V4?e{pC6Tk@d8M)aCrm*b7#|V= z7ut~&J9B$Vi`L%PLcAW23$>C}R3JjrULJ4~2p(+rk`G5Fz&V%l556j1!B?xXE({Hg&#(p=zUc+lx-EDYW zOg5!M%~SVgtJebgW%}s2nZol?S(&H=-SDRa56*DUUYfax9!9#MFFKY5s)VM{zRbSZ zI9?db7q{?$9mmZGSiMolYro5mGoc#z!n()38o%@zT>E)*ZyHIoX4`w3k2zzwOT7F7 zdb0jEoeW6>Z|!iBZ`2e8x>7WiHIH?@MN=`WGmvZYr^yU+u+HY(w1IU#MYTG2iZ$sW zAG>>YAf)O`G!W8(EFVh(3MGguSMW<2_r8!yrN%l3r0VZUJUS9>ksVRPj- ziS)jZK@s*WfuaV%*h!bVD059dhihvUxQ=?dUAJh;?kx*ACcnMu%2ta|3Av`$Evnw) zyI&PwjGK^XihGKBv8b+ZxDgeaBCQY~GqA9cZ=m>*o)F!E(5*^a$KPW@&eSyEERk;8 zI~y1AHJr5uxZ&%&oV@9QDrVXF&x-|fNC^g!)-L7CUmu@-Rje^>K+;fq=^@<^vvsF@ z>wnX>rM(^6`n@b3`ZVb`~u0 zwefoIPwO^~+I(`WPFAz@G)#c};rIipipVfxvH{~@`r(XGxMLe#eiWQOlG+&BS2PeL zcPc6c)iuDx@t}LYZIXq{Ee0uPC6vzVB&DKPkwD63+TRhR8k`V&t`Scb@1;(Q(t~HZ z_~u;-dFQ>RX-4Iej7dFEcbKHJerDppS?Cj<>x9pIpm|-y+r&+FuF%B?j!UgucbC#R zf<;&%TMyJ^6q;{X_}+k>UK}76V{)bXmVZ;+*Tp=dww3$Bu3R=)^Eao}E%;;Ome5ZJ z@|bExq*`|J>(2g!w_7bOiY`##;#=Igy1evu5IU5;&yJ1neL!I-HR@(4GD4~I(bBrf z_pjJB8`Il2m?oAxLO1VDKV{>v_fos!+RPN^xnv!j#iILEL%d!_a#m(SID?1YW{si5gVIWAPd%6=SVXwfo$ox~` zZTSWrVey$jKQ|^N`a3F-Or1B0DF)6L)Qbq7gWKPw(P#H8*77H^Yj3^BXk7V2kc6lT zA+6u^@_|DxLr-AsdwXr-Q6Nu{qb zy5w;4^NS~+hWG`hh7zM3SaLIQI{SY#4knM4t34-+NunaD@90dQt5LzSw$vo|ZR;LXqX$R9u_r?1*GK#|zY2RXI^-2PjwEO1bqq+?sA!%4F%#%}dDgpSSnCyq zyFRO_ZsRxg8zZ}@*lnDTKXE3Nx2va6x5G=pksngMI-72ylq`L={Hm%`HWHL9LmVr@ zERk{f94m!%k#Vvpmi%Wp0aCQ>_pkj&7feHApya`uAmCa2w8 zpODi)sZS`e4W2H1;EZld*8GIC+a2VdFn9SOlnE`oeIZ;7#Yb23tOE-#NDa9GnD-j` z!$40YOAY5(BfhIz-xAxob#6EO#n!CRwxU6OT#zRw(X=w>4d>5Fbs2jdK}YXCA#-Ni z#f&c*lGi;M2rnR(&Ic^&^IKuf-{uae5C4q)d=l{^Xr_`3VI@3$o6MMFcLMvd$&U$a zqXvX`?`^;gm0p7|Wtc}`{~TEor@7R6kn}KL-C4(foH^k2PiV=2PvCd@Y~pEX`5;4m zxP$<(;4nCh;MW?$=tbx7+Fj^3DO?x~+A{mc~VJM(YY21#NtB@;;B4iIAs5-x-#E(6IMgWk|RDengK_`#R|hDI=Bpx|$4!67t?1?>0@ zHJwx&`gdngV#h$4I{+F?ZTpeMl3j*MaX2-e>~YE*iEg=cAhH*rz%=RgUMR>HPK+@f zGGgHN<3x5ZA^0fNQYYclXThG{qu#K82p#4-gbIrZdi~w;Ut5q$+e0*!f2mLu#02!~ zI|^8wsA!rAdCvS5a775?+3?#l_&hd&CgiySwfRYDqoR-}6WJf54`l0yRJuuESd99Q zO6_=P?3KY7pZqqibU{XUxx|cn)F9Y(Ar6g+tg*O%(PMrmP{4pVx{rhO6(J)% z=+X@kV`>`X`B(|#nSa7_;XKB;al$C3f-%bW9Op!-9vb0N{}{p`hQIX!2G#T%HPQo> ziCPU~BM&5@0{|EK=bRPpdVqD9B1Q$OYK{|A7LPd$tQ`XA6DQ_> z4I8$@+2<0`bkh7e7;LK)fYF`szjXOm9Pq~=K!Ne)%DQVt1xQw9zMcmL4#rNo{u5Bm``CsxrJi|~3VIuiFG)d8ey4007us>9BWFJv-4IfeB z&lvxJ|5H_E>5(e;VStA4u(a(QYVXp)_+fw=o3;FisOmS7%73f?Jw&wdSMl&#kBA6A z05lkZM%b~l?L07;g72Tz=GKPc<;1wv&uLZtoAUO?5oP*sO0>1N1veZ9L-zk!=H<;J zsZVYLxZw8>N3&>}y_pIhq_^cSy=FvU4OIW2;4x~7;HEnI*|N?>jdvF)`Ag$5;OHN5rOZ9As1DsXPr6W zN+I)$wfa3kgt=c}n#Lsz7upa5gPA}|pqe>$$;tvCvKe5;3^|7Zs2{8U2Y?X+04oM~ zJWD3jT*t0rc_5LcoEl^q0EjWJ$8&;_9CL)h_O$^}a{!>n#K=92A!7u9%>w`<26#Nb z75xd|J7XZxXMh1@9XyJ0JSP;<30E~!0Ay|k=rD}Ok9CIz*0Iaj3;?4$0Va&w&0}Y! zo^&g*2S6>zjo@$`9iBr1E_Xs>{hliT;&&aHp#vb0V8Nl;JQx65AaBb<@8f$MPm__lob5cVrT(}N7#-gtbh3a3F#y=p5740wgB*YPa=0AOb4Z@73u g&#aaPKCX5imw(++aPbcPG@y?$)F)@&LY*4+KiLy3l>h($ delta 63344 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

0)I^W;A+R;nUGxTpziBBtQ~Jigw4 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 diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index 150d2e8f913e1d57613d07c109f43fc119a899fe..06fcde6330bbd390e33e36c2e6a452a2ea89aa89 100644 GIT binary patch delta 829 zcmZvZO-vI}5XZaSX$y3BXG^UnM#D2)(1X+)2Z@bMV;d47wiiy@MmUftZCbeC0TL5A zS}$-hu?J-j9;`9xyGd^zCB&fdBZ&Bc8lfhF>4$mFgJ2foVJ7qb`)0)@_JV7WS&nB?FX;lliVwZ_Q9?%s!Vyo8>=cXWrYi&uAuMbX;6|% zd_JIKHy6Foz|iL@+HCc_o3et-lwBLR%mWei8TvjRz^9>!M{W?X+3%o#{-d`J(1W0v z+;74rde`NM*IZl1e5n zv3w!JGUFpG8jnQ>tby3Dl^Pz3v0c7LC5Pb2b8hKC{n^~aSav*f?GQ^}`rY34P%tzq zL2{IX&K#b!Yx%!f`PntgL*b?HQM6ICQ*`h(%io=|A%`Z1C4UQV)%=y31`!*Ebkl~2 zPI_5ybk;AXk_!x)!RsSt%M@+WgR2qmBQ*tG5R$Wdo?kG$_?{W&@K|&R`tE zMhoY{2H5akIu_|z32Q)QNq3vvYZ?u#lr^BHC4 z#ZF3UKLXkt>=fCMt9=M)TiCfPxmN*gquyyUS6#vGD&wyD5`|zZ5Pp4(s~L4-J~{$p z^Wp_F(a7^Ha5gJ+-ulT9brqJWE1zH)qjB~e!XE=36u`US{JcrT_frA<*FU_y2TyU+ zMx#nQ^i{dz9f~5SL*DP9#(57kIsS~|wj6&6jQe+LO?uq!Zz?^xe+R`w$#w%QiY5=& z1}h3|z*D#fl_!T|SK!i7uB*-g=Qul;%w#W~zfdk*n=VgfCz2UAHR&eKOlC95v+j*@ zVLu~qctiT>aV;p&l)KHv;HC!|c+|J)QiyOz_abgD4bKB@t#L0rojVS?Px+K8Fl zzRx2>wk@~LThe1k$P4$mFgJ2foVJ7qb`)0)@_JV7WS&nB?FX;lliVwZ_Q9?%s!Vyo8>=cXWrYi&uAuMbX;6|% zd_JIKHy6Foz|iL@+HCc_o3et-lwBLR%mWei8TvjRz^9>!M{W?X+3%o#{-d`J(1W0v z+;74rde`NM*IZl1e5n zv3w!JGUFpG8jnQ>tby3Dl^Pz3v0c7LC5Pb2b8hKC{n^~aSav*f?GQ^}`rY34P%tzq zL2{IX&K#b!Yx%!f`PntgL*b?HQM6ICQ*`h(%io=|A%`Z1C4UQV)%=y31`!*Ebkl~2 zPI_5ybk;AXk_!x)!RsSt%M@+WgR2qmBQ*tG5R$Wdo?kG$_?{W&@K|&R`tE zMhoY{2H5akIu_|z32Q)QNq3vvYZ?u#lr^BHC4 z#ZF3UKLXkt>=fCMt9=M)TiCfPxmN*gquyyUS6#vGD&wyD5`|zZ5Pp4(s~L4-J~{$p z^Wp_F(a7^Ha5gJ+-ulT9brqJWE1zH)qjB~e!XE=36u`US{JcrT_frA<*FU_y2TyU+ zMx#nQ^i{dz9f~5SL*DP9#(57kIsS~|wj6&6jQe+LO?uq!Zz?^xe+R`w$#w%QiY5=& z1}h3|z*D#fl_!T|SK!i7uB*-g=Qul;%w#W~zfdk*n=VgfCz2UAHR&eKOlC95v+j*@ zVLu~qctiT>aV;p&l)KHv;HC!|c+|J)QiyOz_abgD4bKB@t#L0rojVS?Px+K8Fl zzRx2>wk@~LThe1k$P so it gets removed + return field.split("#")[:-1] + else: + return "1" + def prepare_testssl_output(self, test_ssl_output): for site in test_ssl_output: @@ -355,12 +363,6 @@ def prepare_testssl_output(self, test_ssl_output): value = value.split(" ")[-1] self._user_configuration["CipherSuite"].add(value) - elif field.startswith("cert_keySize"): - if not self._user_configuration.get("KeyLengths"): - self._user_configuration["KeyLengths"] = [] - # the first two tokens (after doing a space split) are the Algorithm and the keysize - self._user_configuration["KeyLengths"].append(actual_dict["finding"].split(" ")[:2]) - elif field == "TLS_extensions": entry = actual_dict["finding"] entry = entry.replace("' '", ",").replace("'", "") @@ -378,6 +380,8 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["CertificateSignature"] = set() if not self._user_configuration.get("Hash"): self._user_configuration["Hash"] = set() + if not self._user_configuration.get("Certificates_SigAlg_KeyAlg"): + self._user_configuration["Certificates_SigAlg_KeyAlg"] = {} if " " in actual_dict["finding"]: tokens = actual_dict["finding"].split(" ") sig_alg = tokens[-1] @@ -387,6 +391,23 @@ def prepare_testssl_output(self, test_ssl_output): sig_alg, hash_alg = hash_alg, sig_alg self._user_configuration["CertificateSignature"].add(sig_alg) self._user_configuration["Hash"].add(hash_alg) + cert_index = self.find_cert_index(field) + if not self._user_configuration["Certificates_SigAlg_KeyAlg"].get(cert_index): + self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index] = {} + self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index]["SigAlg"] = sig_alg + + elif field.startswith("cert_keySize"): + if not self._user_configuration.get("KeyLengths"): + self._user_configuration["KeyLengths"] = [] + if not self._user_configuration.get("Certificates_SigAlg_KeyAlg"): + self._user_configuration["Certificates_SigAlg_KeyAlg"] = {} + # the first two tokens (after doing a space split) are the Key Algorithm and its key size + element_to_add = actual_dict["finding"].split(" ")[:2] + self._user_configuration["KeyLengths"].append(element_to_add) + cert_index = self.find_cert_index(field) + if not self._user_configuration["Certificates_SigAlg_KeyAlg"].get(cert_index): + self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index] = {} + self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index]["KeyAlg"] = element_to_add[0] # In TLS 1.2 the certificate signatures and hashes are present in the signature algorithms field. elif field[-11:] == "12_sig_algs": @@ -418,11 +439,6 @@ def prepare_testssl_output(self, test_ssl_output): values = [convert_signature_algorithm(sig) for sig in values] self._user_configuration["Signature"].update(values) - elif field.startswith("cert_keySize"): - if not self._user_configuration.get("KeyLengths"): - self._user_configuration["KeyLengths"] = set() - self._user_configuration["KeyLengths"].update(actual_dict["finding"].split(" ")[:2]) - # The supported groups are available as a list in this field elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ @@ -470,6 +486,8 @@ def update_result(self, sheet, name, entry_level, enabled, source, valid_conditi action = "should be disabled" if information_level: self._output_dict[sheet][name] = f"{information_level}: {action} according to {source}" + else: + self._output_dict[sheet][name] = "" def _retrieve_entries(self, sheets_to_check, columns): """ @@ -784,7 +802,7 @@ def check_this(self, **kwargs): """ :param kwargs: Dictionary of arguments :type kwargs: dict - :return: True if the year indicated has already passed + :return: True if either the one calling or the other is enabled :rtype: bool :Keyword Arguments: * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not @@ -798,6 +816,33 @@ def add_notes(self, **kwargs): self._entry_updates["notes"].append(note) return True + def check_key_type(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True always + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- The name of the algorithm that is using the condition + """ + note = "" + alg = kwargs.get("data", "").lower() + valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] + recommend_dsa = False + for cert in self._user_configuration["Certificates_SigAlg_KeyAlg"]: + cert_data = self._user_configuration["Certificates_SigAlg_KeyAlg"][cert] + data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] + if cert_data["KeyAlg"] == "DH": + recommend_dsa = True + if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: + note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ + f"with an algorithm that isn't consistent with the public key" + if note: + self._entry_updates["notes"].append(note) + if recommend_dsa: + self._entry_updates["levels"].append("recommended") + return True + @property def entry_updates(self): return self._entry_updates From 9f4fa66d05cfdb73da6e5bbb7fbb20a4509bc201 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 17 Apr 2023 20:08:14 +0200 Subject: [PATCH 092/209] Fixed checks for "key lengths". Added the CHECK_ONLY_FIRST condition that makes it possible to improve BSI "key lengths" compliance checks by having checks for 3000+ lengths. --- DatabaseFiller/guidelines.xlsx | Bin 119123 -> 119174 bytes DatabaseFiller/requirements.db | Bin 1101824 -> 1101824 bytes ...ault_levels.json => default_versions.json} | 2 +- .../compliance/condition_instructions.json | 3 +- configs/compliance/requirements.db | Bin 1101824 -> 1101824 bytes configs/compliance/sheet_columns.json | 5 +- modules/compliance/compare_many.py | 9 +- modules/compliance/compare_one.py | 12 ++- modules/compliance/compliance_base.py | 89 +++++++++++++----- 9 files changed, 87 insertions(+), 33 deletions(-) rename configs/compliance/alias/{default_levels.json => default_versions.json} (96%) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index f263f9f89310e24ffc20c3262e41fe9c2286e877..132e613384f428528655ec368deed94763484ed9 100644 GIT binary patch delta 46491 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 NsGxD$Ub_wKe*pShcQ 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 I2Jqz4ABYJP8UO$Q diff --git a/configs/compliance/alias/default_levels.json b/configs/compliance/alias/default_versions.json similarity index 96% rename from configs/compliance/alias/default_levels.json rename to configs/compliance/alias/default_versions.json index 844008e..de60f8e 100644 --- a/configs/compliance/alias/default_levels.json +++ b/configs/compliance/alias/default_versions.json @@ -35,7 +35,7 @@ }, "Signature": { "NIST": "", - "BSI": "", + "BSI": "CLIENT_SERVERSIGNATURES", "ANSSI": "", "MOZILLA": "", "AGID": "" diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index b363bf6..cf7afde 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -9,5 +9,6 @@ "CA": "FUNCTION check_ca", "THIS": "FUNCTION check_this", "NOTE": "FUNCTION add_notes", - "CHECK_KEY_TYPE": "FUNCTION check_key_type" + "CHECK_KEY_TYPE": "FUNCTION check_key_type", + "CHECK_ONLY_FIRST": "FUNCTION always_true" } \ No newline at end of file diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 06fcde6330bbd390e33e36c2e6a452a2ea89aa89..beb487069cf9d8bcc82610f0e87853119f54d934 100644 GIT binary patch delta 1576 zcmeH`OH30{6oxzPbRNvyd!|&V29PPmN31|wq7eZ>Dh3oN4>znRVq9o=D2WLeBP{QByG{x)7hyg`tRX!^7yAwx&a3gKl52x(WNk= ztfjMvVwF}L8JNS-@*U=MN40Gh^cNv^H|l(-W8Ua1y{rRgiZS~(x-!OM*fBVLWsH}b z3%f1w7$%?#O6V$`rB~@Gx{pfaC7B{8NU6Le-<7-XxqU%dtafmWGvskAS_=`Jny*O6 zb+*^;Z&d@nz#eb4lE^|L+w_EYXge2pDbpl@ghXp5g3rWvQRHPFw>G@MZ!vR{=CTxgUL8G2U(R@nQ|I5oBhT@=xtg|m z?cGy8+q6f3!(1uuy%6IIOlt!61ZRyk?hyXBDiEX%DboO(5ZG$8z7jz-S(e%`Q-sGD zT;x3_9oe!7s5pOH$lO8y}~ln1cYq5rM+{^M4c#9NKG=K=`%d8xh?$6M_&T6(&_v!m~1SHJcQAD`(q z6vw-cOJ9Sq*&bgyx(Wh@h>?LIVaWO_5HWh4rOGt>m8jF~TcYT!Lf7Dik47(#Q+AqF zKrsY53f5r8C$p%n2Gm9+{0f7rv*W<@Z6bFpFb~(@C=}Aq^d`MXkJD}B3%P@rXPk)T z63;jtR!>OlrAppbwX^4#8my}g9_|P=HMDmetZ!{QqWbq{Bvg$e7I&adn*=3WO{1+8 zQJZBrKA?)4{YS%q_KGS&Qo*6riBh}q4DuJj=y-rx*A>7$ex{dI`v9mEc>U?;*#5YT<;PO&{c+XkO)q=k zH9UZuP=ql1%AT|Pte$+cblqC#U8lrdz-2$$Ye+I)yPOzW?yDfg&X(HxqtsY&jGUG|baVuS1wi_-7ZrlYin>c$Gr)%zZ1`z`T(te>aYlrbBEhD>4Rt|WxmiGPK?FZfzXz{9AD1s3MYw>O%>cjK) I2Jqz4ABYJP8UO$Q diff --git a/configs/compliance/sheet_columns.json b/configs/compliance/sheet_columns.json index 4f27067..dcccab6 100644 --- a/configs/compliance/sheet_columns.json +++ b/configs/compliance/sheet_columns.json @@ -1,3 +1,6 @@ { - "KeyLength": ["name", "length", "level", "condition", "guidelineName"] + "KeyLengths":{ + "columns" : ["name", "length", "level", "condition", "guidelineName"], + "name_columns": [0, 1] + } } \ No newline at end of file diff --git a/modules/compliance/compare_many.py b/modules/compliance/compare_many.py index af378b5..4369c50 100644 --- a/modules/compliance/compare_many.py +++ b/modules/compliance/compare_many.py @@ -13,14 +13,19 @@ def _worker(self, sheets_to_check): if not self._user_configuration: raise ValueError("No configuration provided") columns = ["name", "level", "condition", "guidelineName"] - name_index = columns.index("name") # fill the entries field with the data from the sheets self._retrieve_entries(sheets_to_check, columns) self._evaluate_entries(sheets_to_check, columns) for sheet in self.evaluated_entries: for entry_dict in self.evaluated_entries[sheet].values(): entry = entry_dict["entry"] - name = entry[name_index] + columns = ["name", "level", "condition", "guidelineName"] + # If the sheet isn't in the dictionary then I can use the default value + columns = self.sheet_columns.get(sheet, {"columns": columns})["columns"] + name_index = columns.index("name") + name_columns = self.sheet_columns.get(sheet, {}).get("name_columns", [name_index]) + # if it has multiple name_columns they get only shown in the output + name = "_".join([str(entry[i]) for i in name_columns]) level = entry_dict["level"] enabled = entry_dict["enabled"] valid_condition = entry_dict["valid_condition"] diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 96d43ca..507c6b7 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -12,11 +12,12 @@ def _worker(self, sheets_to_check): """ if not self._user_configuration: raise ValueError("No configuration provided") - columns = ["name", "level", "condition", "guidelineName"] for sheet in sheets_to_check: + columns = ["name", "level", "condition", "guidelineName"] # If the sheet isn't in the dictionary then I can use the default value - columns = self.sheet_columns.get(sheet, columns) + columns = self.sheet_columns.get(sheet, {"columns": columns})["columns"] name_index = columns.index("name") + name_columns = self.sheet_columns.get(sheet, {}).get("name_columns", [name_index]) level_index = columns.index("level") condition_index = columns.index("condition") guideline = list(sheets_to_check[sheet].keys())[0] @@ -30,9 +31,10 @@ def _worker(self, sheets_to_check): if config_field: name = entry[name_index] level = entry[level_index] - enabled = self._condition_parser.is_enabled(self._user_configuration, config_field, name, entry) - valid_condition = True condition = entry[condition_index] + enabled = self._condition_parser.is_enabled(self._user_configuration, config_field, name, + entry, condition=condition) + valid_condition = True if condition: valid_condition = self._condition_parser.run(condition, enabled) if self._condition_parser.entry_updates.get("levels"): @@ -46,6 +48,8 @@ def _worker(self, sheets_to_check): # This is to trigger the output condition. This works because I'm assuming that "THIS" is only # used in a positive (recommended, must) context. valid_condition = True + # if it has multiple name_columns they get only shown in the output + name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) note = "" if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index ec67b94..7e78192 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -22,14 +22,15 @@ def convert_signature_algorithm(sig_alg: str) -> str: class ConditionParser: + _logical_separators = ["and", "or"] + # simple regex to find all occurrences of the separators + _splitting_regex = "|".join(_logical_separators) + # same as above but also captures the separators + _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + def __init__(self, user_configuration): self.__logging = Logger("Condition parser") self.expression = "" - self._logical_separators = ["and", "or"] - # simple regex to find all occurrences of the separators - self._splitting_regex = "|".join(self._logical_separators) - # same as above but also captures the separators - self._splitting_capturing_regex = "(" + ")|(".join(self._logical_separators) + ")" self._user_configuration = user_configuration self.instructions = load_configuration("condition_instructions", "configs/compliance/") self._custom_functions = CustomFunctions(user_configuration) @@ -57,23 +58,39 @@ def _partial_match_checker(field_value, name): return enabled @staticmethod - def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False): + def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition=""): """ Checks if a field is enabled in the user configuration :param user_configuration: the configuration in which the data should be searched :param config_field: the field of the configuration containing the target data :param name: the value to search - :param entry: the database entry (only the first two elements are checked, they are neededd for KeyLengths) + :param entry: the database entry (only the first two elements are checked, they are needed for KeyLengths) :param partial_match: Default to false, if True the + :param condition: Default to "", the condition that the field has. + :type condition: str :return: """ field_value = user_configuration.get(config_field, None) + check_first = 0 + + if condition: + conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) + for condition in conditions: + condition = condition.strip() + if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: + check_first = condition.split(" ")[1] + if check_first.isdecimal(): + check_first = int(check_first) + # If the condition value isn't a number it doesn't become an int and the validator gives the error. + Validator().int(check_first) + enabled = False if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): # Protocols case enabled = field_value.get(name, None) if enabled is None: enabled = True if "all" in field_value else False + elif isinstance(field_value, dict): # Extensions and transparency case if name.isnumeric(): @@ -83,13 +100,20 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = name in field_value.values() if not enabled and partial_match: enabled = ConditionParser._partial_match_checker(field_value.values(), name) - elif field_value and isinstance(field_value, list) and isinstance(field_value[0], list): + + elif field_value and isinstance(field_value, set) and isinstance(list(field_value)[0], tuple): # KeyLengths case enabled = entry[:2] in field_value + if not enabled and check_first: + for field in field_value: + if field[0] == entry[0] and str(field[1])[:check_first] == str(entry[1])[:check_first]: + enabled = True + elif isinstance(field_value, list) or isinstance(field_value, set): enabled = name in field_value if not enabled and partial_match: enabled = ConditionParser._partial_match_checker(field_value, name) + return enabled @staticmethod @@ -398,12 +422,12 @@ def prepare_testssl_output(self, test_ssl_output): elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): - self._user_configuration["KeyLengths"] = [] + self._user_configuration["KeyLengths"] = set() if not self._user_configuration.get("Certificates_SigAlg_KeyAlg"): self._user_configuration["Certificates_SigAlg_KeyAlg"] = {} # the first two tokens (after doing a space split) are the Key Algorithm and its key size element_to_add = actual_dict["finding"].split(" ")[:2] - self._user_configuration["KeyLengths"].append(element_to_add) + self._user_configuration["KeyLengths"].add(tuple(element_to_add)) cert_index = self.find_cert_index(field) if not self._user_configuration["Certificates_SigAlg_KeyAlg"].get(cert_index): self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index] = {} @@ -498,6 +522,7 @@ def _retrieve_entries(self, sheets_to_check, columns): tables = [] for sheet in sheets_to_check: columns_to_get = [] + columns_to_use = self.sheet_columns.get(sheet, {"columns": columns})["columns"] if not self._output_dict.get(sheet): self._output_dict[sheet] = {} for guideline in sheets_to_check[sheet]: @@ -506,7 +531,7 @@ def _retrieve_entries(self, sheets_to_check, columns): sheets_to_check[sheet][guideline]) tables.append(table_name) for t in tables: - for column in columns: + for column in columns_to_use: # all the columns are repeated to make easier index access later columns_to_get.append(f"{t}.{column}") @@ -517,7 +542,7 @@ def _retrieve_entries(self, sheets_to_check, columns): tables = [] self.entries = entries - def _evaluate_entries(self, sheets_to_check, columns): + def _evaluate_entries(self, sheets_to_check, original_columns): """ This function checks the entries with the same name and chooses which guideline to follow for that entry. The results can be found in the evaluated_entries field. The dictionary will have form: @@ -533,18 +558,22 @@ def _evaluate_entries(self, sheets_to_check, columns): :param columns: columns used to retrieve data from database :type columns: list """ - # A more fitting name could be current_requirement_level - guideline_index = columns.index("guidelineName") - level_index = columns.index("level") - name_index = columns.index("name") - condition_index = columns.index("condition") - # this variable is needed to get the relativa position of the condition in respect of the level - level_to_condition_index = condition_index - level_index # The entry is composed of all the columns repeated n times, with n being the number of guidelines. # The step is the number of columns. This allows easy data retrieval by doing something like: # "value_index * step * guideline_index" to retrieve data for a specific guideline - step = len(columns) for sheet in self.entries: + columns = self.sheet_columns.get(sheet, {"columns": original_columns})["columns"] + guideline_index = columns.index("guidelineName") + # A more fitting name could be current_requirement_level + level_index = columns.index("level") + name_index = columns.index("name") + condition_index = columns.index("condition") + # this variable is needed to get the relative position of the condition in respect of the level + level_to_condition_index = condition_index - level_index + # this variable is needed to get the relative position of the guideline in respect of the level + level_to_guideline_index = guideline_index - level_index + step = len(columns) + entries = self.entries[sheet] if not self.evaluated_entries.get(sheet): self.evaluated_entries[sheet] = {} @@ -557,16 +586,20 @@ def _evaluate_entries(self, sheets_to_check, columns): # list holding all the notes so that a note gets displayed only if needed notes = [] name = entry[name_index] - enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], entry) pos = level_index + field_is_enabled_in_guideline = {} while pos < len(entry): level = entry[pos] condition = entry[pos + level_to_condition_index] + guideline = entry[pos + level_to_guideline_index] + valid_condition = True # Add an empty string to the notes so that all the notes are in the same position of their entry notes.append("") if condition: + enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], + entry, condition=condition) valid_condition = self._condition_parser.run(condition, enabled) if self._condition_parser.entry_updates.get("levels"): potential_levels = self._condition_parser.entry_updates.get("levels") @@ -574,7 +607,7 @@ def _evaluate_entries(self, sheets_to_check, columns): has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: - parts = entry[condition_index].split(" ") + parts = condition.split(" ") # Tokens[1] is the logical operator notes[-1] += f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" # This is to trigger the output condition. This works because I'm assuming that "THIS" @@ -583,9 +616,13 @@ def _evaluate_entries(self, sheets_to_check, columns): if additional_notes: notes[-1] += "\nNOTE:" notes[-1] += "\n".join(additional_notes) + else: + enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], + entry) conditions.append(valid_condition) levels.append(level) + field_is_enabled_in_guideline[guideline] = enabled pos += step best_level = self.level_to_use(levels) @@ -619,7 +656,7 @@ def _evaluate_entries(self, sheets_to_check, columns): "entry": entry, "level": resulting_level, "source": source_guideline, - "enabled": enabled, + "enabled": field_is_enabled_in_guideline[source_guideline], "valid_condition": condition, "note": note } @@ -843,6 +880,10 @@ def check_key_type(self, **kwargs): self._entry_updates["levels"].append("recommended") return True + @staticmethod + def always_true(**kwargs): + return True + @property def entry_updates(self): return self._entry_updates @@ -863,7 +904,7 @@ def __init__(self): self._guidelines_versions = {} self._fill_guidelines_versions() self._aliases = load_configuration("alias_mapping", "configs/compliance/alias/") - self._default_versions = load_configuration("default_levels", "configs/compliance/alias/") + self._default_versions = load_configuration("default_versions", "configs/compliance/alias/") def list_aliases(self): print("Alias mapping:") From 13bdf6da71c07ff23a7f730edb8796e2004ec10c Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 18 Apr 2023 14:33:07 +0200 Subject: [PATCH 093/209] updated sample_dump with the new testssl version. added certificateparser.py to extract information like extensions from certificates. --- modules/compliance/compliance_base.py | 38 +++++++++++-------- .../compliance/wrappers/certificateparser.py | 26 +++++++++++++ requirements.txt | 1 + testssl_dump.json | 12 +++--- 4 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 modules/compliance/wrappers/certificateparser.py diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 7e78192..6a16332 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -6,6 +6,7 @@ from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration +from modules.compliance.wrappers.certificateparser import CertificateParser from modules.compliance.wrappers.db_reader import Database from modules.server.wrappers.testssl import Testssl from utils.database import get_standardized_level @@ -230,14 +231,12 @@ def __init__(self): self.misc_fields = load_configuration("misc_fields", "configs/compliance/") self._validator = Validator() self._condition_parser = ConditionParser(self._user_configuration) - - # This will be removed when integrating the module in the core self.test_ssl = Testssl() - self._config_class = None self._database_instance.input(["Guideline"]) self._guidelines = [name[0].upper() for name in self._database_instance.output()] self._alias_parser = AliasParser() + self._certificate_parser = CertificateParser() def level_to_use(self, levels, security: bool = True): """ @@ -404,8 +403,8 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["CertificateSignature"] = set() if not self._user_configuration.get("Hash"): self._user_configuration["Hash"] = set() - if not self._user_configuration.get("Certificates_SigAlg_KeyAlg"): - self._user_configuration["Certificates_SigAlg_KeyAlg"] = {} + if not self._user_configuration.get("CertificateData"): + self._user_configuration["CertificateData"] = {} if " " in actual_dict["finding"]: tokens = actual_dict["finding"].split(" ") sig_alg = tokens[-1] @@ -416,22 +415,22 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["CertificateSignature"].add(sig_alg) self._user_configuration["Hash"].add(hash_alg) cert_index = self.find_cert_index(field) - if not self._user_configuration["Certificates_SigAlg_KeyAlg"].get(cert_index): - self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index] = {} - self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index]["SigAlg"] = sig_alg + if not self._user_configuration["CertificateData"].get(cert_index): + self._user_configuration["CertificateData"][cert_index] = {} + self._user_configuration["CertificateData"][cert_index]["SigAlg"] = sig_alg elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = set() - if not self._user_configuration.get("Certificates_SigAlg_KeyAlg"): - self._user_configuration["Certificates_SigAlg_KeyAlg"] = {} + if not self._user_configuration.get("CertificateData"): + self._user_configuration["CertificateData"] = {} # the first two tokens (after doing a space split) are the Key Algorithm and its key size element_to_add = actual_dict["finding"].split(" ")[:2] self._user_configuration["KeyLengths"].add(tuple(element_to_add)) cert_index = self.find_cert_index(field) - if not self._user_configuration["Certificates_SigAlg_KeyAlg"].get(cert_index): - self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index] = {} - self._user_configuration["Certificates_SigAlg_KeyAlg"][cert_index]["KeyAlg"] = element_to_add[0] + if not self._user_configuration["CertificateData"].get(cert_index): + self._user_configuration["CertificateData"][cert_index] = {} + self._user_configuration["CertificateData"][cert_index]["KeyAlg"] = element_to_add[0] # In TLS 1.2 the certificate signatures and hashes are present in the signature algorithms field. elif field[-11:] == "12_sig_algs": @@ -487,11 +486,20 @@ def prepare_testssl_output(self, test_ssl_output): index = len(config_dict) config_dict[index] = actual_dict["finding"] + elif field == "cert" or re.match(r"cert ", field): + if not self._user_configuration.get("CertificateData"): + self._user_configuration["CertificateData"] = {} + cert_index = self.find_cert_index(field) + cert_data = self._certificate_parser.run(actual_dict["finding"]) + for entry in cert_data: + self._user_configuration["CertificateData"][cert_index][entry] = cert_data[entry] + elif field in self.misc_fields: if not self._user_configuration.get("Misc"): self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] + def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None action = None @@ -866,8 +874,8 @@ def check_key_type(self, **kwargs): alg = kwargs.get("data", "").lower() valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] recommend_dsa = False - for cert in self._user_configuration["Certificates_SigAlg_KeyAlg"]: - cert_data = self._user_configuration["Certificates_SigAlg_KeyAlg"][cert] + for cert in self._user_configuration["CertificateData"]: + cert_data = self._user_configuration["CertificateData"][cert] data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] if cert_data["KeyAlg"] == "DH": recommend_dsa = True diff --git a/modules/compliance/wrappers/certificateparser.py b/modules/compliance/wrappers/certificateparser.py new file mode 100644 index 0000000..4f0ec5f --- /dev/null +++ b/modules/compliance/wrappers/certificateparser.py @@ -0,0 +1,26 @@ +import OpenSSL.crypto as crypto + + +class CertificateParser: + def __init__(self): + self.certificate = "" + self._output_dict = {} + + def input(self, certificate): + self.certificate = certificate + + def run(self, certificate): + loaded_cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) + cert_sha = loaded_cert.digest("SHA256").decode("utf-8") + self._output_dict[cert_sha] = { + "extensions": {} + } + for index in range(loaded_cert.get_extension_count()): + ext = loaded_cert.get_extension(index) + ext_name = ext.get_short_name().decode("utf-8") + self._output_dict[cert_sha]["extensions"][ext_name] = ext.__str__() + + return self.output(cert_sha) + + def output(self, cert_sha): + return self._output_dict.get(cert_sha, {}).copy() diff --git a/requirements.txt b/requirements.txt index 3486ce1..1ae5eae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pdfkit==0.6.1 Jinja2==3.0.2 stix2==3.0.1 stix2-patterns==1.3.2 +pyopenssl diff --git a/testssl_dump.json b/testssl_dump.json index de68e0b..8b454e5 100644 --- a/testssl_dump.json +++ b/testssl_dump.json @@ -1,5 +1,5 @@ { - "44.236.48.31": { + "44.235.246.155": { "service": { "port": "443", "severity": "INFO", @@ -294,12 +294,12 @@ "FS_TLS12_sig_algs": { "port": "443", "severity": "LOW", - "finding": "RSA-PSS+SHA256 RSA-PSS+SHA384 RSA-PSS+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224 RSA+SHA1" + "finding": "RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224 RSA+SHA1" }, "FS_TLS13_sig_algs": { "port": "443", "severity": "INFO", - "finding": "RSA-PSS+SHA256 RSA-PSS+SHA384 RSA-PSS+SHA512" + "finding": "RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512" }, "TLS_extensions": { "port": "443", @@ -424,7 +424,7 @@ "cert_expirationStatus": { "port": "443", "severity": "OK", - "finding": "67 >= 30 days" + "finding": "32 >= 30 days" }, "cert_notBefore": { "port": "443", @@ -569,7 +569,7 @@ "HTTP_headerTime": { "port": "443", "severity": "INFO", - "finding": "1678787795" + "finding": "1681805592" }, "HSTS_time": { "port": "443", @@ -1014,7 +1014,7 @@ "scanTime": { "port": "443", "severity": "INFO", - "finding": "303" + "finding": "293" } } } \ No newline at end of file From 2a3dd4edcb4899d8a78fa65727d6976c74a624b7 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 19 Apr 2023 09:34:29 +0200 Subject: [PATCH 094/209] Improved certificateparser.py to extract more data from the certificates. Added intermediate certificates data to the user_configuration dictionary --- modules/compliance/compliance_base.py | 16 +- .../compliance/wrappers/certificateparser.py | 26 +- testssl_dump.json | 1020 ----------------- 3 files changed, 33 insertions(+), 1029 deletions(-) delete mode 100644 testssl_dump.json diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 6a16332..344f5d1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -354,7 +354,7 @@ def prepare_configuration(self, actual_configuration): def find_cert_index(field: str): if "#" in field: # the last char is a > so it gets removed - return field.split("#")[:-1] + return field.split("#")[-1].strip(">") else: return "1" @@ -490,6 +490,19 @@ def prepare_testssl_output(self, test_ssl_output): if not self._user_configuration.get("CertificateData"): self._user_configuration["CertificateData"] = {} cert_index = self.find_cert_index(field) + if not self._user_configuration["CertificateData"].get(cert_index): + self._user_configuration["CertificateData"][cert_index] = {} + cert_data = self._certificate_parser.run(actual_dict["finding"]) + for entry in cert_data: + self._user_configuration["CertificateData"][cert_index][entry] = cert_data[entry] + + elif field == "intermediate_cert" or re.match(r"intermediate_cert <#\d+>", field): + if not self._user_configuration.get("CertificateData"): + self._user_configuration["CertificateData"] = {} + cert_index = self.find_cert_index(field) + cert_index = "int_" + cert_index + if not self._user_configuration["CertificateData"].get(cert_index): + self._user_configuration["CertificateData"][cert_index] = {} cert_data = self._certificate_parser.run(actual_dict["finding"]) for entry in cert_data: self._user_configuration["CertificateData"][cert_index][entry] = cert_data[entry] @@ -499,7 +512,6 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] - def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None action = None diff --git a/modules/compliance/wrappers/certificateparser.py b/modules/compliance/wrappers/certificateparser.py index 4f0ec5f..97d412e 100644 --- a/modules/compliance/wrappers/certificateparser.py +++ b/modules/compliance/wrappers/certificateparser.py @@ -1,25 +1,37 @@ import OpenSSL.crypto as crypto +from dateutil import parser class CertificateParser: def __init__(self): - self.certificate = "" + self.certificate = None self._output_dict = {} def input(self, certificate): - self.certificate = certificate + self.certificate = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) def run(self, certificate): - loaded_cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) - cert_sha = loaded_cert.digest("SHA256").decode("utf-8") + self.input(certificate) + cert_sha = self.certificate.digest("SHA256").decode("utf-8") self._output_dict[cert_sha] = { "extensions": {} } - for index in range(loaded_cert.get_extension_count()): - ext = loaded_cert.get_extension(index) + if not isinstance(self.certificate, crypto.X509): + return {} + for index in range(self.certificate.get_extension_count()): + ext = self.certificate.get_extension(index) ext_name = ext.get_short_name().decode("utf-8") self._output_dict[cert_sha]["extensions"][ext_name] = ext.__str__() - + self._output_dict[cert_sha]["version"] = self.certificate.get_version() + self._output_dict[cert_sha]["SigAlgComplete"] = self.certificate.get_signature_algorithm().decode("utf-8") + self._output_dict[cert_sha]["issuer"] = dict(self.certificate.get_issuer().get_components()) + self._output_dict[cert_sha]["subject"] = dict(self.certificate.get_subject().get_components()) + # validity should be the difference between these two fields + not_after = self.certificate.get_notAfter().decode("utf-8") + not_before = self.certificate.get_notBefore().decode("utf-8") + self._output_dict[cert_sha]["not_after"] = not_after + self._output_dict[cert_sha]["not_before"] = not_before + self._output_dict[cert_sha]["validity"] = parser.parse(not_after) - parser.parse(not_before) return self.output(cert_sha) def output(self, cert_sha): diff --git a/testssl_dump.json b/testssl_dump.json deleted file mode 100644 index 8b454e5..0000000 --- a/testssl_dump.json +++ /dev/null @@ -1,1020 +0,0 @@ -{ - "44.235.246.155": { - "service": { - "port": "443", - "severity": "INFO", - "finding": "HTTP" - }, - "pre_128cipher": { - "port": "443", - "severity": "INFO", - "finding": "No 128 cipher limit bug" - }, - "SSLv2": { - "port": "443", - "severity": "OK", - "finding": "not offered" - }, - "SSLv3": { - "port": "443", - "severity": "OK", - "finding": "not offered" - }, - "TLS1": { - "port": "443", - "severity": "LOW", - "finding": "offered (deprecated)" - }, - "TLS1_1": { - "port": "443", - "severity": "LOW", - "finding": "offered (deprecated)" - }, - "TLS1_2": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "TLS1_3": { - "port": "443", - "severity": "OK", - "finding": "offered with final" - }, - "NPN": { - "port": "443", - "severity": "INFO", - "finding": "offered with h2, http/1.1 (advertised)" - }, - "ALPN_HTTP2": { - "port": "443", - "severity": "OK", - "finding": "h2" - }, - "ALPN": { - "port": "443", - "severity": "INFO", - "finding": "http/1.1" - }, - "cipherlist_NULL": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_aNULL": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_EXPORT": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_LOW": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_3DES_IDEA": { - "port": "443", - "severity": "INFO", - "cwe": "CWE-310", - "finding": "not offered" - }, - "cipherlist_OBSOLETED": { - "port": "443", - "severity": "LOW", - "cwe": "CWE-310", - "finding": "offered" - }, - "cipherlist_STRONG_NOFS": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "cipherlist_STRONG_FS": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "cipher_order-tls1": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "cipher-tls1_xc013": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_xc014": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - }, - "cipher-tls1_x2f": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1 x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_x35": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" - }, - "cipherorder_TLSv1": { - "port": "443", - "severity": "INFO", - "finding": "ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-SHA AES256-SHA" - }, - "cipher_order-tls1_1": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "cipher-tls1_1_xc013": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_1_xc014": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - }, - "cipher-tls1_1_x2f": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.1 x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_1_x35": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" - }, - "cipherorder_TLSv1_1": { - "port": "443", - "severity": "INFO", - "finding": "ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-SHA AES256-SHA" - }, - "cipher_order-tls1_2": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "cipher-tls1_2_xc02f": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.2 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" - }, - "cipher-tls1_2_xc030": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" - }, - "cipher-tls1_2_xcca8": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.2 xcca8 ECDHE-RSA-CHACHA20-POLY1305 ECDH 253 ChaCha20 256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" - }, - "cipher-tls1_2_xc027": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 xc027 ECDHE-RSA-AES128-SHA256 ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" - }, - "cipher-tls1_2_xc013": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_2_xc028": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 xc028 ECDHE-RSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" - }, - "cipher-tls1_2_xc014": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - }, - "cipher-tls1_2_x9c": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.2 x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" - }, - "cipher-tls1_2_x9d": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.2 x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" - }, - "cipher-tls1_2_x3c": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" - }, - "cipher-tls1_2_x3d": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" - }, - "cipher-tls1_2_x2f": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher-tls1_2_x35": { - "port": "443", - "severity": "LOW", - "finding": "TLSv1.2 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" - }, - "cipherorder_TLSv1_2": { - "port": "443", - "severity": "INFO", - "finding": "ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-CHACHA20-POLY1305 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA AES128-GCM-SHA256 AES256-GCM-SHA384 AES128-SHA256 AES256-SHA256 AES128-SHA AES256-SHA" - }, - "prioritize_chacha_TLSv1_2": { - "port": "443", - "severity": "INFO", - "finding": "false" - }, - "cipher_order-tls1_3": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "cipher-tls1_3_x1302": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.3 x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384" - }, - "cipher-tls1_3_x1303": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.3 x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256" - }, - "cipher-tls1_3_x1301": { - "port": "443", - "severity": "OK", - "finding": "TLSv1.3 x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256" - }, - "cipherorder_TLSv1_3": { - "port": "443", - "severity": "INFO", - "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256" - }, - "prioritize_chacha_TLSv1_3": { - "port": "443", - "severity": "INFO", - "finding": "false" - }, - "cipher_order": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "FS": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "FS_ciphers": { - "port": "443", - "severity": "INFO", - "finding": "TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES128-SHA" - }, - "FS_ECDHE_curves": { - "port": "443", - "severity": "OK", - "finding": "prime256v1 secp384r1 secp521r1 X25519 X448" - }, - "FS_TLS12_sig_algs": { - "port": "443", - "severity": "LOW", - "finding": "RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224 RSA+SHA1" - }, - "FS_TLS13_sig_algs": { - "port": "443", - "severity": "INFO", - "finding": "RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512" - }, - "TLS_extensions": { - "port": "443", - "severity": "INFO", - "finding": "'renegotiation info/#65281' 'server name/#0' 'EC point formats/#11' 'next protocol/#13172' 'supported versions/#43' 'key share/#51' 'supported_groups/#10' 'max fragment length/#1' 'application layer protocol negotiation/#16' 'encrypt-then-mac/#22' 'extended master secret/#23'" - }, - "TLS_session_ticket": { - "port": "443", - "severity": "INFO", - "finding": "no -- no lifetime advertised" - }, - "SSL_sessionID_support": { - "port": "443", - "severity": "INFO", - "finding": "yes" - }, - "sessionresumption_ticket": { - "port": "443", - "severity": "INFO", - "finding": "not supported" - }, - "sessionresumption_ID": { - "port": "443", - "severity": "INFO", - "finding": "not supported" - }, - "TLS_timestamp": { - "port": "443", - "severity": "INFO", - "finding": "random" - }, - "certificate_compression": { - "port": "443", - "severity": "INFO", - "finding": "none" - }, - "clientAuth": { - "port": "443", - "severity": "INFO", - "finding": "none" - }, - "cert_numbers": { - "port": "443", - "severity": "INFO", - "finding": "1" - }, - "cert_signatureAlgorithm": { - "port": "443", - "severity": "OK", - "finding": "SHA256 with RSA" - }, - "cert_keySize": { - "port": "443", - "severity": "INFO", - "finding": "RSA 2048 bits (exponent is 65537)" - }, - "cert_keyUsage": { - "port": "443", - "severity": "INFO", - "finding": "Digital Signature, Key Encipherment" - }, - "cert_extKeyUsage": { - "port": "443", - "severity": "INFO", - "finding": "TLS Web Server Authentication, TLS Web Client Authentication" - }, - "cert_serialNumber": { - "port": "443", - "severity": "INFO", - "finding": "037BE38EA85C55524F3257B593544F08832A" - }, - "cert_serialNumberLen": { - "port": "443", - "severity": "INFO", - "finding": "18" - }, - "cert_fingerprintSHA1": { - "port": "443", - "severity": "INFO", - "finding": "B5736D5276797C0BBF6AB47857C9263E9C05923F" - }, - "cert_fingerprintSHA256": { - "port": "443", - "severity": "INFO", - "finding": "A4AFB51D0E4754D22E3C2746FDFB12C0479E04851AB01E26B0782411BE93C494" - }, - "cert": { - "port": "443", - "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE-----\nMIIFHDCCBASgAwIBAgISA3vjjqhcVVJPMle1k1RPCIMqMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJSMzAeFw0yMzAyMjAwNjMyMjlaFw0yMzA1MjEwNjMyMjhaMBYxFDASBgNVBAMTC21vemlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyUrg3XvxqqCCWhNh7gBNMIAUicqbeYykKLElezIxCN5ECVOTg9Sarv41jsqSDubuEDtAP7pynHdVS2Wku8MU0LrkIBpGcIjKtwDJgD3X8VZitfgHbLf5uN38MbXfNJbmAfv0PFP3r1Yaltt0KYfkzImmj3vWT19lG2BXkNh+6VCSNhDVTsOAA1+yxuJJ7EwErAL/tA/J8llPGTqJ0tgScMJ6Be7ZRWAe5YOBQNycfjHkosMg44esE3ac4B65Rj4eDOldhbm6b04oz/2AJsx363Is0hI24oG0DSHlp7kHBVreEhxLDCFUjo6h+/oO5orRaTUgCD0yQCkieYGKmuTOnwIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS5sQu8XaFj/vyeFzJi7RTBnVXW0zAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAWBgNVHREEDzANggttb3ppbGxhLm9yZzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhm29Bl8AAAQDAEcwRQIgPrPtPLcpyd+w5N0ntsuPr9wswow3JN3+/5gsOIqcxe4CIQCCu3M6J/qBWse4GMuqVrlrQdjz9fd6+g4Ar+kS7UpyogB2AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutSAAABhm29CC0AAAQDAEcwRQIhAJ+12rPURQCkBI3tXz+NDexkXBr7+y6kXBQDdWcL8oMdAiADEIXTd11JwPa6Rwwxis2++LqwB38eci587dHpbzCCzjANBgkqhkiG9w0BAQsFAAOCAQEAnWA3G2F+zwjt4agdNXklZhCtr35yUgeLZlm4qb+p4ZLHcEdJ8czejsgLWoUoc1WkKmzi+c6zSUk5bTdX0piayV4VNARaA69XnhJ3mwIzOAhL8jP76bqPvVTHaGzm43Yh2gQiWqHfQZBV22bJCopkfmZPdil+gpiD2z2YtYw0cs1AZc4CwwG7GMZxDduurvGAUwfgdkrLcc8Dx3dZJhQi/ngIJ+BU4FWtvlnKoaalS+9LjvDZCPuMXqUxF5ofHPrruYBSmP9xoDrNL7Fs7YC7ML5PsgKXZ5uTXwSXwx4Bf6M6lkaiXCdod8/KlQmAik9hn4laYC5spJgufVj6sHKFNA==\n-----END CERTIFICATE-----" - }, - "cert_commonName": { - "port": "443", - "severity": "OK", - "finding": "mozilla.org" - }, - "cert_commonName_wo_SNI": { - "port": "443", - "severity": "INFO", - "finding": "Kubernetes Ingress Controller Fake Certificate" - }, - "cert_subjectAltName": { - "port": "443", - "severity": "INFO", - "finding": "mozilla.org" - }, - "cert_trust": { - "port": "443", - "severity": "OK", - "finding": "Ok via SAN and CN (SNI mandatory)" - }, - "cert_chain_of_trust": { - "port": "443", - "severity": "OK", - "finding": "passed." - }, - "cert_certificatePolicies_EV": { - "port": "443", - "severity": "INFO", - "finding": "no" - }, - "cert_expirationStatus": { - "port": "443", - "severity": "OK", - "finding": "32 >= 30 days" - }, - "cert_notBefore": { - "port": "443", - "severity": "INFO", - "finding": "2023-02-20 06:32" - }, - "cert_notAfter": { - "port": "443", - "severity": "OK", - "finding": "2023-05-21 06:32" - }, - "cert_extlifeSpan": { - "port": "443", - "severity": "OK", - "finding": "certificate has no extended life time according to browser forum" - }, - "cert_eTLS": { - "port": "443", - "severity": "INFO", - "finding": "not present" - }, - "cert_crlDistributionPoints": { - "port": "443", - "severity": "INFO", - "finding": "--" - }, - "cert_ocspURL": { - "port": "443", - "severity": "INFO", - "finding": "http://r3.o.lencr.org" - }, - "OCSP_stapling": { - "port": "443", - "severity": "LOW", - "finding": "not offered" - }, - "cert_mustStapleExtension": { - "port": "443", - "severity": "INFO", - "finding": "--" - }, - "DNS_CAArecord": { - "port": "443", - "severity": "OK", - "finding": "iodef=mailto:foxsec+caaiodef@mozilla.com, issue=amazon.com, issue=amazonaws.com, issue=amazontrust.com, issue=awstrust.com, issue=comodoca.com, issue=digicert.com, issue=letsencrypt.org, issue=pki.goog, issue=sectigo.com" - }, - "certificate_transparency": { - "port": "443", - "severity": "OK", - "finding": "yes (certificate extension)" - }, - "certs_countServer": { - "port": "443", - "severity": "INFO", - "finding": "3" - }, - "certs_list_ordering_problem": { - "port": "443", - "severity": "INFO", - "finding": "no" - }, - "cert_caIssuers": { - "port": "443", - "severity": "INFO", - "finding": "R3 (Let's Encrypt from US)" - }, - "intermediate_cert <#1>": { - "port": "443", - "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE-----\nMIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAwWhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cPR5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdxsxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8ZutmNHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxgZ3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6WPTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wlikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQzCkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BImlJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1OyK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90IdshCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6ZvMldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqXnLRbwHOoq7hHwg==\n-----END CERTIFICATE-----" - }, - "intermediate_cert_fingerprintSHA256 <#1>": { - "port": "443", - "severity": "INFO", - "finding": "67ADD1166B020AE61B8F5FC96813C04C2AA589960796865572A3C7E737613DFD" - }, - "intermediate_cert_notBefore <#1>": { - "port": "443", - "severity": "INFO", - "finding": "2020-09-04 00:00" - }, - "intermediate_cert_notAfter <#1>": { - "port": "443", - "severity": "OK", - "finding": "2025-09-15 16:00" - }, - "intermediate_cert_expiration <#1>": { - "port": "443", - "severity": "OK", - "finding": "ok > 40 days" - }, - "intermediate_cert_chain <#1>": { - "port": "443", - "severity": "INFO", - "finding": "R3 <-- ISRG Root X1" - }, - "intermediate_cert <#2>": { - "port": "443", - "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE-----\nMIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1owTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XCov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpLwYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+DLtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5ysR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZXmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBcSLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2qlPRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TNDTwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26ZtuMA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuGWCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9Ohe8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFCDfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n-----END CERTIFICATE-----" - }, - "intermediate_cert_fingerprintSHA256 <#2>": { - "port": "443", - "severity": "INFO", - "finding": "6D99FB265EB1C5B3744765FCBC648F3CD8E1BFFAFDC4C2F99B9D47CF7FF1C24F" - }, - "intermediate_cert_notBefore <#2>": { - "port": "443", - "severity": "INFO", - "finding": "2021-01-20 19:14" - }, - "intermediate_cert_notAfter <#2>": { - "port": "443", - "severity": "OK", - "finding": "2024-09-30 18:14" - }, - "intermediate_cert_expiration <#2>": { - "port": "443", - "severity": "OK", - "finding": "ok > 40 days" - }, - "intermediate_cert_chain <#2>": { - "port": "443", - "severity": "INFO", - "finding": "ISRG Root X1 <-- DST Root CA X3" - }, - "intermediate_cert_badOCSP": { - "port": "443", - "severity": "OK", - "finding": "intermediate certificate(s) is/are ok" - }, - "HTTP_status_code": { - "port": "443", - "severity": "INFO", - "finding": "301 Moved Permanently ('/')" - }, - "HTTP_clock_skew": { - "port": "443", - "severity": "INFO", - "finding": "0 seconds from localtime" - }, - "HTTP_headerTime": { - "port": "443", - "severity": "INFO", - "finding": "1681805592" - }, - "HSTS_time": { - "port": "443", - "severity": "MEDIUM", - "finding": "max-age too short. 0 days (=60 seconds) < 15552000 seconds" - }, - "HSTS_subdomains": { - "port": "443", - "severity": "OK", - "finding": "includes subdomains" - }, - "HSTS_preload": { - "port": "443", - "severity": "INFO", - "finding": "domain is NOT marked for preloading" - }, - "HPKP": { - "port": "443", - "severity": "INFO", - "finding": "No support for HTTP Public Key Pinning" - }, - "banner_server": { - "port": "443", - "severity": "INFO", - "finding": "No Server banner line in header, interesting!" - }, - "banner_application": { - "port": "443", - "severity": "INFO", - "finding": "No application banner found" - }, - "cookie_count": { - "port": "443", - "severity": "INFO", - "finding": "0 at '/' (30x detected, better try target URL of 30x)" - }, - "X-Frame-Options": { - "port": "443", - "severity": "OK", - "finding": "SAMEORIGIN" - }, - "Content-Security-Policy": { - "port": "443", - "severity": "OK", - "finding": "frame-ancestors 'none'" - }, - "Cache-Control": { - "port": "443", - "severity": "INFO", - "finding": "max-age=3600" - }, - "banner_reverseproxy": { - "port": "443", - "severity": "INFO", - "cwe": "CWE-200", - "finding": "--" - }, - "heartbleed": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-0160", - "cwe": "CWE-119", - "finding": "not vulnerable, no heartbeat extension" - }, - "CCS": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-0224", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "ticketbleed": { - "port": "443", - "severity": "OK", - "cve": "CVE-2016-9244", - "cwe": "CWE-200", - "finding": "no session ticket extension" - }, - "ROBOT": { - "port": "443", - "severity": "OK", - "cve": "CVE-2017-17382 CVE-2017-17427 CVE-2017-17428 CVE-2017-13098 CVE-2017-1000385 CVE-2017-13099 CVE-2016-6883 CVE-2012-5081 CVE-2017-6168", - "cwe": "CWE-203", - "finding": "not vulnerable" - }, - "secure_renego": { - "port": "443", - "severity": "OK", - "cwe": "CWE-310", - "finding": "supported" - }, - "secure_client_renego": { - "port": "443", - "severity": "OK", - "cve": "CVE-2011-1473", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "CRIME_TLS": { - "port": "443", - "severity": "OK", - "cve": "CVE-2012-4929", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "BREACH": { - "port": "443", - "severity": "OK", - "cve": "CVE-2013-3587", - "cwe": "CWE-310", - "finding": "not vulnerable, no gzip/deflate/compress/br HTTP compression - only supplied '/' tested" - }, - "POODLE_SSL": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-3566", - "cwe": "CWE-310", - "finding": "not vulnerable, no SSLv3" - }, - "fallback_SCSV": { - "port": "443", - "severity": "OK", - "finding": "supported" - }, - "SWEET32": { - "port": "443", - "severity": "OK", - "cve": "CVE-2016-2183 CVE-2016-6329", - "cwe": "CWE-327", - "finding": "not vulnerable" - }, - "FREAK": { - "port": "443", - "severity": "OK", - "cve": "CVE-2015-0204", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "DROWN": { - "port": "443", - "severity": "OK", - "cve": "CVE-2016-0800 CVE-2016-0703", - "cwe": "CWE-310", - "finding": "not vulnerable on this host and port" - }, - "DROWN_hint": { - "port": "443", - "severity": "INFO", - "cve": "CVE-2016-0800 CVE-2016-0703", - "cwe": "CWE-310", - "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=A4AFB51D0E4754D22E3C2746FDFB12C0479E04851AB01E26B0782411BE93C494" - }, - "LOGJAM": { - "port": "443", - "severity": "OK", - "cve": "CVE-2015-4000", - "cwe": "CWE-310", - "finding": "not vulnerable, no DH EXPORT ciphers," - }, - "LOGJAM-common_primes": { - "port": "443", - "severity": "OK", - "cve": "CVE-2015-4000", - "cwe": "CWE-310", - "finding": "no DH key with <= TLS 1.2" - }, - "BEAST_CBC_TLS1": { - "port": "443", - "severity": "MEDIUM", - "cve": "CVE-2011-3389", - "cwe": "CWE-20", - "finding": "ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA AES128-SHA AES256-SHA" - }, - "BEAST": { - "port": "443", - "severity": "LOW", - "cve": "CVE-2011-3389", - "cwe": "CWE-20", - "finding": "VULNERABLE -- but also supports higher protocols TLSv1.1 TLSv1.2 (likely mitigated)" - }, - "LUCKY13": { - "port": "443", - "severity": "LOW", - "cve": "CVE-2013-0169", - "cwe": "CWE-310", - "finding": "potentially vulnerable, uses TLS CBC ciphers" - }, - "winshock": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-6321", - "cwe": "CWE-94", - "finding": "not vulnerable" - }, - "RC4": { - "port": "443", - "severity": "OK", - "cve": "CVE-2013-2566 CVE-2015-2808", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "clientsimulation-android_60": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-android_70": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-android_81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-android_90": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-android_X": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-android_11": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-android_12": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-chrome_79_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-chrome_101_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-firefox_66_win81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-firefox_100_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-ie_6_xp": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-ie_8_win7": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.0 ECDHE-RSA-AES128-SHA" - }, - "clientsimulation-ie_8_xp": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-ie_11_win7": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" - }, - "clientsimulation-ie_11_win81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" - }, - "clientsimulation-ie_11_winphone81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-SHA256" - }, - "clientsimulation-ie_11_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-edge_15_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-edge_101_win10_21h2": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-safari_121_ios_122": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-safari_130_osx_10146": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-safari_154_osx_1231": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-java_7u25": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.0 ECDHE-RSA-AES128-SHA" - }, - "clientsimulation-java_8u161": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-java1102": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-java1703": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-go_1178": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-libressl_283": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-openssl_102e": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-openssl_110l": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-openssl_111d": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-openssl_303": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "clientsimulation-apple_mail_16_0": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256" - }, - "clientsimulation-thunderbird_91_9": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.3 TLS_AES_256_GCM_SHA384" - }, - "rating_spec": { - "port": "443", - "severity": "INFO", - "finding": "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" - }, - "rating_doc": { - "port": "443", - "severity": "INFO", - "finding": "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" - }, - "protocol_support_score": { - "port": "443", - "severity": "INFO", - "finding": "95" - }, - "protocol_support_score_weighted": { - "port": "443", - "severity": "INFO", - "finding": "28" - }, - "key_exchange_score": { - "port": "443", - "severity": "INFO", - "finding": "90" - }, - "key_exchange_score_weighted": { - "port": "443", - "severity": "INFO", - "finding": "27" - }, - "cipher_strength_score": { - "port": "443", - "severity": "INFO", - "finding": "90" - }, - "cipher_strength_score_weighted": { - "port": "443", - "severity": "INFO", - "finding": "36" - }, - "final_score": { - "port": "443", - "severity": "INFO", - "finding": "91" - }, - "overall_grade": { - "port": "443", - "severity": "MEDIUM", - "finding": "B" - }, - "grade_cap_reason_1": { - "port": "443", - "severity": "INFO", - "finding": "Grade capped to B. TLS 1.1 offered" - }, - "grade_cap_reason_2": { - "port": "443", - "severity": "INFO", - "finding": "Grade capped to B. TLS 1.0 offered" - }, - "grade_cap_reason_3": { - "port": "443", - "severity": "INFO", - "finding": "Grade capped to A. HSTS max-age is too short" - }, - "scanTime": { - "port": "443", - "severity": "INFO", - "finding": "293" - } - } -} \ No newline at end of file From 76723a6cbd7adfbfcaf38628b5fa650aa32eecfa Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 19 Apr 2023 16:38:32 +0200 Subject: [PATCH 095/209] Integrated the compliance module into the core. --- modules/compliance/compliance_base.py | 6 +++++- modules/core.py | 6 +++++- run.py | 4 ++-- tlsa/tlsa.py | 11 ++++++++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 344f5d1..ccf3bb2 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -277,6 +277,7 @@ def input(self, **kwargs): * *config_output* (``str``) -- The path and name of the output file * *custom_guidelines* (``dict``) -- dictionary with form: { sheet : {guideline: name: {"level":level}} """ + print(kwargs) actual_configuration = kwargs.get("actual_configuration_path") hostname = kwargs.get("hostname") self._apache = kwargs.get("apache", True) @@ -297,7 +298,7 @@ def input(self, **kwargs): # this is temporary with open("testssl_dump.json", 'r') as f: test_ssl_output = json.load(f) - self.prepare_testssl_output(test_ssl_output) + self.prepare_testssl_output(test_ssl_output) if output_file and self._validator.string(output_file): if self._apache: self._config_class = ApacheConfiguration() @@ -887,6 +888,8 @@ def check_key_type(self, **kwargs): valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] recommend_dsa = False for cert in self._user_configuration["CertificateData"]: + if cert.startswith("int"): + continue cert_data = self._user_configuration["CertificateData"][cert] data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] if cert_data["KeyAlg"] == "DH": @@ -894,6 +897,7 @@ def check_key_type(self, **kwargs): if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ f"with an algorithm that isn't consistent with the public key" + if note: self._entry_updates["notes"].append(note) if recommend_dsa: diff --git a/modules/core.py b/modules/core.py index 6526db1..5093658 100644 --- a/modules/core.py +++ b/modules/core.py @@ -461,7 +461,11 @@ def __run_analysis( for name, module in loaded_modules.items(): if hostname_or_path_type not in loaded_arguments[name]: loaded_arguments[name][hostname_or_path_type] = hostname_or_path - args = loaded_arguments[name] + args={} + if self.__input_dict['compliance_args'] and name in self.__input_dict['compliance_args']: # if we are not checking compliance + args = self.__input_dict['compliance_args'][name] + + args.update(loaded_arguments[name]) if type_of_analysis != self.Analysis.APK: # server analysis args["port"] = port # set the port self.__logging.info(f"{Color.CBEIGE}Running {name} module...") diff --git a/run.py b/run.py index 6a54fa2..8cfc4bb 100644 --- a/run.py +++ b/run.py @@ -13,7 +13,7 @@ def __call__(self, parser, namespace, values, option_string=None): if not isinstance(namespace.__getattribute__(self.dest), dict): namespace.__setattr__(self.dest, {}) dictionary = namespace.__getattribute__(self.dest) - dictionary[option_string.strip("-")] = values + dictionary[option_string.strip("-")] = values[0] if __name__ == "__main__": @@ -164,7 +164,7 @@ def __call__(self, parser, namespace, values, option_string=None): "--apache", type=bool, nargs=1, - action="append", + action=ComplianceAction, default=True, dest="compliance_args", help="Default to True. If True the output configuration will have apache syntax, if false nginx will be used." diff --git a/tlsa/tlsa.py b/tlsa/tlsa.py index e66784e..11db16c 100644 --- a/tlsa/tlsa.py +++ b/tlsa/tlsa.py @@ -103,7 +103,16 @@ def __start_analysis(self, args): f"default{'_android.json' if args.apk else '_server.json'}" ) config_or_modules = args.configuration - + if args.compliance_args: + assert "guideline" in args.compliance_args, "Guideline Argument Missing!" + all_args=args.compliance_args.copy() + comp_one_or_many="compare_one" if "," not in all_args['guideline'] else "compare_many" + gen_one_or_many="generate_one" if "," not in all_args['guideline'] else "generate_many" + args.compliance_args={ + comp_one_or_many:all_args, + gen_one_or_many:all_args + } + if args.apply_fix or args.file: # checks for openssl and ignore-openssl flag if not args.ignore_openssl and not args.openssl: From c0e881d038397872a658f8a7a5a79069692583ae Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 24 Apr 2023 10:21:34 +0200 Subject: [PATCH 096/209] Fixed small issue with compliance module and updated testssl version in dependencies.json. --- dependencies.json | 6 +++--- modules/compliance/compare_one.py | 4 ++-- modules/compliance/compliance_base.py | 3 ++- modules/server/wrappers/testssl.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/dependencies.json b/dependencies.json index 2de91ac..644c22d 100644 --- a/dependencies.json +++ b/dependencies.json @@ -20,8 +20,8 @@ "url": "https://github.com/SUPERAndroidAnalyzer/super/releases/download/0.5.1/super-analyzer_0.5.1_ubuntu_amd64.deb" }, { - "type": "zip", - "url": "https://github.com/drwetter/testssl.sh/archive/3.0.4.zip" + "type": "git", + "url": "https://github.com/drwetter/testssl.sh.git" }, { "type": "apt", @@ -49,4 +49,4 @@ "path": "TLS-Scanner" } -] \ No newline at end of file +] diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 507c6b7..8d55f66 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -13,9 +13,9 @@ def _worker(self, sheets_to_check): if not self._user_configuration: raise ValueError("No configuration provided") for sheet in sheets_to_check: - columns = ["name", "level", "condition", "guidelineName"] + columns_orig = ["name", "level", "condition", "guidelineName"] # If the sheet isn't in the dictionary then I can use the default value - columns = self.sheet_columns.get(sheet, {"columns": columns})["columns"] + columns = self.sheet_columns.get(sheet, {"columns": columns_orig})["columns"] name_index = columns.index("name") name_columns = self.sheet_columns.get(sheet, {}).get("name_columns", [name_index]) level_index = columns.index("level") diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index ccf3bb2..b2bb577 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -322,7 +322,7 @@ def _worker(self, sheets_to_check): def run(self, **kwargs): self.input(**kwargs) - guidelines_string = kwargs.get("guidelines_to_check") + guidelines_string = kwargs.get("guideline") self._validator.string(guidelines_string) guidelines_list = guidelines_string.split(",") if "," in guidelines_string else [guidelines_string] sheets_to_check = self._alias_parser.get_sheets_to_check(guidelines_list) @@ -331,6 +331,7 @@ def run(self, **kwargs): return self.output() def output(self): + print(self._output_dict) return self._output_dict.copy() def prepare_configuration(self, actual_configuration): diff --git a/modules/server/wrappers/testssl.py b/modules/server/wrappers/testssl.py index cb976c0..7a67832 100644 --- a/modules/server/wrappers/testssl.py +++ b/modules/server/wrappers/testssl.py @@ -68,7 +68,7 @@ def __init__(self): """ Loads testssl variables. """ - self.__testssl = f"dependencies{sep}3.0.4{sep}testssl.sh-3.0.4{sep}testssl.sh" + self.__testssl = f"dependencies{sep}testssl.sh{sep}testssl.sh" self.__input_dict = {} def input(self, **kwargs): From ffcb37cb9a17c74e7b53787f90bc6f210cbc7406 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 27 Apr 2023 16:07:53 +0200 Subject: [PATCH 097/209] Updated database with Certificate and CertificateExtensions as two separate sheets. Now pandas only reads needed sheets. Fixed issues with database_filler and schema_creator --- DatabaseFiller/database_filler.py | 16 +- DatabaseFiller/guidelines.xlsx | Bin 119174 -> 101106 bytes DatabaseFiller/output.prisma | 455 ++++++++++-------- DatabaseFiller/requirements.db | Bin 1101824 -> 1138688 bytes DatabaseFiller/schema_creator.py | 2 +- .../schema_generator/template.prisma | 26 +- DatabaseFiller/utils/configs.py | 7 +- DatabaseFiller/utils/filler_utils.py | 14 +- configs/compliance/requirements.db | Bin 1101824 -> 1142784 bytes 9 files changed, 294 insertions(+), 226 deletions(-) diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py index 6f56a83..f89bee3 100755 --- a/DatabaseFiller/database_filler.py +++ b/DatabaseFiller/database_filler.py @@ -8,7 +8,8 @@ get_version_name_for_database, get_guideline_name_for_database, is_double_guideline, get_first_col_for_guideline, \ get_column -dataframe = pd.read_excel("guidelines.xlsx", header=[0, 1], sheet_name=None, converters=converters, dtype=str) +dataframe = pd.read_excel("guidelines.xlsx", header=[0, 1], sheet_name=list(sheets_mapping.keys()), + converters=converters, dtype=str) sheet_with_extra_table = { "TLS extensions": ("applies to version", "TlsVersionExtension") @@ -226,14 +227,17 @@ def fill_extra_table(sheet_name: str) -> bool: elif pd.notna(header[1]) and \ get_first_col_for_guideline(guidelines_dataframe, header[0]) != header[1]: - # update all the lists of the same guideline + # update all the lists of the same guideline with the condition for t_name in values_dict: - if t_name.startswith(sheet_mapped + header[0]): - values_dict[t_name][row[0]].append(content) + guideline_db_name = get_guideline_name_for_database(header[0]) + # this is needed only for the case of KeyLengthsBSI and KeyLengths BSI (from ...) + has_valid_underscore = "_" in guideline_db_name and "_" in t_name + if t_name.startswith(sheet_mapped + guideline_db_name): + if "_" not in t_name or has_valid_underscore: + values_dict[t_name][row[0]].append(content) if is_double_guideline(header[0]): for other_guideline in header[0].split("+")[1:]: - other_name = get_guideline_name_for_database( - other_guideline) + other_name = get_guideline_name_for_database(other_guideline) other_table = sheet_mapped + other_name + version_name values_dict[other_table] = values_dict[table_name] diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index 132e613384f428528655ec368deed94763484ed9..bbe82820c52c7378851c0ba59fd62e947fa143c8 100644 GIT binary patch literal 101106 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") - critical String @default("no") + level String? // put level as Nullable (it would be considered not mentioned) condition String? @default("") @@id([id, name]) } -model CertificateBSI { +model CertificateANSSI { id Int guideline Guideline @relation(fields: [guidelineName], references: [name]) guidelineName String Certificate Certificate @relation(fields: [name], references: [name]) name String - level String @default("") - critical String @default("no") + level String? // put level as Nullable (it would be considered not mentioned) condition String? @default("") @@id([id, name]) } -model CertificateANSSI { +model CertificateMOZILLA { id Int guideline Guideline @relation(fields: [guidelineName], references: [name]) guidelineName String Certificate Certificate @relation(fields: [name], references: [name]) name String - level String @default("") - critical String @default("no") + level String? // put level as Nullable (it would be considered not mentioned) condition String? @default("") @@id([id, name]) } -model CertificateMOZILLA { - id Int - guideline Guideline @relation(fields: [guidelineName], references: [name]) - guidelineName String - Certificate Certificate @relation(fields: [name], references: [name]) - name String - level String @default("") - critical String @default("no") - condition String? @default("") +model CertificateExtensionsNIST { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateExtensions CertificateExtensions @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateExtensionsBSI { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateExtensions CertificateExtensions @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateExtensionsANSSI { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateExtensions CertificateExtensions @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") + + @@id([id, name]) +} + +model CertificateExtensionsMOZILLA { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + CertificateExtensions CertificateExtensions @relation(fields: [name], references: [name]) + name String + level String @default("") + critical String @default("no") + condition String? @default("") @@id([id, name]) } diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index beb487069cf9d8bcc82610f0e87853119f54d934..eb0af2c306bc8bc6911b2b7f6ab4b739f1535dfb 100644 GIT binary patch delta 48486 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 v83!1BD8CQo|FGRK^r3v`VQ`meJGzk=_~h~UB^ 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 diff --git a/DatabaseFiller/schema_creator.py b/DatabaseFiller/schema_creator.py index b592bb3..7ccdc64 100644 --- a/DatabaseFiller/schema_creator.py +++ b/DatabaseFiller/schema_creator.py @@ -82,5 +82,5 @@ def generate_template(df: Dict[str, pd.DataFrame]): if __name__ == "__main__": - dataframe = pd.read_excel("./guidelines.xlsx", header=[0, 1], sheet_name=None) + dataframe = pd.read_excel("./guidelines.xlsx", header=[0, 1], sheet_name=list(sheets_mapping.keys())) generate_template(dataframe) diff --git a/DatabaseFiller/schema_generator/template.prisma b/DatabaseFiller/schema_generator/template.prisma index e3da6ac..a47e60c 100644 --- a/DatabaseFiller/schema_generator/template.prisma +++ b/DatabaseFiller/schema_generator/template.prisma @@ -11,11 +11,11 @@ model TlsVersion { } model Guideline { - name String @id - updated_at String - KeyLengths7WJsEz KeyLengths7WJsEz[] - Sheet7WJsEz Sheet7WJsEz[] - Certificate7WJsEz Certificate7WJsEz[] + name String @id + updated_at String + KeyLengths7WJsEz KeyLengths7WJsEz[] + Sheet7WJsEz Sheet7WJsEz[] + CertificateExtensions7WJsEz CertificateExtensions7WJsEz[] } // GENERAL DATA @@ -59,6 +59,10 @@ model Certificate { name String @id } +model CertificateExtensions { + name String @id +} + model Misc { name String @id } @@ -91,11 +95,11 @@ model TlsVersionExtension { // TEMPLATE DATA model Sheet { - name String @id - iana_code String - version String - Sheet7WJsEz Sheet7WJsEz[] - Certificate7WJsEz Certificate7WJsEz[] + name String @id + iana_code String + version String + Sheet7WJsEz Sheet7WJsEz[] + CertificateExtensions7WJsEz CertificateExtensions7WJsEz[] } model Sheet7WJsEz { @@ -123,7 +127,7 @@ model KeyLengths7WJsEz { @@id([id, name, length]) } -model Certificate7WJsEz { +model CertificateExtensions7WJsEz { id Int guideline Guideline @relation(fields: [guidelineName], references: [name]) guidelineName String diff --git a/DatabaseFiller/utils/configs.py b/DatabaseFiller/utils/configs.py index 5f4fb6e..1fa6c72 100644 --- a/DatabaseFiller/utils/configs.py +++ b/DatabaseFiller/utils/configs.py @@ -1,13 +1,13 @@ # schema creator configs TEMPLATE_FILE = "schema_generator/template.prisma" -GUIDELINE_BLOCKS = 14 +GUIDELINE_BLOCKS = 15 RANDOM_STRING = "7WJsEz" # If the sheet has vertically merged cells in the name column add it here has_merged_names = ["Key lengths"] # list of sheet_names that need a different template, order is important -different_templates = ["KeyLengths", "Certificate"] +different_templates = ["KeyLengths", "CertificateExtensions"] # The syntax for this is: Sheet: list of keys # it is assumed that a field with the same name of the key was added using the additional_fields dict @@ -40,7 +40,8 @@ "Hash Algorithm": "Hash", "Certificate Signature": "CertificateSignature", "Key lengths": "KeyLengths", - "Certificate": "Certificate", # at the moment the certificate table doesn't exist + "Certificate": "Certificate", + "Certificate Extensions": "CertificateExtensions", "Misc": "Misc" } diff --git a/DatabaseFiller/utils/filler_utils.py b/DatabaseFiller/utils/filler_utils.py index 6de8a5f..7286cb7 100644 --- a/DatabaseFiller/utils/filler_utils.py +++ b/DatabaseFiller/utils/filler_utils.py @@ -35,7 +35,7 @@ def get_requirements_columns(requirements_df: pd.DataFrame, sheet_name: str) -> if not result_dict.get(col[0]): result_dict[col[0]] = [] val = row_dict[col] - if val[0] in levels_mapping.values(): + if get_standardized_level(val[0]) in levels_mapping.values(): result_dict[col[0]].append(col[1]) for val in also_add: if not result_dict.get(val): @@ -130,3 +130,15 @@ def get_first_col_for_guideline(df: pd.DataFrame, guideline: str): def get_column(df: pd.DataFrame, index: int): return df.iloc[:, index] + +def get_standardized_level(level): + """ + Takes a level in input and returns it after removing °,* and trailing spaces + :param level: + :type level: str + :return: + """ + if isinstance(level, str): + return level.replace("*", "").replace("°", "").strip() + else: + return "" \ No newline at end of file diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index beb487069cf9d8bcc82610f0e87853119f54d934..5fa3935d068419a97a516c0d24f502e22bac55fe 100644 GIT binary patch delta 43349 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 DzCM5J From 85bcff2a2b66865abc5af8d57d9b5532af2605be Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 27 Apr 2023 16:08:58 +0200 Subject: [PATCH 098/209] Improved certificateparser.py. Fixed issue with check_this. Improved testssl output data extraction. --- modules/compliance/compare_one.py | 2 +- modules/compliance/compliance_base.py | 130 +++++++++++------- .../compliance/wrappers/certificateparser.py | 13 +- 3 files changed, 92 insertions(+), 53 deletions(-) diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 8d55f66..6eed1b5 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -52,7 +52,7 @@ def _worker(self, sheets_to_check): name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) note = "" - if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: + if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: parts = entry[condition_index].split(" ") # Tokens[1] is the logical operator note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index b2bb577..f50bd2d 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -3,6 +3,7 @@ import json import re from pathlib import Path +import pprint from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration @@ -72,18 +73,10 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match :return: """ field_value = user_configuration.get(config_field, None) - check_first = 0 + check_first = None if condition: - conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) - for condition in conditions: - condition = condition.strip() - if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: - check_first = condition.split(" ")[1] - if check_first.isdecimal(): - check_first = int(check_first) - # If the condition value isn't a number it doesn't become an int and the validator gives the error. - Validator().int(check_first) + check_first = ConditionParser.get_check_first(condition) enabled = False if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): @@ -114,6 +107,8 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = name in field_value if not enabled and partial_match: enabled = ConditionParser._partial_match_checker(field_value, name) + if not enabled and check_first: + enabled = name[:check_first] in field_value return enabled @@ -124,6 +119,20 @@ def _prepare_to_search(field, to_search): new_to_search = "TLS " + to_search.strip() return new_to_search + @staticmethod + def get_check_first(condition: str): + check_first = None + conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) + for condition in conditions: + condition = condition.strip() + if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: + check_first = condition.split(" ")[1] + if check_first.isdecimal(): + check_first = int(check_first) + # If the condition value isn't a number it doesn't become an int and the validator gives the error. + Validator().int(check_first) + return check_first + def _closing_parenthesis_index(self, start): count = 0 for i, c in enumerate(self.expression[start:]): @@ -146,8 +155,9 @@ def _solve(self, start, finish): to_solve = to_solve.replace(to_replace, replacement) tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) tokens = [token.strip() for token in tokens] - for token in tokens: - to_solve = to_solve.replace(token, str(self._evaluate_condition(token))) + for i, token in enumerate(tokens): + next_token = tokens[i+1] if i < len(tokens)-1 else None + to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) tokens = [token for token in tokens if token] while len(tokens) >= 3: @@ -160,7 +170,7 @@ def _solve(self, start, finish): tokens.insert(0, str(result)) return tokens[0] - def _evaluate_condition(self, condition): + def _evaluate_condition(self, condition, next_condition = None): """ Evaluates a condition and returns if it is True or False :param condition: condition to evaluate @@ -188,7 +198,8 @@ def _evaluate_condition(self, condition): args = { "data": to_search, "enabled": self._enabled, - "tokens": tokens[1:] + "tokens": tokens[1:], + "next_condition": next_condition } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) else: @@ -237,6 +248,8 @@ def __init__(self): self._guidelines = [name[0].upper() for name in self._database_instance.output()] self._alias_parser = AliasParser() self._certificate_parser = CertificateParser() + self._cert_sig_algs = [el[0] for el in self._database_instance.run(tables=["CertificateSignature"], + columns=["name"])] def level_to_use(self, levels, security: bool = True): """ @@ -277,7 +290,6 @@ def input(self, **kwargs): * *config_output* (``str``) -- The path and name of the output file * *custom_guidelines* (``dict``) -- dictionary with form: { sheet : {guideline: name: {"level":level}} """ - print(kwargs) actual_configuration = kwargs.get("actual_configuration_path") hostname = kwargs.get("hostname") self._apache = kwargs.get("apache", True) @@ -322,7 +334,7 @@ def _worker(self, sheets_to_check): def run(self, **kwargs): self.input(**kwargs) - guidelines_string = kwargs.get("guideline") + guidelines_string = kwargs.get("guidelines") self._validator.string(guidelines_string) guidelines_list = guidelines_string.split(",") if "," in guidelines_string else [guidelines_string] sheets_to_check = self._alias_parser.get_sheets_to_check(guidelines_list) @@ -331,9 +343,29 @@ def run(self, **kwargs): return self.output() def output(self): - print(self._output_dict) + # pprint.pprint(self._output_dict) return self._output_dict.copy() + def _add_certificate_signature_algorithm(self, alg): + """ + Adds the passed algorithm to the CertificateSignature field after parsing it. + :param alg: the algorithm to add + :return: a list containing the parsed algorithms + """ + to_return = [] + if not self._user_configuration.get("CertificateSignature"): + self._user_configuration["CertificateSignature"] = set() + if isinstance(alg, str): + alg = [alg] + + for sig_alg in alg: + sig_alg = sig_alg.replace("RSASSA-PSS", "RSA") + if sig_alg.lower() not in self._cert_sig_algs: + sig_alg = "anonymous" + to_return.append(sig_alg) + self._user_configuration["CertificateSignature"].add(sig_alg.lower()) + return to_return + def prepare_configuration(self, actual_configuration): for field in actual_configuration: new_field = actual_configuration[field] @@ -401,12 +433,10 @@ def prepare_testssl_output(self, test_ssl_output): # From the certificate signature algorithm is possible to extract both CertificateSignature and Hash elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): - if not self._user_configuration.get("CertificateSignature"): - self._user_configuration["CertificateSignature"] = set() if not self._user_configuration.get("Hash"): self._user_configuration["Hash"] = set() - if not self._user_configuration.get("CertificateData"): - self._user_configuration["CertificateData"] = {} + if not self._user_configuration.get("Certificate"): + self._user_configuration["Certificate"] = {} if " " in actual_dict["finding"]: tokens = actual_dict["finding"].split(" ") sig_alg = tokens[-1] @@ -414,30 +444,28 @@ def prepare_testssl_output(self, test_ssl_output): # sometimes the hashing algorithm comes first, so they must be switched if sig_alg.startswith("SHA"): sig_alg, hash_alg = hash_alg, sig_alg - self._user_configuration["CertificateSignature"].add(sig_alg) + sig_alg = self._add_certificate_signature_algorithm(sig_alg)[0] self._user_configuration["Hash"].add(hash_alg) cert_index = self.find_cert_index(field) - if not self._user_configuration["CertificateData"].get(cert_index): - self._user_configuration["CertificateData"][cert_index] = {} - self._user_configuration["CertificateData"][cert_index]["SigAlg"] = sig_alg + if not self._user_configuration["Certificate"].get(cert_index): + self._user_configuration["Certificate"][cert_index] = {} + self._user_configuration["Certificate"][cert_index]["SigAlg"] = sig_alg elif field.startswith("cert_keySize"): if not self._user_configuration.get("KeyLengths"): self._user_configuration["KeyLengths"] = set() - if not self._user_configuration.get("CertificateData"): - self._user_configuration["CertificateData"] = {} + if not self._user_configuration.get("Certificate"): + self._user_configuration["Certificate"] = {} # the first two tokens (after doing a space split) are the Key Algorithm and its key size element_to_add = actual_dict["finding"].split(" ")[:2] self._user_configuration["KeyLengths"].add(tuple(element_to_add)) cert_index = self.find_cert_index(field) - if not self._user_configuration["CertificateData"].get(cert_index): - self._user_configuration["CertificateData"][cert_index] = {} - self._user_configuration["CertificateData"][cert_index]["KeyAlg"] = element_to_add[0] + if not self._user_configuration["Certificate"].get(cert_index): + self._user_configuration["Certificate"][cert_index] = {} + self._user_configuration["Certificate"][cert_index]["KeyAlg"] = element_to_add[0] # In TLS 1.2 the certificate signatures and hashes are present in the signature algorithms field. elif field[-11:] == "12_sig_algs": - if not self._user_configuration.get("CertificateSignature"): - self._user_configuration["CertificateSignature"] = set() if not self._user_configuration.get("Hash"): self._user_configuration["Hash"] = set() finding = actual_dict["finding"] @@ -449,9 +477,9 @@ def prepare_testssl_output(self, test_ssl_output): if "-" not in el and "+" in el: # The entries are SigAlg+HashAlg tokens = el.split("+") - signatures.append(tokens[0]) + signatures.append(tokens[0].replace("RSASSA-PSS", "RSA").lower()) hashes.append(tokens[1]) - self._user_configuration["CertificateSignature"].update(signatures) + self._add_certificate_signature_algorithm(signatures) self._user_configuration["Hash"].update(hashes) # From TLS 1.3 the signature algorithms are different from the previous versions. @@ -489,25 +517,25 @@ def prepare_testssl_output(self, test_ssl_output): config_dict[index] = actual_dict["finding"] elif field == "cert" or re.match(r"cert ", field): - if not self._user_configuration.get("CertificateData"): - self._user_configuration["CertificateData"] = {} + if not self._user_configuration.get("Certificate"): + self._user_configuration["Certificate"] = {} cert_index = self.find_cert_index(field) - if not self._user_configuration["CertificateData"].get(cert_index): - self._user_configuration["CertificateData"][cert_index] = {} + if not self._user_configuration["Certificate"].get(cert_index): + self._user_configuration["Certificate"][cert_index] = {} cert_data = self._certificate_parser.run(actual_dict["finding"]) for entry in cert_data: - self._user_configuration["CertificateData"][cert_index][entry] = cert_data[entry] + self._user_configuration["Certificate"][cert_index][entry] = cert_data[entry] elif field == "intermediate_cert" or re.match(r"intermediate_cert <#\d+>", field): - if not self._user_configuration.get("CertificateData"): - self._user_configuration["CertificateData"] = {} + if not self._user_configuration.get("Certificate"): + self._user_configuration["Certificate"] = {} cert_index = self.find_cert_index(field) cert_index = "int_" + cert_index - if not self._user_configuration["CertificateData"].get(cert_index): - self._user_configuration["CertificateData"][cert_index] = {} + if not self._user_configuration["Certificate"].get(cert_index): + self._user_configuration["Certificate"][cert_index] = {} cert_data = self._certificate_parser.run(actual_dict["finding"]) for entry in cert_data: - self._user_configuration["CertificateData"][cert_index][entry] = cert_data[entry] + self._user_configuration["Certificate"][cert_index][entry] = cert_data[entry] elif field in self.misc_fields: if not self._user_configuration.get("Misc"): @@ -867,7 +895,15 @@ def check_this(self, **kwargs): * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not """ enabled = kwargs.get("enabled", False) - self._entry_updates["has_alternative"] = True + second_condition = kwargs.get("next_condition", " ") + field, name = second_condition.split(" ") + # only the first two fields of the entry matter, and entry is only needed for key lengths + entry_data = name.split(",") if "," in name else (None, None) + second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, + partial_match=True) + print(enabled, second_enabled, name) + enabled = second_enabled or enabled + self._entry_updates["has_alternative"] = enabled return enabled def add_notes(self, **kwargs): @@ -888,10 +924,10 @@ def check_key_type(self, **kwargs): alg = kwargs.get("data", "").lower() valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] recommend_dsa = False - for cert in self._user_configuration["CertificateData"]: + for cert in self._user_configuration["Certificate"]: if cert.startswith("int"): continue - cert_data = self._user_configuration["CertificateData"][cert] + cert_data = self._user_configuration["Certificate"][cert] data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] if cert_data["KeyAlg"] == "DH": recommend_dsa = True diff --git a/modules/compliance/wrappers/certificateparser.py b/modules/compliance/wrappers/certificateparser.py index 97d412e..3242672 100644 --- a/modules/compliance/wrappers/certificateparser.py +++ b/modules/compliance/wrappers/certificateparser.py @@ -14,18 +14,21 @@ def run(self, certificate): self.input(certificate) cert_sha = self.certificate.digest("SHA256").decode("utf-8") self._output_dict[cert_sha] = { - "extensions": {} + "Extensions": {} } if not isinstance(self.certificate, crypto.X509): return {} for index in range(self.certificate.get_extension_count()): ext = self.certificate.get_extension(index) ext_name = ext.get_short_name().decode("utf-8") - self._output_dict[cert_sha]["extensions"][ext_name] = ext.__str__() - self._output_dict[cert_sha]["version"] = self.certificate.get_version() + self._output_dict[cert_sha]["Extensions"][ext_name] = ext.__str__() + self._output_dict[cert_sha]["X.509 version"] = self.certificate.get_version() self._output_dict[cert_sha]["SigAlgComplete"] = self.certificate.get_signature_algorithm().decode("utf-8") - self._output_dict[cert_sha]["issuer"] = dict(self.certificate.get_issuer().get_components()) - self._output_dict[cert_sha]["subject"] = dict(self.certificate.get_subject().get_components()) + # this list comprehension takes every tuple, decodes its elements and puts the decoded pair in a new list + entries = [[el.decode("utf-8") for el in entry] for entry in self.certificate.get_issuer().get_components()] + self._output_dict[cert_sha]["Issuer Distinguished Name"] = dict(entries) + entries = [[el.decode("utf-8") for el in entry] for entry in self.certificate.get_subject().get_components()] + self._output_dict[cert_sha]["Subject Distinguished Name"] = dict(entries) # validity should be the difference between these two fields not_after = self.certificate.get_notAfter().decode("utf-8") not_before = self.certificate.get_notBefore().decode("utf-8") From 97805229b7550c3d9deeca1c81a91a46e41cc8f5 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 28 Apr 2023 16:18:38 +0200 Subject: [PATCH 099/209] Improved prepare_testssl_output function readability and performances. Added new sheets to default_versions.json. Updated database. --- DatabaseFiller/guidelines.xlsx | Bin 101106 -> 101364 bytes DatabaseFiller/output.prisma | 14 ++ DatabaseFiller/requirements.db | Bin 1138688 -> 1146880 bytes .../compliance/alias/default_versions.json | 12 ++ .../compliance/condition_instructions.json | 4 +- .../compliance/generate/user_conf_types.json | 11 +- configs/compliance/requirements.db | Bin 1142784 -> 1146880 bytes modules/compliance/compliance_base.py | 148 +++++++++++------- 8 files changed, 125 insertions(+), 64 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index bbe82820c52c7378851c0ba59fd62e947fa143c8..e98915202e51789e4b317006a357a145407ce0d1 100644 GIT binary patch delta 19073 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 delta 2729 zcmb_deQX=$8NZL$=dbttoSQmP+a!I{fJoXbPMHccgA$X_VxigA+#G)w4Okl7O6my#ztA!oF@Jt6@oF;svXim+6U`CCB`7hv@%w*dCy(r z#PRyis#$t1-TS`3kN3Iv_uTaK-s$=KPi)@6y~=TXJGb^5|JK0m0{7}mhxP-1=h<^` zU_|1637^16=i>Yu@Z``)kMvFzx4(V1elU0@a*BfmxCrOr)Y+bqCxLVZgp`xX(#;Sd zLf28Xq!)F&#zevSVUlhRu)eWOzq7EF?}*HDZ~)rK$D{_7cv#(uR=z<(az^3L=wyoKvOh2U@c z37_X5aF&iMn;hkFh&c5rkp}*_p5b>s!@)mn-!nv+n-`-> z;KOz6?eEXXXzMR~n>>EdQ21-Bf{PwMh7N9V1`xFHKojV14bW06$f*ONfsavk?Ug02 z9z%k^?`b0VM}y#_8x#C%9f5T15hVDhTR%wNhTtdv4auTruo=SId4d>T{LGptFZrUc zLx>NwQn+=4T-%z6)es}!-GotAG0=)}>Yl97EZdW)drtq|tcrI5FM+4ZwKBNKa19b+ z&uOR`6J5nbS~1sE+nZJKux+044b8?As}vhsDUR3c1V;degd>O}gd^k(-&D(f6v zid~GIl6B0+=VkcHog~O7QoUc>L^A38em`JSrdBW}C$(}>E2fyGmyeV*hS8~N%$hK& zr6L>Gm{u+rxX$!4)6~KQ``lQD_1P1+rD?y#>voYPA(h9a=*7CSsKEmu;sdZWwJI4H}u}q$o)k*EkeWJuCx_Wy= zXL~)`6PuY5g5gBE_p7~r)x1uU@;k{*l};3s{ zFNG4fC3blSe0Xrz&~A2AGcDaHPgX74zYW9e_JPLaF#B`_(?Q^A4nBl8;bnLlFWA?? z`5w>!PyBo3yaH4LvC)LZUJ_pE!gu(!6XGOvTX>z8EL`G3Tpt%Hg!YC40Vntq@s>c2 z>k}@?QTTz|t`4+@0igsYBxk$%TH z^Co@YeXpB#@J_Ug<`u7F5=e3I4ty6LffQQa3E^Et;yY41IO1fJ^p0LJmC5JR4cB*P z44Yv#Y^;P<+;f*8g*(#D^GV89K0$9J>7K|+SX`czx6?E9C>^7n(IqtY^Qb#pqQ`h;rbi0#iF6`9Gb#CrIF#kPr@Wg|HoOe^ zVe*1BzzlOWXqYqIPa;NNw^-T0?r=SCOtmci2o`vdWihFyA3{H`7MoOSUIWt0g;Ete z(16FGRw|}ZF?H3}2Db;WqwVU7bIzk>`XiWW6GGv{{k`#-wD0VEmhW_)Flgc-Y+bqP zcu6mO>Orl>M$KBqHjbETWunINwpuQ#X3=7Oxd%t{{cPtDGqo>PHEa`jfPz{oR57&J z|Hf;6E72{h>UFEplsj(FXOzxcId~1e0S1i1w%B6q$FVQOIC)-vOnykd6)PvoZud9o z6TCbe4DxO19dS;&FQ0b2vN`A8rEz!hak_D5x6BQ4@O?Q<=OU5tS?Q^8mRQnZVL!Q# z8x(rnZS(Z8Fn-b87k)+~qKkL>s^DDOKo87arexYBhva3L8;SFZ%U5KPohCzZ#U&?j z_9O%1l6eK!XJvvb6$Mx3S67slmBqCcSdU?`Wq2;9u(>lbQBIS~fIK;zJDF1?S2_gO zt}D3Yinnn(rkuPIL+Q)-G&dCUq;sG=OFY;#EN@+*nN@B$=aLF6C)f70KA$Ed-Z_-` PDp5{GQ|l+uAD-qvgQzn& diff --git a/configs/compliance/alias/default_versions.json b/configs/compliance/alias/default_versions.json index de60f8e..3ab1fc9 100644 --- a/configs/compliance/alias/default_versions.json +++ b/configs/compliance/alias/default_versions.json @@ -54,6 +54,18 @@ "NIST": "", "BSI": "" }, + "Certificate": { + "NIST": "", + "BSI": "", + "AGID": "", + "MOZILLA": "" + }, + "CertificateExtensions": { + "NIST": "", + "BSI": "", + "AGID": "", + "MOZILLA": "" + }, "Misc": { "NIST": "", "BSI": "", diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index cf7afde..e9f1b51 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -5,10 +5,12 @@ "TLS": "Protocol", "CERTSIG": "CertificateSignature", "YEAR": "FUNCTION check_year", + "YEARS": "FUNCTION check_year_in_days", "VLP": "FUNCTION check_vlp", "CA": "FUNCTION check_ca", "THIS": "FUNCTION check_this", "NOTE": "FUNCTION add_notes", "CHECK_KEY_TYPE": "FUNCTION check_key_type", - "CHECK_ONLY_FIRST": "FUNCTION always_true" + "CHECK_ONLY_FIRST": "FUNCTION always_true", + "VALUE": "FUNCTION check_value" } \ No newline at end of file diff --git a/configs/compliance/generate/user_conf_types.json b/configs/compliance/generate/user_conf_types.json index 51a6138..f14b6a7 100644 --- a/configs/compliance/generate/user_conf_types.json +++ b/configs/compliance/generate/user_conf_types.json @@ -1,12 +1,15 @@ { - "Protocol": "dict", + "Certificate": "dict", + "CertificateExtensions": "dict", + "CertificateSignature": "set", "CipherSuite": "set", + "Extension": "dict", "Groups": "list", - "CertificateSignature": "set", "Hash": "set", + "KeyLengths": "set", + "Misc": "dict", + "Protocol": "dict", "Signature": "set", - "Extension": "dict", - "KeyLengths": "list", "TrustedCerts": "dict", "Transparency": "dict" } \ No newline at end of file diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 5fa3935d068419a97a516c0d24f502e22bac55fe..e51163a4902aae263b0d11f7d34acdc66b81828c 100644 GIT binary patch delta 9508 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 EzhB1d9{>OV delta 10208 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 diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index f50bd2d..33b6d40 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -3,7 +3,6 @@ import json import re from pathlib import Path -import pprint from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration @@ -156,7 +155,7 @@ def _solve(self, start, finish): tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) tokens = [token.strip() for token in tokens] for i, token in enumerate(tokens): - next_token = tokens[i+1] if i < len(tokens)-1 else None + next_token = tokens[i + 1] if i < len(tokens) - 1 else None to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) tokens = [token for token in tokens if token] @@ -170,7 +169,7 @@ def _solve(self, start, finish): tokens.insert(0, str(result)) return tokens[0] - def _evaluate_condition(self, condition, next_condition = None): + def _evaluate_condition(self, condition, next_condition=None): """ Evaluates a condition and returns if it is True or False :param condition: condition to evaluate @@ -250,6 +249,12 @@ def __init__(self): self._certificate_parser = CertificateParser() self._cert_sig_algs = [el[0] for el in self._database_instance.run(tables=["CertificateSignature"], columns=["name"])] + self._user_configuration_types = load_configuration("user_conf_types", "configs/compliance/generate/") + self._type_converter = { + "dict": dict, + "list": list, + "set": set, + } def level_to_use(self, levels, security: bool = True): """ @@ -310,7 +315,7 @@ def input(self, **kwargs): # this is temporary with open("testssl_dump.json", 'r') as f: test_ssl_output = json.load(f) - self.prepare_testssl_output(test_ssl_output) + self.prepare_testssl_output(test_ssl_output) if output_file and self._validator.string(output_file): if self._apache: self._config_class = ApacheConfiguration() @@ -353,8 +358,6 @@ def _add_certificate_signature_algorithm(self, alg): :return: a list containing the parsed algorithms """ to_return = [] - if not self._user_configuration.get("CertificateSignature"): - self._user_configuration["CertificateSignature"] = set() if isinstance(alg, str): alg = [alg] @@ -393,27 +396,27 @@ def find_cert_index(field: str): return "1" def prepare_testssl_output(self, test_ssl_output): + # all the necessary field are initialized here + for field in self._user_configuration_types: + data_structure = self._user_configuration_types.get(field) + # this final step is needed to convert from string to data_structure + self._user_configuration[field] = self._type_converter.get(data_structure, dict)() for site in test_ssl_output: for field in test_ssl_output[site]: actual_dict = test_ssl_output[site][field] # Each protocol has its own field if (field.startswith("SSL") or field.startswith("TLS")) and field[3] != "_": - if not self._user_configuration.get("Protocol"): - self._user_configuration["Protocol"] = {} - protocol_dict = self._user_configuration.get("Protocol") # Standardization to have it compliant with the database new_version_name = field.replace("_", ".").replace("v", " ").replace("TLS1", "TLS 1") if new_version_name[-2] != '.': new_version_name += ".0" # The protocols may appear both as supported and not supported, so they are saved in a dictionary # with a boolean associated to the protocol to know if it is supported or not - protocol_dict[new_version_name] = "not" not in actual_dict["finding"] + self._user_configuration["Protocol"][new_version_name] = "not" not in actual_dict["finding"] # All the ciphers appear in different fields whose form is cipher_%x% elif field.startswith("cipher") and "x" in field: - if not self._user_configuration.get("CipherSuite"): - self._user_configuration["CipherSuite"] = set() value = actual_dict.get("finding", "") if " " in value: # Only the last part of the line is the actual cipher @@ -433,10 +436,6 @@ def prepare_testssl_output(self, test_ssl_output): # From the certificate signature algorithm is possible to extract both CertificateSignature and Hash elif field.startswith("cert_Algorithm") or field.startswith("cert_signatureAlgorithm"): - if not self._user_configuration.get("Hash"): - self._user_configuration["Hash"] = set() - if not self._user_configuration.get("Certificate"): - self._user_configuration["Certificate"] = {} if " " in actual_dict["finding"]: tokens = actual_dict["finding"].split(" ") sig_alg = tokens[-1] @@ -452,10 +451,6 @@ def prepare_testssl_output(self, test_ssl_output): self._user_configuration["Certificate"][cert_index]["SigAlg"] = sig_alg elif field.startswith("cert_keySize"): - if not self._user_configuration.get("KeyLengths"): - self._user_configuration["KeyLengths"] = set() - if not self._user_configuration.get("Certificate"): - self._user_configuration["Certificate"] = {} # the first two tokens (after doing a space split) are the Key Algorithm and its key size element_to_add = actual_dict["finding"].split(" ")[:2] self._user_configuration["KeyLengths"].add(tuple(element_to_add)) @@ -466,8 +461,6 @@ def prepare_testssl_output(self, test_ssl_output): # In TLS 1.2 the certificate signatures and hashes are present in the signature algorithms field. elif field[-11:] == "12_sig_algs": - if not self._user_configuration.get("Hash"): - self._user_configuration["Hash"] = set() finding = actual_dict["finding"] elements = finding.split(" ") if " " in finding else [finding] hashes = [] @@ -485,8 +478,6 @@ def prepare_testssl_output(self, test_ssl_output): # From TLS 1.3 the signature algorithms are different from the previous versions. # So they are saved in a different field of the configuration dictionary. elif field[-11:] == "13_sig_algs": - if not self._user_configuration.get("Signature"): - self._user_configuration["Signature"] = set() finding = actual_dict["finding"] values = finding.split(" ") if " " in finding else [finding] values = [convert_signature_algorithm(sig) for sig in values] @@ -501,45 +492,33 @@ def prepare_testssl_output(self, test_ssl_output): # The transparency field describes how the transparency is handled in each certificate. # https://developer.mozilla.org/en-US/docs/Web/Security/Certificate_Transparency (for the possibilities) elif "transparency" in field: - if not self._user_configuration.get("Transparency"): - self._user_configuration["Transparency"] = {} - config_dict = self._user_configuration["Transparency"] # the index is basically the certificate number - index = len(config_dict) - config_dict[index] = actual_dict["finding"] + index = self.find_cert_index(field) + self._user_configuration["Transparency"][index] = actual_dict["finding"] elif field.startswith("cert_chain_of_trust"): - if not self._user_configuration.get("TrustedCerts"): - self._user_configuration["TrustedCerts"] = {} - config_dict = self._user_configuration["TrustedCerts"] # the index is basically the certificate number - index = len(config_dict) - config_dict[index] = actual_dict["finding"] + index = self.find_cert_index(field) + self._user_configuration["TrustedCerts"][index] = actual_dict["finding"] - elif field == "cert" or re.match(r"cert ", field): - if not self._user_configuration.get("Certificate"): - self._user_configuration["Certificate"] = {} - cert_index = self.find_cert_index(field) - if not self._user_configuration["Certificate"].get(cert_index): - self._user_configuration["Certificate"][cert_index] = {} - cert_data = self._certificate_parser.run(actual_dict["finding"]) - for entry in cert_data: - self._user_configuration["Certificate"][cert_index][entry] = cert_data[entry] + elif (field == "cert" or re.match(r"cert ", field)) or \ + (field == "intermediate_cert" or re.match(r"intermediate_cert <#\d+>", field)): - elif field == "intermediate_cert" or re.match(r"intermediate_cert <#\d+>", field): - if not self._user_configuration.get("Certificate"): - self._user_configuration["Certificate"] = {} cert_index = self.find_cert_index(field) - cert_index = "int_" + cert_index + if field.startswith("int"): + cert_index = "int_" + cert_index if not self._user_configuration["Certificate"].get(cert_index): self._user_configuration["Certificate"][cert_index] = {} + if not self._user_configuration["CertificateExtensions"].get(cert_index): + self._user_configuration["CertificateExtensions"][cert_index] = {} cert_data = self._certificate_parser.run(actual_dict["finding"]) for entry in cert_data: - self._user_configuration["Certificate"][cert_index][entry] = cert_data[entry] + if entry == "Extensions": + self._user_configuration["CertificateExtensions"][cert_index] = cert_data[entry] + else: + self._user_configuration["Certificate"][cert_index][entry] = cert_data[entry] elif field in self.misc_fields: - if not self._user_configuration.get("Misc"): - self._user_configuration["Misc"] = {} self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): @@ -720,12 +699,6 @@ def __init__(self): super().__init__() self._configuration_rules = load_configuration("configuration_rules", "configs/compliance/generate/") self._configuration_mapping = load_configuration("configuration_mapping", "configs/compliance/generate/") - self._user_configuration_types = load_configuration("user_conf_types", "configs/compliance/generate/") - self._type_converter = { - "dict": dict, - "list": list, - "set": set, - } def _get_config_name(self, field): name = self._configuration_mapping.get(field, None) @@ -817,8 +790,12 @@ def __init__(self, user_configuration): "<": lambda op1, op2: op1 < op2, ">=": lambda op1, op2: op1 >= op2, "<=": lambda op1, op2: op1 <= op2, - "==": lambda op1, op2: op1 == op2 + "==": lambda op1, op2: op1 == op2, + "!=": lambda op1, op2: op1 != op2, + "in": lambda op1, op2: op1 in op2, + "not in": lambda op1, op2: op1 not in op2, } + self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: # function(**kwargs) -> bool @@ -896,12 +873,13 @@ def check_this(self, **kwargs): """ enabled = kwargs.get("enabled", False) second_condition = kwargs.get("next_condition", " ") - field, name = second_condition.split(" ") + tokens = second_condition.split(" ") + field = tokens[0] + name = " ".join(tokens[1:]) # only the first two fields of the entry matter, and entry is only needed for key lengths entry_data = name.split(",") if "," in name else (None, None) second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, partial_match=True) - print(enabled, second_enabled, name) enabled = second_enabled or enabled self._entry_updates["has_alternative"] = enabled return enabled @@ -941,6 +919,58 @@ def check_key_type(self, **kwargs): self._entry_updates["levels"].append("recommended") return True + def check_value(self, **kwargs): + tokens = kwargs.get("tokens", []) + config_field = tokens.pop(0) + tokens_string = " ".join(tokens) + tokens = re.split(self._operators_regex, tokens_string) + tokens = [t.strip() for t in tokens if t] + value = tokens[0] + operator = tokens[1] + name = "".join(tokens[2:]) + # if there is a "[" and a corresponding "]" then the text inside is a level of a dictionary + levels = re.findall(r"\[(.*?)]", name) + if not levels: + levels = [name] + field = self._user_configuration.get(config_field, {}) + last_level = [levels[-1]] if "," not in levels[-1] else levels[-1].split(",") + last_level = map(str.strip, last_level) + # used the in because in this way is easier to edit if needed + if config_field in ["Certificate", "CertificateExtensions"]: + result = True + for cert in field: + for level in last_level: + levels[-1] = level + # I pass to the function that gets the value the certificate dictionary as field + configuration_value = self._get_configuration_field(field.get(cert, {}), levels) + reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result or len(configuration_value) == 0: + self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") + result = result and partial_result + else: + result = True + for level in last_level: + levels[-1] = level + configuration_value = self._get_configuration_field(field, levels) + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result: + self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") + result = result and partial_result + return result + + @staticmethod + def _get_configuration_field(field, levels): + for level in levels: + field = field.get(level, {}) + return field + + def check_dict_value(self, **kwargs): + self.check_value(**kwargs) + + def check_year_in_days(self, **kwargs): + print(kwargs) + @staticmethod def always_true(**kwargs): return True From 1351c3f7806daf879c9a370312abd9170cef5a57 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Sat, 3 Jun 2023 11:42:23 +0200 Subject: [PATCH 100/209] Fixed issue with database_filler.py and updated database --- DatabaseFiller/database_filler.py | 9 ++++++++- DatabaseFiller/guidelines.xlsx | Bin 101364 -> 101688 bytes DatabaseFiller/requirements.db | Bin 1146880 -> 1146880 bytes configs/compliance/requirements.db | Bin 1146880 -> 1146880 bytes 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py index f89bee3..e438d0e 100755 --- a/DatabaseFiller/database_filler.py +++ b/DatabaseFiller/database_filler.py @@ -236,10 +236,17 @@ def fill_extra_table(sheet_name: str) -> bool: if "_" not in t_name or has_valid_underscore: values_dict[t_name][row[0]].append(content) if is_double_guideline(header[0]): - for other_guideline in header[0].split("+")[1:]: + tokens = header[0].split("+") + base_guideline = tokens[0].replace("(", "").strip() + for other_guideline in tokens[1:]: other_name = get_guideline_name_for_database(other_guideline) other_table = sheet_mapped + other_name + version_name values_dict[other_table] = values_dict[table_name] + for el in values_dict[other_table]: + # Update the guideline name + for i, entry in enumerate(values_dict[other_table][el]): + if isinstance(entry, str) and entry.upper() == base_guideline: + values_dict[other_table][el][i] = other_name # Convert all the data into tuples to add them to the database and group them by guideline name values_groups = {} diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index e98915202e51789e4b317006a357a145407ce0d1..7c64d0008e4bae37ab9990689c42e36448b6bd27 100644 GIT binary patch delta 9208 zcma)B1z1$w)87ly-JODzl%#YC(p@6m-6gSf2uR9;w1g7Ug6K;N3Ic*CA&MZVGzNl* z!gqHUW!KmD|9qF{;kjq-%^bw!E%d;pm*$@F z!PUELGX!H1uVcu&M5#qDO%3vpeVp{X2c`OI;t^Fc^1=BZaj7Wcmhl6d+ILiS1D%8e zd%j)4mcq7oHr&YcsNSr;f4sV}&=|m)DAu)EcN9lp=G&jAkucha=NEr%!&a=WPN;12`6##4K7V2e!P{WTQ^j( z_QTJ<&$188%|g}{M7ywXHEt5Vc-pnR%?WMh|NK_>^IQ23110YVphGG0Pg0I<&_Ii6 zj`;mw<~zT49t%quN%kfa`yyulvj~G(>u{CR?G@gu+n>((_z#?Sb7k`>pQ6N&a1#b^ zOvu2SeO6FHq%og#Nr-#lG98~+GPE?R0URFX>QdypldUqjjZIZ}``mP5jLX$St8v^ZI^B%+7E{ zG_JOB^ufeWUfIpzAI7=b3wR-mkL~c4C3;xgFW)@0n8_;o$7QBE(DK38pry5&z=0#Mk8E1OyE!Ptv(Pu>)7W2}p;<4(* zx8w98^CGH_DY2zyY22GScXn7)J6{knTnHtH5;dPy_p7=^=Tkt(#7q2LuacB0-fP@P zNStc+-Hk!<>8(PwA5V;or!A97YQmZHZm=X+8%Cce9Fllzxk_<2DPhLZ{3-uF{lfDK zFKsfb><+Plk2m5ivd=7@*CcV?4gMB9Hrzkd=kG`2Ojg{o>yz7iKHDnu{N6=>g8J(E zPucqH`h21$PgUmx`+i(?y+^QAtN#&aiAVn%aVRfkmtrwS{I>4mU|o#8xR=&p18vZk zlD30W@Kq@FWGh1yT!EW-f25&oMJ-C0{Aqx+Wy@j|3r;?)mkr>}Ua=*FKGirSOqgh0 zx$Gs~5|iW+ESS=0YR{tjD^y-i2aLg2YW)*`OT?}{H^za z3lmK&_TG#8J0bog8u+T|6l%?+bh{5WDYVVploEuKYm&IB6AYfk3ctn1=e|KPAhjxq zbN23Nr}~OjBUg*-4e4yCh_}s0BaLzFkbWzhe(_a@Pn!8F|;N z8aY9`#QpATgdicS7M_BGTOsMWOPB7F_N?Z$egD9la7Jg1V)F&eHR=PKF{zi7UiQUa zdKt@t6|c6La|$KQg=2{Yg5L_lM&C!yR`L4>L8;fg_yQEub^LZq+O6S4?f_ESq+m=@ zVg4tP9x0sRzlnrp!~*!xJ6XM6v2ZT2-svdKx!$OHX7y5CcH>+iiA{9$?OsJ+{EbbT z@%zMh6--w&mzNLZE#|ilUaIg*P30OiWhEzNoGWnWHT)DBnPRQ-keM6D9?wwG1l;RM zL>Kx=>I3@q6-aO0{M;34e6tf=?;iXmI&u!rU8(D}zbMAsXzkFwoTl$=8vX9K^#5VECmUwC*0guAUnN2>)k4VhvcTi6&-T*xeGf*J4Ih0RBa>+z;>{?W2ryss z6QK**DkaeDY47h(6g%v|3LPHTb~eR0=Qh~EAC4V1!eo3lDLzw}Q#caj z-Q7BJKCw^WlpqEk*Z!g4qC>})XEx(_i*HU!wbq503QODD!vhCfj?JWaIHef$YIy{H zcAb~HY%$r27hbj&s+{r_SR;MORcSWu5?$$@X;%pim{&^Dw63P!Vdgbz5sff9lu#}d z$@a`7=;p6tpN(!1T2|7iEV_F!T7<5n>GSMcsDY(9eHz0p`GiTDK4_1UE}S2cANR&|s3h2pr_WI-uq7*-Im^O|B&-sVh|mQyd1>|ySO}@IGJD_S;;9%te7Z|UXb=>O&=pz^h{%P}! z$BttIk^+)3m9Ujl=w-~P-qs#=T&>5C{jLxe7|OWNkwO(`j#I-v#PByRK`KS2J(#OJ z>D_mKahdg-%cmjwS^vppJGfn6|4kz=Y$G0-R*`hVL$jSt;UqNSTh#B+v(hf|}wQxG=?L89GI^XWJZwINBGVe?wy zXPCOLjP`fFgJhmfdlq&}E@3PABa0BlBzL*qw`>#E!U6%(CMp@%`gaAUQwpX7JscHjxXgU|3aXc>u z!=>a45>&EL?z-dzM?>}oR}SbWa@et|34+?xzH~{n!Pd*g2BI=sgNbTGhVTQP@onBu z=-jq)A~4Y-*TGw25+4a|v*LdB*Ilm9|E%v*FZehl&yI@k<=;yY$StT<{jJUbnrEpT| zymEcBBpq*Qf5&TbC!aDc=)huWq`}Mh!l!)eg292;-eP-+VNc%MzI1AI62T|hqq<+J ztK;)E-}DVUZ9Z(RvPH_$pM2Ssragfrd8koYQiPoGWuN^EDjc)83#939! z^<<13mKCT!erWt=p0e(KV@%?_6K&+1@DY!oZCfJ>M>=*3vsG-$rj*EqvA!ptR^Kz{ zP&%aBJ2dx@1SxhA$fgCr7bEvcg%evY)meRzR6;N8@a+RY=6G*4;Cmuh=gbOg^HVaJ+JnLQN^ zA8fzE+(BtNz5n*m(f@&mNW3qU>_$D`POO}C{_I|R37m{`wRX&ZJx8Kd_Hiy2vSmq$Q*O;_(>RR@EGgecbKaEzNVHwPn zePjPpuFKMFT9EB+?rrZ!%aQXB#3@#FNr``P>7!H_c5XAi>RsBpxTr?+hI!B0bAc6u zfyrd-9C+GeuTx0XC8la0Hnl`&;g_4{*EdlYJySEb5Ei@&txST_2{|=)W|HnzoeI)x zYBR_9pj5smzHCHY*h3i+yen_tR8!by@x0{2Zt^N7-NV*LKh}Kd630d^-_9_sEmg<~ z?}h!doE}hooB8>h@r-v{Ii7@T^;1NdcI$nga(C@o$-|8MD_?A1?->S1c zw`ueI&GgE!oaX7|iEhgy#9nMPPMm@R2G!3$&*Sak@<1P=3WW0}@&71CN^5_YBe=6$ zZ~7~ohR^x6j;~=;UU(Fl+8O_0a%ohSn}=3bhjB>BVP?BcSJbu|k8Y5ohW(})t%f?u zU=jm&sf!Nfdc_iB<8$%7Gi71D#UoX&TUv*s&?rtrrq1~A9sb)C?~AWu-AP*_<)(JE zuU-ihh;avi{k^7IF78gajUK~6Q_KheJ{AexJzAgo?N6Po_tIBV*8PgeoP5iJ2^ETyRe1km(Rd@yIlS#xf@uK3Ez zt{q0!Tl*ZxENhdbtsvuJce?~E28(1qDDSnmQ|)^(-M5Nt*RF}>G(!|U3Yj{leLQsw zWRvnT_;s$SV@kWMttnAdg_9*tsAZBdiV^bc#~Mr!irVLlQ8?f7w;jxJT)D31-(UQ^ zjXQ`pT*PLS(pjgLt1u-x^1dK zJIr7uFo49y;~hx~r+t{~-mZVy9Q|XP#CeW~+mngoPQjnb^MtCR)s^}ANH6BTRwznn zpIltUQW;Jtq|4XC6y9hnFlE#hNPbSjmK;-BQTFtF-F1yNHSetDD5Ep{YTkaX_P1i1 zwyTt17`9odT!3A#0!$c$(l|svPE_Gk^Q0jqlUlC3@?b zLuKr{8xtckmqkDLmF!qPuT?vE)dO9v%MCmuzvZo(R>6K`^ZcjN` zK4`r@)ZTu&lw9ajG|v3@?cy)e9jiqnatu_SJyNa#(FTR3xS}GL*J>2J8+1R>UKleR ztv*MOUqN(3n~P8Hc_KcS!}?9uGRx?=y-g;zYj`fV`>U{^OkNuH3~T*UCKZ)+Ra6;@ zS+B?sEHIheRSB$EA1v@(wW_|;LxXGIr!^V1E5P)Ym9f5!|Hq@LoJxm}HZyA0k%K=i z=rX@B#hvQeI?6vEp?D5_8D~S&1XaA_$!dgPVBlfigCCtezn%4!WWU+W;El#+0bguP_@{L?9;`PQU}lBSkZI70Z62gKAL~!8kA$!zPzzCeu!aB(7=$D+ z*^py_6)S&@nz7`jT{0&{}S!=Xx73)|2DU5HmRX#@_r3|0$pCb7q zYG8=xbX?iOdQ`C3NPz6b$blOn96}Q|@W6%HjoMg$NBYi-FfxXsB3(EO3h7~`P=N3R zK7${wNh$Gf0-KRe%?Ti&xS~*@&Ilodp1*|AQQ-7^h2hhKwW0|-&mrdAZGx2>ef(a8 zGM*@0PWiVeh9MM;#XtoH*flh(GI7K!#-jl;1o^Up{#v@=YT3epKp4;#+ol8z2m@$g ziC2#OF1P}^7%zpCf^B{zz$?DcAA#VPOFr`_K!hw@l?TBTe;8;ht%$G{78MBkBe{S} zivQfYcMOO#%=agbnhNrjwPDAObi={OFCwKopcFBa1cpP7Zg3{vlz#$*Yamr+!+(8& za1bC)8!1irCA|Tcdgvpi;7<)mSix1ucqi!BEq(}u5se@nBZO);i5xbJIpX-!@JE0k z#%4$my9j_B;ajh851}9E+W^=VM+fP-gjB5qRS`_}!U4i9k<#5?Qs1ip116ab1eSgk zU}J-)Apfq-1s(9Cxgz>Np{i!B3;; zfZr(|=Bo+<+Q$G?2v@^mKv%O9;I>q-J2A)nqc(rh0kZDE0T{B0ui~G0m1TSniX9GS ztbZ{7@ajuF5?Lko7#Xz*ijFML2b+io7~p+mYJ_Xmv7l>JXJLAYplfr#08pEI=m2-c zkN{k9#{dd(00Y1e(@g}Zg~k4oDNInCTIg7xLIPUz09Y`YgyZ-@t?|)O*^Q9Cfo&3eSQ@C0_y&aupl>P0NT{)d;~AnB z$LJ`F=13HOJI6wM@n2Jymt!!+t9a1SH_zbm?1Kb(=MU_FKf zD@r;h54EU4hu(2TLZ^ZS8*CfYN04`z1Uk6qhJX?l`C9;@mIdgDcYKhD5y{8S&hxQ~ z(1DJMf_Vqs*=b*-v%|^96ri3*(NQIW5U6b6Zt&}u1wBnH@XJ6TTHxO)Fu0jRKt>?{ E2iZlJR{#J2 delta 8810 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 Y2IC|?6q+!Y9`x4(^_%%B6d17o0+d^_!2kdN diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index e51163a4902aae263b0d11f7d34acdc66b81828c..f0a7000329090a06a79f98535ff5f3c2224ee790 100644 GIT binary patch delta 33620 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 BC$#_o delta 37434 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 BC$#_o delta 37434 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 Date: Sat, 3 Jun 2023 11:44:30 +0200 Subject: [PATCH 101/209] Fixed issues with the core running the module --- modules/compliance/compliance_base.py | 62 +++++++++++++++++++-------- modules/core.py | 1 + modules/server/wrappers/testssl.py | 2 +- run.py | 30 +++++++++++-- tlsa/tlsa.py | 33 ++++++++++---- 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 33b6d40..e4405c9 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,6 +1,6 @@ import datetime import itertools -import json +import pprint import re from pathlib import Path @@ -12,6 +12,7 @@ from utils.database import get_standardized_level from utils.loader import load_configuration from utils.logger import Logger +from utils.prune import pruner from utils.validation import Validator @@ -59,7 +60,8 @@ def _partial_match_checker(field_value, name): return enabled @staticmethod - def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition=""): + def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition="", + certificate_index="1"): """ Checks if a field is enabled in the user configuration :param user_configuration: the configuration in which the data should be searched @@ -69,6 +71,8 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match :param partial_match: Default to false, if True the :param condition: Default to "", the condition that the field has. :type condition: str + :param certificate_index: Default to "1", the certificate to check + :type certificate_index: str :return: """ field_value = user_configuration.get(config_field, None) @@ -84,6 +88,12 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match if enabled is None: enabled = True if "all" in field_value else False + elif isinstance(field_value, dict) and field_value.get("1"): + # Certificate case + cert_data = field_value.get(certificate_index, {}) + enabled = name in cert_data + print(cert_data, name) + elif isinstance(field_value, dict): # Extensions and transparency case if name.isnumeric(): @@ -228,6 +238,8 @@ class Compliance: def __init__(self): self._custom_guidelines = None self._apache = True + # legacy vs security level switch + self._security = True self._input_dict = {} self._database_instance = Database() self.__logging = Logger("Compliance module") @@ -256,19 +268,17 @@ def __init__(self): "set": set, } - def level_to_use(self, levels, security: bool = True): + def level_to_use(self, levels): """ Given two evaluations returns true if the first one wins, false otherwise. :param levels: list of evaluations to be checked :type levels: list - :param security: True if security wins false if legacy wins, default to true - :type security: bool :return: the standard which wins :rtype: int """ # If a level is not mapped it can be considered as a Not mentioned - security_mapping = "security" if security else "legacy" + security_mapping = "security" if self._security else "legacy" if not levels: raise IndexError("Levels list is empty") first_value = self.evaluations_mapping.get(security_mapping, {}).get(get_standardized_level(levels[0]), 4) @@ -276,6 +286,7 @@ def level_to_use(self, levels, security: bool = True): for i, el in enumerate(levels[1:]): evaluation_value = self.evaluations_mapping.get(security_mapping, {}).get(get_standardized_level(el), 4) if first_value > evaluation_value: + # +1 is needed because the first element is ignored best = i + 1 # if they have the same value first wins return best @@ -298,8 +309,17 @@ def input(self, **kwargs): actual_configuration = kwargs.get("actual_configuration_path") hostname = kwargs.get("hostname") self._apache = kwargs.get("apache", True) + self._security = kwargs.get("security", True) output_file = kwargs.get("output_config") self._custom_guidelines = kwargs.get("custom_guidelines") + guidelines_string = kwargs.get("guidelines") + + # guidelines evaluation + self._validator.string(guidelines_string) + guidelines_list = guidelines_string.split(",") if "," in guidelines_string else [guidelines_string] + sheets_to_check = self._alias_parser.get_sheets_to_check(guidelines_list) + self._validator.dict(sheets_to_check) + if actual_configuration and self._validator.string(actual_configuration): try: self._config_class = ApacheConfiguration(actual_configuration) @@ -309,12 +329,9 @@ def input(self, **kwargs): ) self._config_class = NginxConfiguration(actual_configuration) self.prepare_configuration(self._config_class.configuration) - if hostname and self._validator.string(hostname): - # test_ssl_output = self.test_ssl.run(**{"hostname": hostname}) + if hostname and self._validator.string(hostname) and hostname != "placeholder": + test_ssl_output = self.test_ssl.run(**{"hostname": hostname, "one": True}) - # this is temporary - with open("testssl_dump.json", 'r') as f: - test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) if output_file and self._validator.string(output_file): if self._apache: @@ -323,6 +340,7 @@ def input(self, **kwargs): self._config_class = NginxConfiguration() self._config_class.set_out_file(Path(output_file)) self._input_dict = kwargs + self._input_dict["sheets_to_check"] = sheets_to_check # To override def _worker(self, sheets_to_check): @@ -339,16 +357,11 @@ def _worker(self, sheets_to_check): def run(self, **kwargs): self.input(**kwargs) - guidelines_string = kwargs.get("guidelines") - self._validator.string(guidelines_string) - guidelines_list = guidelines_string.split(",") if "," in guidelines_string else [guidelines_string] - sheets_to_check = self._alias_parser.get_sheets_to_check(guidelines_list) - self._validator.dict(sheets_to_check) - self._worker(sheets_to_check) + self._worker(self._input_dict["sheets_to_check"]) return self.output() def output(self): - # pprint.pprint(self._output_dict) + pprint.pp(pruner(self._output_dict)) return self._output_dict.copy() def _add_certificate_signature_algorithm(self, alg): @@ -400,6 +413,7 @@ def prepare_testssl_output(self, test_ssl_output): for field in self._user_configuration_types: data_structure = self._user_configuration_types.get(field) # this final step is needed to convert from string to data_structure + # by using a dict is possible avoid using eval self._user_configuration[field] = self._type_converter.get(data_structure, dict)() for site in test_ssl_output: @@ -470,6 +484,7 @@ def prepare_testssl_output(self, test_ssl_output): if "-" not in el and "+" in el: # The entries are SigAlg+HashAlg tokens = el.split("+") + # RSASSA-PSS is a subset of RSA signatures.append(tokens[0].replace("RSASSA-PSS", "RSA").lower()) hashes.append(tokens[1]) self._add_certificate_signature_algorithm(signatures) @@ -799,6 +814,7 @@ def __init__(self, user_configuration): # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: # function(**kwargs) -> bool + # kwargs are defined in the _evaluate_condition method of the ConditionParser class. def check_year(self, **kwargs): """ @@ -969,7 +985,8 @@ def check_dict_value(self, **kwargs): self.check_value(**kwargs) def check_year_in_days(self, **kwargs): - print(kwargs) + # todo implement this + return True @staticmethod def always_true(**kwargs): @@ -1020,6 +1037,8 @@ def list_strings(self): print("Guideline ", guideline, " doesn't have any special version") print("") print("NOTE: if a version is omitted the default one will be used.") + import sys + sys.exit(0) def _fill_sheets_dict(self): for table in self._database_instance.table_names: @@ -1075,6 +1094,11 @@ def get_sheets_to_check(self, aliases): sheets_to_check = {} for alias in aliases: alias = alias.strip() + if alias == "list": + # print the list. The function terminates the program + self.list_strings() + if alias == "aliases": + self.list_aliases() self.is_valid(alias) tokens = alias.split("_") guideline = tokens[0].upper() diff --git a/modules/core.py b/modules/core.py index 5093658..aaa0d11 100644 --- a/modules/core.py +++ b/modules/core.py @@ -45,6 +45,7 @@ class Analysis(Enum): APK = 1 DOMAINS = 2 CONFIGURATION = 3 + COMPLIANCE = 4 def __init__( self, diff --git a/modules/server/wrappers/testssl.py b/modules/server/wrappers/testssl.py index 7a67832..906e952 100644 --- a/modules/server/wrappers/testssl.py +++ b/modules/server/wrappers/testssl.py @@ -68,7 +68,7 @@ def __init__(self): """ Loads testssl variables. """ - self.__testssl = f"dependencies{sep}testssl.sh{sep}testssl.sh" + self.__testssl = f"dependencies{sep}testssl.sh.git{sep}testssl.sh" self.__input_dict = {} def input(self, **kwargs): diff --git a/run.py b/run.py index 8cfc4bb..7962894 100644 --- a/run.py +++ b/run.py @@ -9,11 +9,15 @@ class ComplianceAction(argparse.Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): super().__init__(option_strings, dest, nargs, **kwargs) - def __call__(self, parser, namespace, values, option_string=None): + def __call__(self, parser, namespace, values, option_string=""): if not isinstance(namespace.__getattribute__(self.dest), dict): namespace.__setattr__(self.dest, {}) + converters = { + "false": False, + "true": True + } dictionary = namespace.__getattribute__(self.dest) - dictionary[option_string.strip("-")] = values[0] + dictionary[option_string.strip("-")] = converters.get(values[0].lower(), values[0]) if __name__ == "__main__": @@ -150,7 +154,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) parser.add_argument( - "--guideline", + "--guidelines", type=str, nargs=1, action=ComplianceAction, @@ -162,13 +166,31 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument( "--apache", - type=bool, + type=str, nargs=1, action=ComplianceAction, default=True, dest="compliance_args", help="Default to True. If True the output configuration will have apache syntax, if false nginx will be used." ) + parser.add_argument( + "--security", + type=str, + nargs=1, + action=ComplianceAction, + default=True, + dest="compliance_args", + help="Default to True. If False the legacy level priority will be used" + ) + + parser.add_argument( + "--output_config", + type=str, + nargs=1, + action=ComplianceAction, + dest="compliance_args", + help="Where to save the output configuration file, only needed for generate one/many" + ) # todo add default aliases configurations for analysis # configurations.add_argument() diff --git a/tlsa/tlsa.py b/tlsa/tlsa.py index 11db16c..042db8e 100644 --- a/tlsa/tlsa.py +++ b/tlsa/tlsa.py @@ -1,14 +1,14 @@ import logging +from os import listdir +from os.path import isfile, join, sep from pathlib import Path -from utils.logger import Logger +from modules.core import Core from utils.colors import Color -from utils.loader import load_configuration from utils.configuration import pretty +from utils.loader import load_configuration from utils.loader import load_list_of_domains -from modules.core import Core -from os import listdir -from os.path import isfile, join, sep +from utils.logger import Logger class Tlsa: @@ -104,10 +104,10 @@ def __start_analysis(self, args): ) config_or_modules = args.configuration if args.compliance_args: - assert "guideline" in args.compliance_args, "Guideline Argument Missing!" + assert "guidelines" in args.compliance_args, "Guideline Argument Missing!" all_args=args.compliance_args.copy() - comp_one_or_many="compare_one" if "," not in all_args['guideline'] else "compare_many" - gen_one_or_many="generate_one" if "," not in all_args['guideline'] else "generate_many" + comp_one_or_many="compare_one" if "," not in all_args['guidelines'] else "compare_many" + gen_one_or_many="generate_one" if "," not in all_args['guidelines'] else "generate_many" args.compliance_args={ comp_one_or_many:all_args, gen_one_or_many:all_args @@ -133,7 +133,7 @@ def __start_analysis(self, args): group_by=args.group_by, apply_fix=args.apply_fix, stix=args.stix, - compliance_args = args.compliance_args + compliance_args=args.compliance_args ) elif args.apk: Core( @@ -175,6 +175,21 @@ def __start_analysis(self, args): openssl_version=args.openssl, ignore_openssl=args.ignore_openssl, ) + elif any(module in ["generate_one", "generate_many"] for module in args.configuration): + Core( + hostname_or_path="placeholder", + configuration=config_or_modules, + output=args.output, + output_type=self.__to_report_type(args.output_type), + type_of_analysis=Core.Analysis.COMPLIANCE, + to_exclude=args.exclude, + group_by=args.group_by, + apply_fix=args.apply_fix, + stix=args.stix, + openssl_version=args.openssl, + ignore_openssl=args.ignore_openssl, + compliance_args=args.compliance_args + ) else: # must be args.list, unless argparse throws error. self.__print_module(args.list) From c7722c524ae7d010807ea52bb505ddf982d9e3cc Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Sat, 3 Jun 2023 11:44:54 +0200 Subject: [PATCH 102/209] Fixed issues with generation and default_versions.json --- configs/compliance/alias/default_versions.json | 12 ++++++------ configs/compliance/apache/template.conf | 2 -- .../compliance/generate/configuration_mapping.json | 2 +- modules/compliance/generate_one.py | 13 +++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/configs/compliance/alias/default_versions.json b/configs/compliance/alias/default_versions.json index 3ab1fc9..43bab9f 100644 --- a/configs/compliance/alias/default_versions.json +++ b/configs/compliance/alias/default_versions.json @@ -30,15 +30,15 @@ "NIST": "", "BSI": "", "ANSSI": "", - "MOZILLA": "", - "AGID": "" + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE" }, "Signature": { "NIST": "", "BSI": "CLIENT_SERVERSIGNATURES", "ANSSI": "", - "MOZILLA": "", - "AGID": "" + "MOZILLA": "INTERMEDIATE", + "AGID": "INTERMEDIATE" }, "Groups": { "NIST": "", @@ -57,13 +57,13 @@ "Certificate": { "NIST": "", "BSI": "", - "AGID": "", + "ANSSI": "", "MOZILLA": "" }, "CertificateExtensions": { "NIST": "", "BSI": "", - "AGID": "", + "ANSSI": "", "MOZILLA": "" }, "Misc": { diff --git a/configs/compliance/apache/template.conf b/configs/compliance/apache/template.conf index a18e210..d61f8c0 100644 --- a/configs/compliance/apache/template.conf +++ b/configs/compliance/apache/template.conf @@ -5,5 +5,3 @@ SSLCertificateKeyFile /path/to/private_key Header always set Strict-Transport-Security "max-age=63072000" - -SSLHonorCipherOrder off \ No newline at end of file diff --git a/configs/compliance/generate/configuration_mapping.json b/configs/compliance/generate/configuration_mapping.json index 430e2cb..087a8c6 100644 --- a/configs/compliance/generate/configuration_mapping.json +++ b/configs/compliance/generate/configuration_mapping.json @@ -4,5 +4,5 @@ "SessionTickets": {"Extension": "session_ticket"}, "Stapling": {"Extension": "status_request"}, "EarlyData": {"Extension": "early_data"}, - "DHParams": {"CipherSuite": "*DH*"} + "DHParams": {"CipherSuite": "%DH%"} } \ No newline at end of file diff --git a/modules/compliance/generate_one.py b/modules/compliance/generate_one.py index 260f33e..80f04ce 100644 --- a/modules/compliance/generate_one.py +++ b/modules/compliance/generate_one.py @@ -21,10 +21,11 @@ def _worker(self, sheets_to_check): sheet = table_to_search columns = self.sheet_columns.get(sheet, columns) # Only the first guideline of each sheet is the interesting one - guideline = list(sheets_to_check[sheet].keys())[0] - table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) - self._database_instance.input([table_name], other_filter=query_filter) - data = self._database_instance.output(columns) - field_rules = self._configuration_rules.get(field, {}) - self._config_class.add_configuration_for_field(field, field_rules, data, columns, guideline) + if sheets_to_check[sheet]: + guideline = list(sheets_to_check[sheet].keys())[0] + table_name = self._database_instance.get_table_name(sheet, guideline, sheets_to_check[sheet][guideline]) + self._database_instance.input([table_name], other_filter=query_filter) + data = self._database_instance.output(columns) + field_rules = self._configuration_rules.get(field, {}) + self._config_class.add_configuration_for_field(field, field_rules, data, columns, guideline) From fed10c3a4c64046ed3d4b7406e5b7ff356997099 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 5 Jun 2023 12:22:00 +0200 Subject: [PATCH 103/209] Removed testing file and improved config loader in compliance module --- .../configuration/apache_configuration.py | 5 +- .../configuration/nginx_configuration.py | 6 +- modules/configuration/configuration.py | 62 +- out.config | 12 - test.config | 32 - testssl_dump_old.json | 752 ------------------ 6 files changed, 42 insertions(+), 827 deletions(-) delete mode 100644 out.config delete mode 100644 test.config delete mode 100644 testssl_dump_old.json diff --git a/modules/compliance/configuration/apache_configuration.py b/modules/compliance/configuration/apache_configuration.py index ee1d233..31006c3 100644 --- a/modules/compliance/configuration/apache_configuration.py +++ b/modules/compliance/configuration/apache_configuration.py @@ -4,6 +4,8 @@ from apacheconfig import make_loader from modules.compliance.configuration.configuration_base import ConfigurationMaker +from modules.configuration.configuration import Configuration +from utils.type import WebserverType class ApacheConfiguration(ConfigurationMaker): @@ -22,8 +24,7 @@ def _load_conf(self, file: Path): :param file: path to the configuration file :type file: str """ - with make_loader() as loader: - self.configuration = loader.load(str(file.absolute())) + self.configuration = Configuration(path=str(file), type_=WebserverType.APACHE).get_conf() def _load_template(self): with open(self._config_template_path, "r") as f: diff --git a/modules/compliance/configuration/nginx_configuration.py b/modules/compliance/configuration/nginx_configuration.py index 4cc60ac..9ab9750 100644 --- a/modules/compliance/configuration/nginx_configuration.py +++ b/modules/compliance/configuration/nginx_configuration.py @@ -5,6 +5,8 @@ from crossplane import parse as nginx_parse from modules.compliance.configuration.configuration_base import ConfigurationMaker +from modules.configuration.configuration import Configuration +from utils.type import WebserverType class NginxConfiguration(ConfigurationMaker): @@ -21,9 +23,7 @@ def _load_conf(self, file: Path): :param file: path to the configuration file :type file: str """ - self.configuration = nginx_parse(str(file.absolute())) - if self.configuration.get("errors", []): - raise ValueError("Invalid nginx config file") + self.configuration = Configuration(path=str(file), type_=WebserverType.NGINX, process=False).get_conf() def add_configuration_for_field(self, field, field_rules, data, columns, guideline, target=None): config_field = self.mapping.get(field, None) diff --git a/modules/configuration/configuration.py b/modules/configuration/configuration.py index f558c20..aa45246 100644 --- a/modules/configuration/configuration.py +++ b/modules/configuration/configuration.py @@ -13,7 +13,7 @@ class Configuration: Apache/Nginx configuration file parser """ - def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=None): + def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=None,process=True): """ :param path: path to the configuration file :type path: str @@ -21,17 +21,22 @@ def __init__(self, path: str, type_: WebserverType = WebserverType.AUTO, port=No :type type_: WebserverType :param port: port to use for the check. :type port: str + :param process: Nginx only, if false the loaded configuration is returned without further processing + :type process: bool """ Validator([(path, str), (type_, WebserverType), (port if port else "", str)]) self.__path = path self.__type = type_ self.__port = port self.__logging = Logger("Configuration APACHE/NGINX") - self.__loaded_conf = self.__load_conf(path) + self.__loaded_conf = self.__load_conf(path, process) def get_path(self): return self.__path + def get_conf(self): + return self.__loaded_conf.copy() + def __obtain_vhost(self, port=None): """ Obtain the virtualhosts from the configuration file. @@ -76,14 +81,16 @@ def __gen(conf_server): for http in conf['http']: if 'server' in http: yield from __gen(http['server']) - - def __load_conf(self, path) -> dict: + + def __load_conf(self, path, process=True) -> dict: """ Load the configuration file. :param path: path to the configuration file :type path: str + :param process: Nginx only, if false the loaded configuration is returned without further processing + :type process: bool :return: loaded configuration :rtype: dict """ @@ -106,7 +113,7 @@ def __load_conf(self, path) -> dict: elif self.__type == WebserverType.APACHE: results = self.__load_apache_conf(file) else: - results = self.__load_nginx_conf(file) + results = self.__load_nginx_conf(file, process) if self.__type == WebserverType.APACHE: self.__logging.set_class_name("Configuration APACHE") @@ -127,12 +134,14 @@ def __load_apache_conf(self, file: Path) -> dict: with make_loader() as loader: return loader.load(str(file.absolute())) - def __load_nginx_conf(self, file: Path) -> dict: + def __load_nginx_conf(self, file: Path, process=True) -> dict: """ Internal method to load the nginx configuration file. :param file: path to the configuration file :type file: str + :param process: If false the loaded configuration is returned without further processing + :type process: bool :return: loaded configuration :rtype: dict """ @@ -152,7 +161,7 @@ def __structure(payload, struct): if directive_key not in struct: struct[directive_key] = [] - elif 'block' not in directive: + elif 'block' not in directive: # if this key already exists, but it's not the start of a subblock, # then it's a list of lists, to indicate many directive with same key # but distinct values @@ -186,21 +195,22 @@ def __structure(payload, struct): struct[directive_key] = directive['args'] payload = nginx_parse(str(file.absolute())) - + if payload['status'] != 'ok' or len(payload['errors']) > 0: self.__logging.error(f"Error parsing nginx config: {payload['errors']}") raise Exception(f"Error parsing nginx config: {payload['errors']}") - - struct = {} - for file in payload['config']: - struct[file['file']] = {}; - __structure(file['parsed'], struct[file['file']]) - - # Remove file if it doesn't have any 'http' or 'server' block - # TODO: not robust enough, an include could be expanded with useful directive for us but not included at this point - # ie: include snippet/directive/ssl.conf from https://github.com/risan/nginx-config - if 'http' not in struct[file['file']] and 'server' not in struct[file['file']]: - del struct[file['file']] + struct = payload + if process: + struct = {} + for file in payload['config']: + struct[file['file']] = {} + __structure(file['parsed'], struct[file['file']]) + + # Remove file if it doesn't have any 'http' or 'server' block + # TODO: not robust enough, an include could be expanded with useful directive for us but not included at this point + # ie: include snippet/directive/ssl.conf from https://github.com/risan/nginx-config + if 'http' not in struct[file['file']] and 'server' not in struct[file['file']]: + del struct[file['file']] return struct @@ -235,8 +245,8 @@ def __check_global(self, modules: dict, openssl: str, ignore_openssl: bool): module, name, fix=False, - vhost=self.__loaded_conf - if self.__type == WebserverType.APACHE + vhost=self.__loaded_conf + if self.__type == WebserverType.APACHE else next(val['http'][0] for file, val in self.__loaded_conf.items() if 'http' in val), vhost_name="global", openssl=openssl, @@ -373,7 +383,7 @@ def __blackbox( self.__logging.debug(f"Analyzing vulnerability {name} in vhost {vhost_name}..") if vhost_name not in boolean_results: boolean_results[vhost_name] = {} - + module.conf.set_webserver(self.__type) is_empty = module.conf.is_empty(vhost) @@ -580,7 +590,7 @@ def save(self, file_path: str = None): elif len(self.__loaded_conf) > 1: self.__logging.debug(f"Folder '{output_folder}/' is not here, creating at {output_folder.absolute()}/") output_folder.mkdir(parents=True, exist_ok=True) - + for path, val in self.__loaded_conf.items(): this_path = Path(path).resolve() @@ -600,13 +610,13 @@ def save(self, file_path: str = None): else: sub_folder = this_path.parent.relative_to(self_path.parent) # subtree relative from main file folder file = output_folder / sub_folder / file_name_extension - + if not file.parent.exists(): self.__logging.debug(f"Folder '{file.parent}/' is not here, creating at {file.parent.absolute()}/") file.parent.mkdir(parents=True, exist_ok=True) # Also here to create 'sub_folder' - + file.touch() # Create the file - + my_payload = [] self.__rebuild_wrapper(val, my_payload) config = nginx_build(my_payload) diff --git a/out.config b/out.config deleted file mode 100644 index 11910c6..0000000 --- a/out.config +++ /dev/null @@ -1,12 +0,0 @@ -# Template based off the Figure 4 of the compliance_paper - - SSLEngine on - SSLCertificateFile /path/to/cert_chain - SSLCertificateKeyFile /path/to/private_key - Header always set Strict-Transport-Security "max-age=63072000" - - -SSLHonorCipherOrder off -SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 TLSv1.2 TLSv1.3 -SSLCipherSuite !ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256:!ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256:!DHE-RSA-WITH-CHACHA20-POLY1305-SHA256:!DH-DSS-WITH-AES-128-GCM-SHA256:!DH-DSS-WITH-AES-256-GCM-SHA384:!DH-DSS-WITH-AES-128-CBC-SHA256:!DH-DSS-WITH-AES-256-CBC-SHA256:!DH-DSS-WITH-AES-128-CBC-SHA:!DH-DSS-WITH-AES-256-CBC-SHA:!DH-RSA-WITH-AES-128-GCM-SHA256:!DH-RSA-WITH-AES-256-GCM-SHA384:!DH-RSA-WITH-AES-128-CBC-SHA256:!DH-RSA-WITH-AES-256-CBC-SHA256:!DH-RSA-WITH-AES-128-CBC-SHA:!DH-RSA-WITH-AES-256-CBC-SHA:!ECDH-ECDSA-WITH-AES-128-GCM-SHA256:!ECDH-ECDSA-WITH-AES-256-GCM-SHA384:!ECDH-ECDSA-WITH-AES-128-CBC-SHA256:!ECDH-ECDSA-WITH-AES-256-CBC-SHA384:!ECDH-ECDSA-WITH-AES-128-CBC-SHA:!ECDH-ECDSA-WITH-AES-256-CBC-SHA:!ECDH-RSA-WITH-AES-128-GCM-SHA256:!ECDH-RSA-WITH-AES-256-GCM-SHA384:!ECDH-RSA-WITH-AES-128-CBC-SHA256:!ECDH-RSA-WITH-AES-256-CBC-SHA384:!ECDH-RSA-WITH-AES-128-CBC-SHA:!ECDH-RSA-WITH-AES-256-CBC-SHA:!AES-128-GCM-SHA256:!AES-256-GCM-SHA384:!CHACHA20-POLY1305-SHA256:!AES-128-CCM-SHA256:!AES-128-CCM-8-SHA256:!ECDHE-PSK-WITH-AES-128-GCM-SHA256:!ECDHE-PSK-WITH-AES-256-GCM-SHA384:!ECDHE-PSK-WITH-AES-128-CCM-SHA256:!RSA-PSK-WITH-AES-128-CBC-SHA256:!RSA-PSK-WITH-AES-256-CBC-SHA384:!RSA-PSK-WITH-AES-128-GCM-SHA256:!RSA-PSK-WITH-AES-256-GCM-SHA384:!PSK-WITH-AES-128-GCM-SHA256:!PSK-WITH-AES-256-GCM-SHA384:!PSK-WITH-AES-128-CCM:!PSK-WITH-AES-256-CCM:!PSK-WITH-AES-128-CCM-8:!PSK-WITH-AES-256-CCM-8:!PSK-WITH-AES-128-CBC-SHA256:!PSK-WITH-AES-256-CBC-SHA384:!PSK-WITH-AES-128-CBC-SHA:!PSK-WITH-AES-256-CBC-SHA:!RSA-WITH-AES-128-CCM:!RSA-WITH-AES-256-CCM:!RSA-WITH-AES-128-CCM-8:!RSA-WITH-AES-256-CCM-8:!RSA-WITH-AES-128-GCM-SHA256:!RSA-WITH-AES-256-GCM-SHA384:!RSA-WITH-AES-128-CBC-SHA256:!RSA-WITH-AES-256-CBC-SHA256:!RSA-WITH-AES-128-CBC-SHA:!RSA-WITH-AES-256-CBC-SHA:!RSA-WITH-3DES-EDE-CBC-SHA:!ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256:!ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384:!ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256:!ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384:!DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256:!DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384:!ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256:!ECDHE-RSA-WITH-ARIA-256-GCM-SHA384:!ECDHE-RSA-WITH-ARIA-128-GCM-SHA256:!DHE-RSA-WITH-ARIA-256-GCM-SHA384:!DHE-RSA-WITH-ARIA-128-GCM-SHA256:!ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384:!ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256:!ECDHE-RSA-WITH-ARIA-256-CBC-SHA384:!ECDHE-RSA-WITH-ARIA-128-CBC-SHA256:!DHE-RSA-WITH-ARIA-256-CBC-SHA384:!DHE-RSA-WITH-ARIA-128-CBC-SHA256:!DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256:!DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256:!DHE-PSK-WITH-CHACHA20-POLY1305-SHA256:!DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384:!DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256:!ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384:!ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256:!DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384:!DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256:!DHE-PSK-WITH-ARIA-256-GCM-SHA384:!DHE-PSK-WITH-ARIA-128-GCM-SHA256:!ECDHE-PSK-WITH-ARIA-256-CBC-SHA384:!ECDHE-PSK-WITH-ARIA-128-CBC-SHA256:!DHE-PSK-WITH-ARIA-256-CBC-SHA384:!DHE-PSK-WITH-ARIA-128-CBC-SHA256:FALLBACK-SCSV -SSLUseStapling on \ No newline at end of file diff --git a/test.config b/test.config deleted file mode 100644 index dab4782..0000000 --- a/test.config +++ /dev/null @@ -1,32 +0,0 @@ -# generated 2023-03-07, Mozilla Guideline v5.6, Apache 2.4.41, OpenSSL 1.1.1k, intermediate configuration -# https://ssl-config.mozilla.org/#server=apache&version=2.4.41&config=intermediate&openssl=1.1.1k&guideline=5.6 - -# this configuration requires mod_ssl, mod_socache_shmcb, mod_rewrite, and mod_headers - - RewriteEngine On - RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/ - RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] - - - - SSLEngine on - - # curl https://ssl-config.mozilla.org/ffdhe2048.txt >> /path/to/signed_cert_and_intermediate_certs_and_dhparams - SSLCertificateFile /path/to/signed_cert_and_intermediate_certs_and_dhparams - SSLCertificateKeyFile /path/to/private_key - - # enable HTTP/2, if available - Protocols h2 http/1.1 - - # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds) - Header always set Strict-Transport-Security "max-age=63072000" - - -# intermediate configuration -SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 -SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 -SSLHonorCipherOrder off -SSLSessionTickets off - -SSLUseStapling On -SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" diff --git a/testssl_dump_old.json b/testssl_dump_old.json deleted file mode 100644 index 85085a1..0000000 --- a/testssl_dump_old.json +++ /dev/null @@ -1,752 +0,0 @@ -{ - "150.35.235.24": { - "service": { - "port": "443", - "severity": "INFO", - "finding": "HTTP" - }, - "pre_128cipher": { - "port": "443", - "severity": "INFO", - "finding": "No 128 cipher limit bug" - }, - "SSLv2": { - "port": "443", - "severity": "OK", - "finding": "not offered" - }, - "SSLv3": { - "port": "443", - "severity": "OK", - "finding": "not offered" - }, - "TLS1": { - "port": "443", - "severity": "INFO", - "finding": "not offered" - }, - "TLS1_1": { - "port": "443", - "severity": "INFO", - "finding": "is not offered" - }, - "TLS1_2": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "TLS1_3": { - "port": "443", - "severity": "INFO", - "finding": "not offered + downgraded to weaker protocol" - }, - "NPN": { - "port": "443", - "severity": "INFO", - "finding": "not offered" - }, - "ALPN": { - "port": "443", - "severity": "INFO", - "finding": "http/1.1" - }, - "cipherlist_NULL": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_aNULL": { - "port": "443", - "severity": "OK", - "cwe": "CWE-327", - "finding": "not offered" - }, - "cipherlist_EXPORT": { - "port": "443", - "severity": "CRITICAL", - "cwe": "CWE-327", - "finding": "offered" - }, - "cipherlist_LOW": { - "port": "443", - "severity": "HIGH", - "cwe": "CWE-327", - "finding": "offered" - }, - "cipherlist_3DES_IDEA": { - "port": "443", - "severity": "MEDIUM", - "cwe": "CWE-310", - "finding": "offered" - }, - "cipherlist_AVERAGE": { - "port": "443", - "severity": "LOW", - "cwe": "CWE-310", - "finding": "offered" - }, - "cipherlist_STRONG": { - "port": "443", - "severity": "OK", - "finding": "offered" - }, - "PFS": { - "port": "443", - "severity": "MEDIUM", - "finding": "No ciphers supporting (P)FS offered" - }, - "cipher_order": { - "port": "443", - "severity": "OK", - "finding": "server" - }, - "protocol_negotiated": { - "port": "443", - "severity": "OK", - "finding": "Default protocol TLS1.2" - }, - "cipher_negotiated": { - "port": "443", - "severity": "OK", - "finding": "AES256-GCM-SHA384 (your /usr/bin/openssl cannot show DH bits)" - }, - "cipherorder_TLSv1_2": { - "port": "443", - "severity": "INFO", - "finding": "AES256-GCM-SHA384 AES256-SHA256 AES256-SHA CAMELLIA256-SHA AES128-GCM-SHA256 AES128-SHA256 AES128-SHA SEED-SHA CAMELLIA128-SHA RC4-SHA RC4-MD5 DES-CBC3-SHA DES-CBC-SHA EXP1024-DES-CBC-SHA EXP1024-RC4-SHA EXP-DES-CBC-SHA EXP-RC4-MD5" - }, - "TLS_extensions": { - "port": "443", - "severity": "INFO", - "finding": "'renegotiation info/#65281' 'application layer protocol negotiation/#16'" - }, - "TLS_session_ticket": { - "port": "443", - "severity": "INFO", - "finding": "no -- no lifetime advertised" - }, - "SSL_sessionID_support": { - "port": "443", - "severity": "INFO", - "finding": "yes" - }, - "sessionresumption_ticket": { - "port": "443", - "severity": "INFO", - "finding": "not supported" - }, - "sessionresumption_ID": { - "port": "443", - "severity": "INFO", - "finding": "supported" - }, - "TLS_timestamp": { - "port": "443", - "severity": "INFO", - "finding": "random" - }, - "cert_numbers": { - "port": "443", - "severity": "INFO", - "finding": "1" - }, - "cert_Algorithm": { - "port": "443", - "severity": "OK", - "finding": "SHA256 with RSA" - }, - "cert_keySize": { - "port": "443", - "severity": "INFO", - "finding": "RSA 2048 bits" - }, - "cert_keyUsage": { - "port": "443", - "severity": "INFO", - "finding": "Digital Signature, Key Encipherment" - }, - "cert_extKeyUsage": { - "port": "443", - "severity": "INFO", - "finding": "TLS Web Server Authentication, TLS Web Client Authentication" - }, - "cert_serialNumber": { - "port": "443", - "severity": "INFO", - "finding": "041DA0D41B11DD0FE6963D44" - }, - "cert_fingerprintSHA1": { - "port": "443", - "severity": "INFO", - "finding": "sha1 4173134AFB85A0486926FADB1944F3EA6BF45B8B" - }, - "cert_fingerprintSHA256": { - "port": "443", - "severity": "INFO", - "finding": "sha256 EFD628F32052E30F5AAAE810ADBF1A22F58FD12F5E770C48937A1F1D893B2808" - }, - "cert": { - "port": "443", - "severity": "INFO", - "finding": "-----BEGIN CERTIFICATE----- MIIGuDCCBaCgAwIBAgIMBB2g1BsR3Q/mlj1EMA0GCSqGSIb3DQEBCwUAMGIxCzAJ BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTgwNgYDVQQDEy9H bG9iYWxTaWduIEV4dGVuZGVkIFZhbGlkYXRpb24gQ0EgLSBTSEEyNTYgLSBHMzAe Fw0yMzAyMTIyMzUxMDZaFw0yNDAzMTUyMzUxMDVaMIG7MR0wGwYDVQQPDBRQcml2 YXRlIE9yZ2FuaXphdGlvbjEXMBUGA1UEBRMOMTIwMC0wMS0wNTk2NjAxEzARBgsr BgEEAYI3PAIBAxMCSlAxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIEwVPc2FrYTEOMAwG A1UEBxMFT3Nha2ExIDAeBgNVBAoTF0RBSUtJTiBJTkRVU1RSSUVTLCBMVEQuMR0w GwYDVQQDExRzc28uYXNhLmRhaWtpbi5jby5qcDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAMN1BYZVfLW+o6H/qTT7U6d2aimCVt/04t0/qaQTe1mQtsJb 3eSsWoMzDpTij2JJUwPNygA1sRx9UG9oJNSEQcbWrsje00G5r25CAkEMrI0hO8S8 5hLyXtGhPga/yIG8D8DEv8oqzIMka2OE4V6GV34D6BgOLGjSFKw7Z4agnwjuqvwt Ox9DD2zr5fhiFkIMliTIkWovJWONBLRZKttuAU7HjDUhcuLc8hhucdOt4HtcrjEy dwMEPS1dKWMkZbGVMiLCX8X77AR34m5do37Akqlowkrs8aaj1g3hXli9PfXlpT/y mZVne00HOXfHfPfUYBsmm6b972Tf4WdoqhH4yFMCAwEAAaOCAxIwggMOMA4GA1Ud DwEB/wQEAwIFoDCBlgYIKwYBBQUHAQEEgYkwgYYwRwYIKwYBBQUHMAKGO2h0dHA6 Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzZXh0ZW5kdmFsc2hhMmcz cjMuY3J0MDsGCCsGAQUFBzABhi9odHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20v Z3NleHRlbmR2YWxzaGEyZzNyMzBVBgNVHSAETjBMMEEGCSsGAQQBoDIBATA0MDIG CCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5 LzAHBgVngQwBATAJBgNVHRMEAjAAMB8GA1UdEQQYMBaCFHNzby5hc2EuZGFpa2lu LmNvLmpwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAfBgNVHSMEGDAW gBTds+dtqC7oxU5uz3TmdTyUFc7oHTAdBgNVHQ4EFgQUHuBS8SatQ+XrzzUNpxEK l3MUc/UwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3AHPZnokbTJZ4oCB9R53m ssYc0FFecRkqjGuAEHrBd3K1AAABhkgKG+EAAAQDAEgwRgIhAMPZwOTdA1JRdWmL yiggEfGI+CiKinYXf5RuV4D2xJJYAiEA97sfmCby0uCkrC4KeQwv7+uZM16nxRMH sbAB1dhUm1oAdgDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYZI Ch66AAAEAwBHMEUCIASn62muO8cMCrKGZeF4NBI7rjTDC4Q6ld1A2QdVE7hvAiEA z4ZJ645mWRoULApzmyajLZNK7FvtXSoORxeM1Wy99j4AdgBIsONr2qZHNA/lagL6 nTDrHFIBy1bdLIHZu7+rOdiEcwAAAYZIChvQAAAEAwBHMEUCIC2tPmPjgBMzew5c UQT55FnFJyRvxwe9A0bFKL3Juh3WAiEA9czPILTEX/+CTDHRuQiFm0kfsOgN41nc lkGaP0tCpa8wDQYJKoZIhvcNAQELBQADggEBAJLL4911GcBvBihK3mN6azUAd6oP 9jswneKa9g8jIUju4/5ABMP7bD1dcLj/9a+UAEkXBr8KcDEjOwzty0AUFY6dsr5Q 7yho6X62NJqN1+OwJfAjt5wsn2rMKjWWaqQtko208cydpxLwsrJ+JbcSZIXoJbSw C+yuy007PHmKlsmeBGe54Q1gx0ipfkhjhos+0FhWhXYjfhWAzvHzU92CuM8zsqXG HsT5L812Vnj+/qvuKU/fEkNJEyCjMcASHnHnx710C5pfA655klt9jAiLNXFGVha7 LGegTs4f3OP7hatHtLUAYWb2Rk0TrHikgd7cxwZ6l52l1paJrIM1rikfdbs= -----END CERTIFICATE-----" - }, - "cert_commonName": { - "port": "443", - "severity": "OK", - "finding": "sso.asa.daikin.co.jp" - }, - "cert_commonName_wo_SNI": { - "port": "443", - "severity": "INFO", - "finding": "sso.asa.daikin.co.jp" - }, - "cert_subjectAltName": { - "port": "443", - "severity": "INFO", - "finding": "sso.asa.daikin.co.jp" - }, - "cert_caIssuers": { - "port": "443", - "severity": "INFO", - "finding": "GlobalSign Extended Validation CA - SHA256 - G3 (GlobalSign nv-sa from BE)" - }, - "cert_trust": { - "port": "443", - "severity": "OK", - "finding": "Ok via SAN (same w/o SNI)" - }, - "cert_chain_of_trust": { - "port": "443", - "severity": "OK", - "finding": "passed." - }, - "cert_certificatePolicies_EV": { - "port": "443", - "severity": "OK", - "finding": "yes" - }, - "cert_eTLS": { - "port": "443", - "severity": "INFO", - "finding": "not present" - }, - "cert_expirationStatus": { - "port": "443", - "severity": "OK", - "finding": "373 >= 60 days" - }, - "cert_notBefore": { - "port": "443", - "severity": "INFO", - "finding": "2023-02-13 00:51" - }, - "cert_notAfter": { - "port": "443", - "severity": "OK", - "finding": "2024-03-16 00:51" - }, - "cert_validityPeriod": { - "port": "443", - "severity": "INFO", - "finding": "No finding" - }, - "certs_countServer": { - "port": "443", - "severity": "INFO", - "finding": "2" - }, - "certs_list_ordering_problem": { - "port": "443", - "severity": "INFO", - "finding": "no" - }, - "cert_crlDistributionPoints": { - "port": "443", - "severity": "INFO", - "finding": "--" - }, - "cert_ocspURL": { - "port": "443", - "severity": "INFO", - "finding": "http://ocsp2.globalsign.com/gsextendvalsha2g3r3" - }, - "OCSP_stapling": { - "port": "443", - "severity": "LOW", - "finding": "not offered" - }, - "cert_mustStapleExtension": { - "port": "443", - "severity": "INFO", - "finding": "--" - }, - "DNS_CAArecord": { - "port": "443", - "severity": "LOW", - "finding": "--" - }, - "certificate_transparency": { - "port": "443", - "severity": "OK", - "finding": "yes (certificate extension)" - }, - "HTTP_status_code": { - "port": "443", - "severity": "INFO", - "finding": "404 Not Found ('/')" - }, - "HTTP_clock_skew": { - "port": "443", - "severity": "INFO", - "finding": "0 seconds from localtime" - }, - "HSTS": { - "port": "443", - "severity": "LOW", - "finding": "not offered" - }, - "HPKP": { - "port": "443", - "severity": "INFO", - "finding": "No support for HTTP Public Key Pinning" - }, - "banner_server": { - "port": "443", - "severity": "INFO", - "finding": "Apache" - }, - "banner_application": { - "port": "443", - "severity": "INFO", - "finding": "No application banner found" - }, - "cookie_count": { - "port": "443", - "severity": "INFO", - "finding": "1 at '/' (30x detected, better try target URL of 30x)" - }, - "cookie_secure": { - "port": "443", - "severity": "INFO", - "finding": "0/1 at '/' marked as secure" - }, - "cookie_httponly": { - "port": "443", - "severity": "INFO", - "finding": "0/1 at '/' marked as HttpOnly (30x detected, better try target URL of 30x)" - }, - "security_headers": { - "port": "443", - "severity": "MEDIUM", - "finding": "--" - }, - "banner_reverseproxy": { - "port": "443", - "severity": "INFO", - "cwe": "CWE-200", - "finding": "--" - }, - "heartbleed": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-0160", - "cwe": "CWE-119", - "finding": "not vulnerable, no heartbeat extension" - }, - "CCS": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-0224", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "ticketbleed": { - "port": "443", - "severity": "OK", - "cve": "CVE-2016-9244", - "cwe": "CWE-200", - "finding": "no session ticket extension" - }, - "ROBOT": { - "port": "443", - "severity": "OK", - "cve": "CVE-2017-17382 CVE-2017-17427 CVE-2017-17428 CVE-2017-13098 CVE-2017-1000385 CVE-2017-13099 CVE-2016-6883 CVE-2012-5081 CVE-2017-6168", - "cwe": "CWE-203", - "finding": "not vulnerable" - }, - "secure_renego": { - "port": "443", - "severity": "OK", - "cwe": "CWE-310", - "finding": "supported" - }, - "secure_client_renego": { - "port": "443", - "severity": "HIGH", - "cve": "CVE-2011-1473", - "cwe": "CWE-310", - "finding": "VULNERABLE, DoS threat" - }, - "CRIME_TLS": { - "port": "443", - "severity": "OK", - "cve": "CVE-2012-4929", - "cwe": "CWE-310", - "finding": "not vulnerable" - }, - "BREACH": { - "port": "443", - "severity": "MEDIUM", - "cve": "CVE-2013-3587", - "cwe": "CWE-310", - "finding": "potentially VULNERABLE, gzip HTTP compression detected - only supplied '/' tested" - }, - "POODLE_SSL": { - "port": "443", - "severity": "OK", - "cve": "CVE-2014-3566", - "cwe": "CWE-310", - "finding": "not vulnerable, no SSLv3" - }, - "fallback_SCSV": { - "port": "443", - "severity": "OK", - "finding": "no protocol below TLS 1.2 offered" - }, - "SWEET32": { - "port": "443", - "severity": "LOW", - "cve": "CVE-2016-2183 CVE-2016-6329", - "cwe": "CWE-327", - "finding": "uses 64 bit block ciphers" - }, - "FREAK": { - "port": "443", - "severity": "CRITICAL", - "cve": "CVE-2015-0204", - "cwe": "CWE-310", - "finding": "VULNERABLE, uses EXPORT RSA ciphers" - }, - "DROWN": { - "port": "443", - "severity": "OK", - "cve": "CVE-2016-0800 CVE-2016-0703", - "cwe": "CWE-310", - "finding": "not vulnerable on this host and port" - }, - "DROWN_hint": { - "port": "443", - "severity": "INFO", - "cve": "CVE-2016-0800 CVE-2016-0703", - "cwe": "CWE-310", - "finding": "Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://censys.io/ipv4?q=sha256 EFD628F32052E30F5AAAE810ADBF1A22F58FD12F5E770C48937A1F1D893B2808" - }, - "LOGJAM": { - "port": "443", - "severity": "OK", - "cve": "CVE-2015-4000", - "cwe": "CWE-310", - "finding": "not vulnerable, no DH EXPORT ciphers," - }, - "LOGJAM-common_primes": { - "port": "443", - "severity": "OK", - "cve": "CVE-2015-4000", - "cwe": "CWE-310", - "finding": "no DH key with <= TLS 1.2" - }, - "BEAST": { - "port": "443", - "severity": "OK", - "cve": "CVE-2011-3389", - "cwe": "CWE-20", - "finding": "not vulnerable, no SSL3 or TLS1" - }, - "LUCKY13": { - "port": "443", - "severity": "LOW", - "cve": "CVE-2013-0169", - "cwe": "CWE-310", - "finding": "potentially vulnerable, uses TLS CBC ciphers" - }, - "RC4": { - "port": "443", - "severity": "HIGH", - "cve": "CVE-2013-2566 CVE-2015-2808", - "cwe": "CWE-310", - "finding": "VULNERABLE, Detected ciphers: RC4-SHA RC4-MD5 EXP1024-RC4-SHA EXP-RC4-MD5" - }, - "cipher_x9d": { - "port": "443", - "severity": "INFO", - "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" - "finding": "x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384" - }, - "cipher_x3d": { - "port": "443", - "severity": "INFO", - "finding": "x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256" - }, - "cipher_x35": { - "port": "443", - "severity": "INFO", - "finding": "x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA" - }, - "cipher_x84": { - "port": "443", - "severity": "INFO", - "finding": "x84 CAMELLIA256-SHA RSA Camellia 256 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA" - }, - "cipher_x9c": { - "port": "443", - "severity": "INFO", - "finding": "x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256" - }, - "cipher_x3c": { - "port": "443", - "severity": "INFO", - "finding": "x3c AES128-SHA256 RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA256" - }, - "cipher_x2f": { - "port": "443", - "severity": "INFO", - "finding": "x2f AES128-SHA RSA AES 128 TLS_RSA_WITH_AES_128_CBC_SHA" - }, - "cipher_x96": { - "port": "443", - "severity": "INFO", - "finding": "x96 SEED-SHA RSA SEED 128 TLS_RSA_WITH_SEED_CBC_SHA" - }, - "cipher_x41": { - "port": "443", - "severity": "INFO", - "finding": "x41 CAMELLIA128-SHA RSA Camellia 128 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA" - }, - "cipher_x05": { - "port": "443", - "severity": "INFO", - "finding": "x05 RC4-SHA RSA RC4 128 TLS_RSA_WITH_RC4_128_SHA" - }, - "cipher_x04": { - "port": "443", - "severity": "INFO", - "finding": "x04 RC4-MD5 RSA RC4 128 TLS_RSA_WITH_RC4_128_MD5" - }, - "cipher_x0a": { - "port": "443", - "severity": "INFO", - "finding": "x0a DES-CBC3-SHA RSA 3DES 168 TLS_RSA_WITH_3DES_EDE_CBC_SHA" - }, - "cipher_x62": { - "port": "443", - "severity": "INFO", - "finding": "x62 EXP1024-DES-CBC-SHA RSA(1024) DES 56,exp TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA" - }, - "cipher_x09": { - "port": "443", - "severity": "INFO", - "finding": "x09 DES-CBC-SHA RSA DES 56 TLS_RSA_WITH_DES_CBC_SHA" - }, - "cipher_x64": { - "port": "443", - "severity": "INFO", - "finding": "x64 EXP1024-RC4-SHA RSA(1024) RC4 56,exp TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" - }, - "cipher_x08": { - "port": "443", - "severity": "INFO", - "finding": "x08 EXP-DES-CBC-SHA RSA(512) DES 40,exp TLS_RSA_EXPORT_WITH_DES40_CBC_SHA" - }, - "cipher_x03": { - "port": "443", - "severity": "INFO", - "finding": "x03 EXP-RC4-MD5 RSA(512) RC4 40,exp TLS_RSA_EXPORT_WITH_RC4_40_MD5" - }, - "clientsimulation-android_442": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-android_500": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA" - }, - "clientsimulation-android_60": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA" - }, - "clientsimulation-android_70": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-android_81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-android_90": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-android_X": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-chrome_74_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-chrome_79_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-firefox_66_win81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA" - }, - "clientsimulation-firefox_71_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA" - }, - "clientsimulation-ie_6_xp": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-ie_8_win7": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-ie_8_xp": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-ie_11_win7": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-ie_11_win81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-ie_11_winphone81": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA256" - }, - "clientsimulation-ie_11_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-edge_15_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-edge_17_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-opera_66_win10": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-safari_9_ios9": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-safari_9_osx1011": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-safari_10_osx1012": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-safari_121_ios_122": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-safari_130_osx_10146": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-apple_ats_9_ios9": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-java_6u45": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-java_7u25": { - "port": "443", - "severity": "INFO", - "finding": "No connection" - }, - "clientsimulation-java_8u161": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-java1102": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-java1201": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-openssl_102e": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-openssl_110l": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-openssl_111d": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-GCM-SHA384" - }, - "clientsimulation-thunderbird_68_3_1": { - "port": "443", - "severity": "INFO", - "finding": "TLSv1.2 AES256-SHA" - }, - "scanTime": { - "port": "443", - "severity": "INFO", - "finding": "321" - } - } -} \ No newline at end of file From 2248bca42ca62b568b1e28d3ee9a3475fa6d2b59 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 5 Jun 2023 12:23:50 +0200 Subject: [PATCH 104/209] Removed old file, not needed anymore --- configs/mitigations/POODLE.json | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 configs/mitigations/POODLE.json diff --git a/configs/mitigations/POODLE.json b/configs/mitigations/POODLE.json deleted file mode 100644 index 5fea2ed..0000000 --- a/configs/mitigations/POODLE.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Entry": { - "Name": "POODLE", - "ExtendedName": "Padding Oracle On Downgraded Legacy Encryption", - "CVE": "2014-3566", - "CVSS3": "3.4 (Low)", - "#comment": " AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:N/A:N ", - "Description": "By exploiting the missing validation of the padding bytes during decryption, an attacker is able to guess the session cookie. The attack is mounted by performing a MITM and requesting a SSLv3 connection between the client and the server. Once accepted, the attack is performed by modifying the padding in order to guess the cookie.", - "Mitigation": { - "Textual": "Disable SSLv3 protocol support.", - "Apache": "1. open your Apache configuration file (default: /etc/apache2/sites-available/default-ssl.conf);
2. search for the line starting with: SSLProtocol
- if it contains the substring +SSLv3, remove it;
- otherwise, add -SSLv3 at the end of the line.

N.B. restart the server by typing: sudo service apache2 restart.", - "Nginx": "1. In a default situation, you can edit your website configuration /etc/nginx/sites-enabled/default
(if you changed your site conf name /etc/nginx/sites-enabled/YOURSITECONFIGURATION);
2. Inside server {...} brackets configuration, find ssl_protocols;
3. Remove SSLv3 (if any). Make sure you have atleast another TLS protocol. If you can't find ssl_protocols you should be fine if your NGINX is updated.


N.B. restart the server by typing: sudo service nginx restart.
" - - } - }, - "#comment": " https://nvd.nist.gov/vuln/detail/CVE-2014-3566 ", - "#comment1": " https://www.acunetix.com/vulnerabilities/web/the-poodle-attack-sslv3-supported/ ", - "#omit-xml-declaration": "yes" -} \ No newline at end of file From 25146e0df20ae2c25f38f526b32379548a2786ce Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 7 Jul 2023 10:11:00 +0200 Subject: [PATCH 105/209] Removed old file, not needed anymore --- configs/modules/server/poodle.json | 20 ------------ modules/server/poodle.py | 49 ------------------------------ 2 files changed, 69 deletions(-) delete mode 100644 configs/modules/server/poodle.json delete mode 100644 modules/server/poodle.py diff --git a/configs/modules/server/poodle.json b/configs/modules/server/poodle.json deleted file mode 100644 index 0a16a5f..0000000 --- a/configs/modules/server/poodle.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "input":[ - { - "name":"hostname", - "type":"str", - "description":"Hostname to analyze", - "required":"True" - } - ], - "description":"This modules runs the vulnerability check for POODLE.", - "path":"modules/server/poodle.py", - "class_name":"Poodle", - "output":[ - { - "name":"results", - "type":"dict", - "description":"results of the scan" - } - ] -} \ No newline at end of file diff --git a/modules/server/poodle.py b/modules/server/poodle.py deleted file mode 100644 index 7ec323c..0000000 --- a/modules/server/poodle.py +++ /dev/null @@ -1,49 +0,0 @@ -from modules.configuration.configuration_base import Parse_configuration_protocols -from modules.server.testssl_base import Testssl_base -from modules.stix.stix_base import Bundled -from utils.mitigations import load_mitigation - - -class Poodle(Testssl_base): - """ - Analysis of the poodle testssl results - """ - - conf = Parse_configuration_protocols(openssl="3.0.0", protocols={"SSLv3": "-"}) - stix = Bundled(mitigation_object=load_mitigation("POODLE")) - - def _set_mitigations(self, result: dict, key: str, condition: bool) -> dict: - """ - Sets the mitigations for the poodle results - - :param result: the result to set the mitigations in - :type result: dict - :param key: the key to set the mitigations for - :type key: str - :param condition: the condition to set the mitigations for - :type condition: bool - :return: the result with the mitigations - :rtype: dict - """ - condition = condition and (key == "POODLE_SSL" or key == "fallback_SCSV") - if condition: - result["mitigation"] = load_mitigation("POODLE") - return result if condition else {} - - # to override - def _set_arguments(self): - """ - Sets the arguments for the testssl command - """ - self._arguments = ["-O"] - - # to override - def _worker(self, results): - """ - The worker method, which runs the testssl command - - :param results: dict - :return: dict - :rtype: dict - """ - return self._obtain_results(results, ["POODLE_SSL", "fallback_SCSV"]) From 31c032252c504cc7d00554bb300c832525b99f63 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Sat, 16 Sep 2023 16:02:22 +0200 Subject: [PATCH 106/209] fix agid guidelines analysis --- modules/compliance/compliance_base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index e4405c9..9781e54 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1031,7 +1031,7 @@ def list_strings(self): print("Strings for guideline: ", guideline) # First I join the output from itertools.product using "_" then I prepend guideline_ to it and in the # end I join all the versions using "," - result = ",".join([guideline + "_" + "_".join(combination) for combination in combinations]) + result = ",".join([guideline + "-" + "-".join(combination) for combination in combinations]) print(result) else: print("Guideline ", guideline, " doesn't have any special version") @@ -1074,7 +1074,7 @@ def _fill_guidelines_versions(self): def is_valid(self, alias): if "_" not in alias and alias.upper() not in self._guidelines: raise ValueError(f"Alias {alias} not valid") - tokens = alias.split("_") + tokens = alias.split("-") guideline = tokens[0].upper() if guideline not in self._guidelines_versions: raise ValueError(f"Invalid guideline in alias: {alias}") @@ -1118,4 +1118,10 @@ def get_sheets_to_check(self, aliases): sheets_to_check[sheet][guideline] = version else: self.__logging.info(f"Skipping {guideline} in {sheet} because no version is available.") + to_remove = set() + for sheet in sheets_to_check.keys(): + if not sheets_to_check[sheet].keys(): + to_remove.add(sheet) + for sheet in to_remove: + del sheets_to_check[sheet] return sheets_to_check From 128d0e20ac0cd4d9c5cffe57cb5d71c3780cc5d3 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Sun, 17 Sep 2023 16:02:03 +0200 Subject: [PATCH 107/209] fix issue with aliases --- modules/compliance/compliance_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 9781e54..a7e0aa1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1029,7 +1029,7 @@ def list_strings(self): combinations = [combination for combination in itertools.product(*sets)] if combinations and combinations[0]: print("Strings for guideline: ", guideline) - # First I join the output from itertools.product using "_" then I prepend guideline_ to it and in the + # First I join the output from itertools.product using "-" then I prepend guideline_ to it and in the # end I join all the versions using "," result = ",".join([guideline + "-" + "-".join(combination) for combination in combinations]) print(result) @@ -1072,7 +1072,7 @@ def _fill_guidelines_versions(self): guideline_dict[i].add(version.upper()) def is_valid(self, alias): - if "_" not in alias and alias.upper() not in self._guidelines: + if "-" not in alias and alias.upper() not in self._guidelines: raise ValueError(f"Alias {alias} not valid") tokens = alias.split("-") guideline = tokens[0].upper() @@ -1100,7 +1100,7 @@ def get_sheets_to_check(self, aliases): if alias == "aliases": self.list_aliases() self.is_valid(alias) - tokens = alias.split("_") + tokens = alias.split("-") guideline = tokens[0].upper() tokens.append("") for i, sheet in enumerate(self._sheets_versions_dict): From 94ec6afeaba2450c5d394203ca264dbd270b2656 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 2 Oct 2023 16:36:17 +0200 Subject: [PATCH 108/209] ConditionParser refactor and fix "this OR" conditions --- DatabaseFiller/guidelines.xlsx | Bin 101688 -> 102555 bytes DatabaseFiller/output.prisma | 14 + DatabaseFiller/requirements.db | Bin 1146880 -> 1159168 bytes configs/compliance/requirements.db | Bin 1146880 -> 1159168 bytes modules/compliance/compliance_base.py | 426 +----------------- .../compliance/wrappers/conditionparser.py | 422 +++++++++++++++++ 6 files changed, 443 insertions(+), 419 deletions(-) create mode 100644 modules/compliance/wrappers/conditionparser.py diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index 7c64d0008e4bae37ab9990689c42e36448b6bd27..f87617f217c15529d5102f7dc0603e9458e3d683 100644 GIT binary patch delta 64886 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 Dq>rwi delta 64366 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 KS5 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

9CDV6wX7q%WoIiPe_=(JZ`HfiS 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 e2*B%kmGsib*IY! diff --git a/DatabaseFiller/output.prisma b/DatabaseFiller/output.prisma index 116e317..da9409d 100644 --- a/DatabaseFiller/output.prisma +++ b/DatabaseFiller/output.prisma @@ -43,6 +43,7 @@ model Guideline { ExtensionBSI ExtensionBSI[] ExtensionANSSI ExtensionANSSI[] ExtensionAGID ExtensionAGID[] + ExtensionMOZILLA ExtensionMOZILLA[] GroupsNIST GroupsNIST[] GroupsBSIFEDERALAPPLICATIONS GroupsBSIFEDERALAPPLICATIONS[] GroupsBSICUSTOMERFACING GroupsBSICUSTOMERFACING[] @@ -131,6 +132,7 @@ model Extension { ExtensionBSI ExtensionBSI[] ExtensionANSSI ExtensionANSSI[] ExtensionAGID ExtensionAGID[] + ExtensionMOZILLA ExtensionMOZILLA[] } model Groups { @@ -587,6 +589,18 @@ model ExtensionAGID { @@id([id, name]) } +model ExtensionMOZILLA { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") + + @@id([id, name]) +} + model GroupsNIST { id Int guideline Guideline @relation(fields: [guidelineName], references: [name]) diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index f0a7000329090a06a79f98535ff5f3c2224ee790..8fc761af35e1254b5f5be539483ecd4b72ea7d42 100644 GIT binary patch delta 36547 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 N*JH%sou&S?{{fWjD`Efu diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index f0a7000329090a06a79f98535ff5f3c2224ee790..f60cd0a8ef7ad2f61992f14093b7a5680ad6fee1 100644 GIT binary patch delta 36915 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 diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index a7e0aa1..ad73a76 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,5 +1,5 @@ -import datetime import itertools +import json import pprint import re from pathlib import Path @@ -7,6 +7,7 @@ from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration from modules.compliance.wrappers.certificateparser import CertificateParser +from modules.compliance.wrappers.conditionparser import ConditionParser from modules.compliance.wrappers.db_reader import Database from modules.server.wrappers.testssl import Testssl from utils.database import get_standardized_level @@ -23,217 +24,6 @@ def convert_signature_algorithm(sig_alg: str) -> str: return sig_alg.replace("-", "_").replace("+", "_").lower() -class ConditionParser: - _logical_separators = ["and", "or"] - # simple regex to find all occurrences of the separators - _splitting_regex = "|".join(_logical_separators) - # same as above but also captures the separators - _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" - - def __init__(self, user_configuration): - self.__logging = Logger("Condition parser") - self.expression = "" - self._user_configuration = user_configuration - self.instructions = load_configuration("condition_instructions", "configs/compliance/") - self._custom_functions = CustomFunctions(user_configuration) - self.entry_updates = {} - self._enabled = None - self._operators = { - "and": lambda op1, op2: op1 and op2, - "or": lambda op1, op2: op1 or op2, - } - - @staticmethod - def _partial_match_checker(field_value, name): - """ - Iters through field_value to check if name is contained in any of them - :param field_value: iterator to search - :param name: name to search - :return: True if the element is contained in the iterator - :rtype: bool - """ - enabled = False - for element in field_value: - if name in element: - enabled = True - break - return enabled - - @staticmethod - def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition="", - certificate_index="1"): - """ - Checks if a field is enabled in the user configuration - :param user_configuration: the configuration in which the data should be searched - :param config_field: the field of the configuration containing the target data - :param name: the value to search - :param entry: the database entry (only the first two elements are checked, they are needed for KeyLengths) - :param partial_match: Default to false, if True the - :param condition: Default to "", the condition that the field has. - :type condition: str - :param certificate_index: Default to "1", the certificate to check - :type certificate_index: str - :return: - """ - field_value = user_configuration.get(config_field, None) - check_first = None - - if condition: - check_first = ConditionParser.get_check_first(condition) - - enabled = False - if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): - # Protocols case - enabled = field_value.get(name, None) - if enabled is None: - enabled = True if "all" in field_value else False - - elif isinstance(field_value, dict) and field_value.get("1"): - # Certificate case - cert_data = field_value.get(certificate_index, {}) - enabled = name in cert_data - print(cert_data, name) - - elif isinstance(field_value, dict): - # Extensions and transparency case - if name.isnumeric(): - # Iana code case - enabled = name in field_value - else: - enabled = name in field_value.values() - if not enabled and partial_match: - enabled = ConditionParser._partial_match_checker(field_value.values(), name) - - elif field_value and isinstance(field_value, set) and isinstance(list(field_value)[0], tuple): - # KeyLengths case - enabled = entry[:2] in field_value - if not enabled and check_first: - for field in field_value: - if field[0] == entry[0] and str(field[1])[:check_first] == str(entry[1])[:check_first]: - enabled = True - - elif isinstance(field_value, list) or isinstance(field_value, set): - enabled = name in field_value - if not enabled and partial_match: - enabled = ConditionParser._partial_match_checker(field_value, name) - if not enabled and check_first: - enabled = name[:check_first] in field_value - - return enabled - - @staticmethod - def _prepare_to_search(field, to_search): - new_to_search = to_search - if field == "TLS": - new_to_search = "TLS " + to_search.strip() - return new_to_search - - @staticmethod - def get_check_first(condition: str): - check_first = None - conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) - for condition in conditions: - condition = condition.strip() - if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: - check_first = condition.split(" ")[1] - if check_first.isdecimal(): - check_first = int(check_first) - # If the condition value isn't a number it doesn't become an int and the validator gives the error. - Validator().int(check_first) - return check_first - - def _closing_parenthesis_index(self, start): - count = 0 - for i, c in enumerate(self.expression[start:]): - if c == "(": - count += 1 - elif c == ")": - count -= 1 - if count == 0: - return i + start - - def _solve(self, start, finish): - to_solve = self.expression[start: finish + 1] - - while "(" in to_solve: - # So that I'm sure that there aren't any parenthesis in the way - starting_index = to_solve.index("(") + start - end_index = self._closing_parenthesis_index(starting_index) - 1 - replacement = self._solve(starting_index + 1, end_index) - to_replace = self.expression[starting_index:end_index + 2] - to_solve = to_solve.replace(to_replace, replacement) - tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) - tokens = [token.strip() for token in tokens] - for i, token in enumerate(tokens): - next_token = tokens[i + 1] if i < len(tokens) - 1 else None - to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) - tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) - tokens = [token for token in tokens if token] - while len(tokens) >= 3: - first_instruction = tokens.pop(0).strip() == "True" - logical_operation = self._operators[tokens.pop(0).lower()] - second_instruction = tokens.pop(0).strip() == "True" - result = logical_operation(first_instruction, second_instruction) - # After calculating the result it is inserted at the beginning of the tokens list to substitute the three - # removed elements - tokens.insert(0, str(result)) - return tokens[0] - - def _evaluate_condition(self, condition, next_condition=None): - """ - Evaluates a condition and returns if it is True or False - :param condition: condition to evaluate - :type condition: str - :return: "True" or "False" accordingly - :rtype: bool - """ - negation = False - if condition[0] == "!": - condition = condition[1:] - negation = True - condition = condition.strip() - if condition in ["True", "False"]: - return condition - if condition not in self.instructions and \ - (" " not in condition and condition.split(" ")[0] not in self.instructions): - self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") - return "False" - tokens = condition.split(" ") - field = tokens[0] - to_search = self._prepare_to_search(field, tokens[-1]) - config_field = self.instructions.get(field) - if config_field and config_field.startswith("FUNCTION"): - assert config_field[8] == " " - args = { - "data": to_search, - "enabled": self._enabled, - "tokens": tokens[1:], - "next_condition": next_condition - } - result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) - else: - # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use - # (None, None) - enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) - result = enabled if not negation else not enabled - return result - - def input(self, expression, enabled): - self.expression = expression - self._enabled = enabled - - def run(self, expression, enabled): - if expression: - self.input(expression, enabled) - return self.output() - - def output(self): - solution = self._solve(0, len(self.expression)) == "True" - self.entry_updates = self._custom_functions.entry_updates.copy() - self._custom_functions.reset() - return solution - - class Compliance: def __init__(self): self._custom_guidelines = None @@ -299,7 +89,7 @@ def input(self, **kwargs): :type kwargs: dict :Keyword Arguments: - * *guidelines_to_check* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated + * *guidelines* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used * *apache* (``bool``) -- Default to True, if false nginx will be used @@ -331,8 +121,10 @@ def input(self, **kwargs): self.prepare_configuration(self._config_class.configuration) if hostname and self._validator.string(hostname) and hostname != "placeholder": test_ssl_output = self.test_ssl.run(**{"hostname": hostname, "one": True}) - + #with open("unitn.json", "r") as f: + # test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) + if output_file and self._validator.string(output_file): if self._apache: self._config_class = ApacheConfiguration() @@ -562,6 +354,7 @@ def _retrieve_entries(self, sheets_to_check, columns): Given the input dictionary and the list of columns updates the entries field with a dictionary in the form sheet: data. The data is ordered by name """ + self.__logging.info("Retrieving entries from database") entries = {} tables = [] for sheet in sheets_to_check: @@ -795,211 +588,6 @@ def output(self): return self._config_class.configuration_output() -class CustomFunctions: - def __init__(self, user_configuration): - self._user_configuration = user_configuration - self._validator = Validator() - self._entry_updates = {"levels": [], "notes": []} - self._operators = { - ">": lambda op1, op2: op1 > op2, - "<": lambda op1, op2: op1 < op2, - ">=": lambda op1, op2: op1 >= op2, - "<=": lambda op1, op2: op1 <= op2, - "==": lambda op1, op2: op1 == op2, - "!=": lambda op1, op2: op1 != op2, - "in": lambda op1, op2: op1 in op2, - "not in": lambda op1, op2: op1 not in op2, - } - self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" - - # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: - # function(**kwargs) -> bool - # kwargs are defined in the _evaluate_condition method of the ConditionParser class. - - def check_year(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True if the year indicated has already passed - :rtype: bool - :Keyword Arguments: - * *data* (``str``) -- Year to check - """ - year = kwargs.get("data", None) - if not year: - raise ValueError("No year provided") - self._validator.string(year) - # This means that the guideline document didn't define a limit to this condition - if year[-1] == "+": - return True - - actual_date = datetime.date.today() - parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") - return parsed_date.date() > actual_date - - def check_vlp(self, **kwargs): - status = kwargs.get("data", "").lower() == "true" - result = False - for version in range(3): - enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) - if enabled and not status: - result = True - self._entry_updates["levels"].append("must not") - # This final operation should give True only if both status and result are True or both are False - return (result and status) or not (result or status) - - def check_ca(self, **kwargs): - to_check = kwargs.get("data", None) - if not to_check: - raise ValueError("No year provided") - self._validator.string(to_check) - if " " in to_check: - tokens = to_check.split(" ") - if tokens[0] == "count": - count = self._count_ca() - op = tokens[1] - num = tokens[2] - self._validator.int(num) - return self._operators[op](count, num) - elif tokens[0] == "publicly": - certs_trust_dict = self._user_configuration.get("TrustedCerts", {}) - trusted = True - if not certs_trust_dict: - trusted = False - for cert in certs_trust_dict: - if certs_trust_dict[cert] != "passed.": - trusted = False - return trusted - - def _count_ca(self): - cas = set() - for field in self._user_configuration: - if field.startswith("cert_caIssuers"): - cas.add(self._user_configuration[field]["finding"]) - return len(cas) - - def check_this(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True if either the one calling or the other is enabled - :rtype: bool - :Keyword Arguments: - * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not - """ - enabled = kwargs.get("enabled", False) - second_condition = kwargs.get("next_condition", " ") - tokens = second_condition.split(" ") - field = tokens[0] - name = " ".join(tokens[1:]) - # only the first two fields of the entry matter, and entry is only needed for key lengths - entry_data = name.split(",") if "," in name else (None, None) - second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, - partial_match=True) - enabled = second_enabled or enabled - self._entry_updates["has_alternative"] = enabled - return enabled - - def add_notes(self, **kwargs): - note = " ".join(kwargs.get("tokens", [])) - self._entry_updates["notes"].append(note) - return True - - def check_key_type(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True always - :rtype: bool - :Keyword Arguments: - * *data* (``str``) -- The name of the algorithm that is using the condition - """ - note = "" - alg = kwargs.get("data", "").lower() - valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] - recommend_dsa = False - for cert in self._user_configuration["Certificate"]: - if cert.startswith("int"): - continue - cert_data = self._user_configuration["Certificate"][cert] - data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] - if cert_data["KeyAlg"] == "DH": - recommend_dsa = True - if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: - note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ - f"with an algorithm that isn't consistent with the public key" - - if note: - self._entry_updates["notes"].append(note) - if recommend_dsa: - self._entry_updates["levels"].append("recommended") - return True - - def check_value(self, **kwargs): - tokens = kwargs.get("tokens", []) - config_field = tokens.pop(0) - tokens_string = " ".join(tokens) - tokens = re.split(self._operators_regex, tokens_string) - tokens = [t.strip() for t in tokens if t] - value = tokens[0] - operator = tokens[1] - name = "".join(tokens[2:]) - # if there is a "[" and a corresponding "]" then the text inside is a level of a dictionary - levels = re.findall(r"\[(.*?)]", name) - if not levels: - levels = [name] - field = self._user_configuration.get(config_field, {}) - last_level = [levels[-1]] if "," not in levels[-1] else levels[-1].split(",") - last_level = map(str.strip, last_level) - # used the in because in this way is easier to edit if needed - if config_field in ["Certificate", "CertificateExtensions"]: - result = True - for cert in field: - for level in last_level: - levels[-1] = level - # I pass to the function that gets the value the certificate dictionary as field - configuration_value = self._get_configuration_field(field.get(cert, {}), levels) - reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" - partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result or len(configuration_value) == 0: - self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") - result = result and partial_result - else: - result = True - for level in last_level: - levels[-1] = level - configuration_value = self._get_configuration_field(field, levels) - partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result: - self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") - result = result and partial_result - return result - - @staticmethod - def _get_configuration_field(field, levels): - for level in levels: - field = field.get(level, {}) - return field - - def check_dict_value(self, **kwargs): - self.check_value(**kwargs) - - def check_year_in_days(self, **kwargs): - # todo implement this - return True - - @staticmethod - def always_true(**kwargs): - return True - - @property - def entry_updates(self): - return self._entry_updates - - def reset(self): - self._entry_updates = {"levels": [], "notes": []} - - class AliasParser: def __init__(self): self.__logging = Logger("Compliance module") diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py new file mode 100644 index 0000000..304257b --- /dev/null +++ b/modules/compliance/wrappers/conditionparser.py @@ -0,0 +1,422 @@ +import datetime +import re + +from utils.loader import load_configuration +from utils.logger import Logger +from utils.validation import Validator + + +class ConditionParser: + _logical_separators = ["and", "or"] + # simple regex to find all occurrences of the separators + _splitting_regex = "|".join(_logical_separators) + # same as above but also captures the separators + _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + # mapping from field indicator used in the conditions to the field of the configuration dictionary + + def __init__(self, user_configuration): + self.__logging = Logger("Condition parser") + self.expression = "" + self._user_configuration = user_configuration + self.instructions = load_configuration("condition_instructions", "configs/compliance/") + self._custom_functions = CustomFunctions(user_configuration) + self.entry_updates = {} + self._enabled = None + self._operators = { + "and": lambda op1, op2: op1 and op2, + "or": lambda op1, op2: op1 or op2, + } + + @staticmethod + def _partial_match_checker(field_value, name): + """ + Iters through field_value to check if name is contained in any of them + :param field_value: iterator to search + :param name: name to search + :return: True if the element is contained in the iterator + :rtype: bool + """ + enabled = False + for element in field_value: + if name in element: + enabled = True + break + return enabled + + @staticmethod + def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition="", + certificate_index="1"): + """ + Checks if a field is enabled in the user configuration + :param user_configuration: the configuration in which the data should be searched + :param config_field: the field of the configuration containing the target data + :param name: the value to search + :param entry: the database entry (only the first two elements are checked, they are needed for KeyLengths) + :param partial_match: Default to false, if True the + :param condition: Default to "", the condition that the field has. + :type condition: str + :param certificate_index: Default to "1", the certificate to check + :type certificate_index: str + :return: + """ + field_value = user_configuration.get(config_field, None) + check_first = None + + if condition: + check_first = ConditionParser.get_check_first(condition) + + enabled = False + if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): + # Protocols case + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + + elif isinstance(field_value, dict) and field_value.get("1"): + # Certificate case + cert_data = field_value.get(certificate_index, {}) + enabled = name in cert_data + print(cert_data, name) + + elif isinstance(field_value, dict): + # Extensions and transparency case + if name.isnumeric(): + # Iana code case + enabled = name in field_value + else: + enabled = name in field_value.values() + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value.values(), name) + + elif field_value and isinstance(field_value, set) and isinstance(list(field_value)[0], tuple): + # KeyLengths case + enabled = entry[:2] in field_value + if not enabled and check_first: + for field in field_value: + if field[0] == entry[0] and str(field[1])[:check_first] == str(entry[1])[:check_first]: + enabled = True + + elif isinstance(field_value, list) or isinstance(field_value, set): + enabled = name in field_value + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value, name) + if not enabled and check_first: + enabled = name[:check_first] in field_value + return enabled + + @staticmethod + def _prepare_to_search(field, to_search): + new_to_search = to_search + if field == "TLS": + new_to_search = "TLS " + to_search.strip() + return new_to_search + + @staticmethod + def get_check_first(condition: str): + check_first = None + conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) + for condition in conditions: + condition = condition.strip() + if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: + check_first = condition.split(" ")[1] + if check_first.isdecimal(): + check_first = int(check_first) + # If the condition value isn't a number it doesn't become an int and the validator gives the error. + Validator().int(check_first) + return check_first + + def _closing_parenthesis_index(self, start): + count = 0 + for i, c in enumerate(self.expression[start:]): + if c == "(": + count += 1 + elif c == ")": + count -= 1 + if count == 0: + return i + start + + def _solve(self, start, finish): + to_solve = self.expression[start: finish + 1] + + while "(" in to_solve: + # So that I'm sure that there aren't any parenthesis in the way + starting_index = to_solve.index("(") + start + end_index = self._closing_parenthesis_index(starting_index) - 1 + replacement = self._solve(starting_index + 1, end_index) + to_replace = self.expression[starting_index:end_index + 2] + to_solve = to_solve.replace(to_replace, replacement) + tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) + tokens = [token.strip() for token in tokens] + for i, token in enumerate(tokens): + next_token = tokens[i + 1] if i < len(tokens) - 1 else None + to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) + tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) + tokens = [token for token in tokens if token] + while len(tokens) >= 3: + first_instruction = tokens.pop(0).strip() == "True" + logical_operation = self._operators[tokens.pop(0).lower()] + second_instruction = tokens.pop(0).strip() == "True" + result = logical_operation(first_instruction, second_instruction) + # After calculating the result it is inserted at the beginning of the tokens list to substitute the three + # removed elements + tokens.insert(0, str(result)) + return tokens[0] + + def _evaluate_condition(self, condition, next_condition=None): + """ + Evaluates a condition and returns if it is True or False + :param condition: condition to evaluate + :type condition: str + :return: "True" or "False" accordingly + :rtype: bool + """ + negation = False + if condition[0] == "!": + condition = condition[1:] + negation = True + condition = condition.strip() + if condition in ["True", "False"]: + return condition + if condition not in self.instructions and \ + (" " not in condition and condition.split(" ")[0] not in self.instructions): + self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") + return "False" + tokens = condition.split(" ") + field = tokens[0] + to_search = self._prepare_to_search(field, tokens[-1]) + config_field = self.instructions.get(field) + if config_field and config_field.startswith("FUNCTION"): + assert config_field[8] == " " + args = { + "data": to_search, + "enabled": self._enabled, + "tokens": tokens[1:], + "next_condition": next_condition + } + result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) + else: + # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use + # (None, None) + enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) + result = enabled if not negation else not enabled + return result + + def input(self, expression, enabled): + self.expression = expression + self._enabled = enabled + + def run(self, expression, enabled): + if expression: + self.input(expression, enabled) + return self.output() + + def output(self): + solution = self._solve(0, len(self.expression)) == "True" + self.entry_updates = self._custom_functions.entry_updates.copy() + self._custom_functions.reset() + return solution + + +class CustomFunctions: + def __init__(self, user_configuration): + self._user_configuration = user_configuration + self._validator = Validator() + self._entry_updates = {"levels": [], "notes": []} + self._operators = { + ">": lambda op1, op2: op1 > op2, + "<": lambda op1, op2: op1 < op2, + ">=": lambda op1, op2: op1 >= op2, + "<=": lambda op1, op2: op1 <= op2, + "==": lambda op1, op2: op1 == op2, + "!=": lambda op1, op2: op1 != op2, + "in": lambda op1, op2: op1 in op2, + "not in": lambda op1, op2: op1 not in op2, + } + self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" + + # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: + # function(**kwargs) -> bool + # kwargs are defined in the _evaluate_condition method of the ConditionParser class. + + def check_year(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if the year indicated has already passed + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- Year to check + """ + year = kwargs.get("data", None) + if not year: + raise ValueError("No year provided") + self._validator.string(year) + # This means that the guideline document didn't define a limit to this condition + if year[-1] == "+": + return True + + actual_date = datetime.date.today() + parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") + return parsed_date.date() > actual_date + + def check_vlp(self, **kwargs): + status = kwargs.get("data", "").lower() == "true" + result = False + for version in range(3): + enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) + if enabled and not status: + result = True + self._entry_updates["levels"].append("must not") + # This final operation should give True only if both status and result are True or both are False + return (result and status) or not (result or status) + + def check_ca(self, **kwargs): + to_check = kwargs.get("data", None) + if not to_check: + raise ValueError("No year provided") + self._validator.string(to_check) + if " " in to_check: + tokens = to_check.split(" ") + if tokens[0] == "count": + count = self._count_ca() + op = tokens[1] + num = tokens[2] + self._validator.int(num) + return self._operators[op](count, num) + elif tokens[0] == "publicly": + certs_trust_dict = self._user_configuration.get("TrustedCerts", {}) + trusted = True + if not certs_trust_dict: + trusted = False + for cert in certs_trust_dict: + if certs_trust_dict[cert] != "passed.": + trusted = False + return trusted + + def _count_ca(self): + cas = set() + for field in self._user_configuration: + if field.startswith("cert_caIssuers"): + cas.add(self._user_configuration[field]["finding"]) + return len(cas) + + def check_this(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if either the one calling or the other is enabled + :rtype: bool + :Keyword Arguments: + * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not + """ + enabled = kwargs.get("enabled", False) + second_condition = kwargs.get("next_condition", " ") + tokens = second_condition.split(" ") + field = tokens[0] + name = " ".join(tokens[1:]) + # only the first two fields of the entry matter, and entry is only needed for key lengths + entry_data = name.split(",") if "," in name else (None, None) + second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, + partial_match=True) + enabled = second_enabled or enabled + self._entry_updates["has_alternative"] = enabled + return enabled + + def add_notes(self, **kwargs): + note = " ".join(kwargs.get("tokens", [])) + self._entry_updates["notes"].append(note) + return True + + def check_key_type(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True always + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- The name of the algorithm that is using the condition + """ + note = "" + alg = kwargs.get("data", "").lower() + valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] + recommend_dsa = False + for cert in self._user_configuration["Certificate"]: + if cert.startswith("int"): + continue + cert_data = self._user_configuration["Certificate"][cert] + data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] + if cert_data["KeyAlg"] == "DH": + recommend_dsa = True + if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: + note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ + f"with an algorithm that isn't consistent with the public key" + + if note: + self._entry_updates["notes"].append(note) + if recommend_dsa: + self._entry_updates["levels"].append("recommended") + return True + + def check_value(self, **kwargs): + tokens = kwargs.get("tokens", []) + config_field = tokens.pop(0) + tokens_string = " ".join(tokens) + tokens = re.split(self._operators_regex, tokens_string) + tokens = [t.strip() for t in tokens if t] + value = tokens[0] + operator = tokens[1] + name = "".join(tokens[2:]) + # if there is a "[" and a corresponding "]" then the text inside is a level of a dictionary + levels = re.findall(r"\[(.*?)]", name) + if not levels: + levels = [name] + field = self._user_configuration.get(config_field, {}) + last_level = [levels[-1]] if "," not in levels[-1] else levels[-1].split(",") + last_level = map(str.strip, last_level) + # used the in because in this way is easier to edit if needed + if config_field in ["Certificate", "CertificateExtensions"]: + result = True + for cert in field: + for level in last_level: + levels[-1] = level + # I pass to the function that gets the value the certificate dictionary as field + configuration_value = self._get_configuration_field(field.get(cert, {}), levels) + reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result or len(configuration_value) == 0: + self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") + result = result and partial_result + else: + result = True + for level in last_level: + levels[-1] = level + configuration_value = self._get_configuration_field(field, levels) + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result: + self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") + result = result and partial_result + return result + + @staticmethod + def _get_configuration_field(field, levels): + for level in levels: + field = field.get(level, {}) + return field + + def check_dict_value(self, **kwargs): + self.check_value(**kwargs) + + def check_year_in_days(self, **kwargs): + # todo implement this + return True + + @staticmethod + def always_true(**kwargs): + return True + + @property + def entry_updates(self): + return self._entry_updates + + def reset(self): + self._entry_updates = {"levels": [], "notes": []} From e387939a24a9e2826cf8591092b3bb70dbca36bb Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 2 Oct 2023 16:53:50 +0200 Subject: [PATCH 109/209] implemented check_year_in_days --- modules/compliance/compliance_base.py | 2 -- modules/compliance/wrappers/conditionparser.py | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index ad73a76..d732954 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -121,8 +121,6 @@ def input(self, **kwargs): self.prepare_configuration(self._config_class.configuration) if hostname and self._validator.string(hostname) and hostname != "placeholder": test_ssl_output = self.test_ssl.run(**{"hostname": hostname, "one": True}) - #with open("unitn.json", "r") as f: - # test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) if output_file and self._validator.string(output_file): diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index 304257b..a3f9ca8 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -1,4 +1,5 @@ import datetime +import pprint import re from utils.loader import load_configuration @@ -76,7 +77,6 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match # Certificate case cert_data = field_value.get(certificate_index, {}) enabled = name in cert_data - print(cert_data, name) elif isinstance(field_value, dict): # Extensions and transparency case @@ -407,8 +407,19 @@ def check_dict_value(self, **kwargs): self.check_value(**kwargs) def check_year_in_days(self, **kwargs): - # todo implement this - return True + for cert in self._user_configuration["Certificate"]: + if cert.startswith("int"): + continue + cert_data = self._user_configuration["Certificate"][cert] + validity = cert_data["validity"] + data = kwargs.get("data", None) + if not data: + raise ValueError("No amount of years provided") + if not data.isnumeric(): + raise ValueError("Amount of years must be a number") + years = int(data) + days = years * 365 + return validity.days < days @staticmethod def always_true(**kwargs): From c7c386d4ab97a9812c9a6172503547231db4aed2 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 3 Oct 2023 17:29:30 +0200 Subject: [PATCH 110/209] updated conditions for protocols checks --- DatabaseFiller/database_filler.py | 34 ++++++++++++++++++++--------- DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py index e438d0e..4ea9e0a 100755 --- a/DatabaseFiller/database_filler.py +++ b/DatabaseFiller/database_filler.py @@ -255,17 +255,31 @@ def fill_extra_table(sheet_name: str) -> bool: table_columns_count = len(cur.execute(f"PRAGMA table_info({table})").fetchall()) entries = values_dict[table] - # This is to prevent the "this or X" condition to appear in tables that don't need it - # this condition checks if the guideline has multiple versions for this sheet - if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: - for entry in entries: - entry = entries[entry] - # Since the problem is a condition, and it only verifies if there are four elements. - # Last element is the condition - # Second to last is the level - if len(entry) > 3 and pd.notna(entry[-1]): - if entry[-2][-1] != "°": + # # This is to prevent the "this or X" condition to appear in tables that don't need it + # # this condition checks if the guideline has multiple versions for this sheet + # if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: + # for entry in entries: + # entry = entries[entry] + # # Since the problem is a condition, and it only verifies if there are four elements. + # # Last element is the condition + # # Second to last is the level + # print(entry) + # if len(entry) > 3 and pd.notna(entry[-1]): + # if entry[-2][-1] != "°": + # entry[-1] = None + last_level = None + + # This is to prevent the "this or X" condition to appear in tables that don't need it, only works + # for the case of Protocol sheet and only if the conditions are in adjacent lines + if table.startswith("Protocol"): + for index, entry in entries.items(): + # skip first element + if index == 0: + continue + if len(entry) > 3 and pd.notna(entry[-1]) and pd.notna(entries[index-1][-1]): + if entry[-2] != entries[index-1][-2]: entry[-1] = None + entries[index-1][-1] = None if not values_groups.get(table): values_groups[table] = [] diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index 8fc761af35e1254b5f5be539483ecd4b72ea7d42..7eadd5f73591a5b7e4e2688809b7f2938a248f84 100644 GIT binary patch delta 443 zcmZp8;MVZKZGsdN&yKPZMCg?%)7IWSpJ{vdeCFH)#_!vI{%2-Q zVEnQDe>}@ieKF2P1_lO3RtF{!#eWGza`9i`VPw%46faIqE-*4ODKb>hR|wDrvWyJDYMK^oTfoTnfzOVC z|33dd{yF?r{4xA?K(n>@rzh4k$??drvM~xvD>JY-1$$0U@Mjiluba=by>32pRRZI$ z?LYrBvnDWp-~Kr{Yc+jJS7`T|wg9i^N_$NYZWqk{17Ed1Dm!0{(-#Jx;0L$n>PE3gSKVF7MKAL!V3 zk56@J8u6WK>2dbP28S-hj9^>GL}D~KKAyN9PsE1?24hBY!iZjs3`P1P=pSZ6UCu*{ z{>4K7fpr+v!pj1M~SU(K2sF zsO}U99zEm<#hk*2+zYFy1lCZMeOO!ekT>A^Wh14Tq`IKG{mgpxBⓈfleDwY7h9= z;3n>44I;DxT4fURU!}QZjrQ%BzevmYW7Xk2M3LrOE1=o}^X8O8ryIuyaFd3E0hlzm zEuh{8%$O#{a$;#%E-W`z4OT5yoit6|8I^Q|RUsQ?;(m%1Q(Oy6ZIxI2**rIjc}7K! z3P8bSj-~`k)LN!2Nn0{A?fi0^8z1hq-J9M6o2SLGqT77uX=+!DCi9#Z1^!P5uU#>ns8Q delta 901 zcmZ8eO-vI(6yDjsc1ycEZx*OEO6U?!mOoFM zI7mIn&xszXsdT)kob;$6{+?_+c|buiXlrGCTamOLzL(kezMnU~mT;98>TkuHrmi$6-8%`}sD1%?o^n zkMm)EoOf|C|j^S1$|HmT8vu zh9hXH6HM7Q5ZnY0K_hqxO#~moFKt_QCv%V&LPFB0;;pn%Omk0MdS3EMXf})>A$lx Date: Tue, 3 Oct 2023 17:47:18 +0200 Subject: [PATCH 111/209] added some debug logs and improved "this or" checks --- .../compliance/condition_instructions.json | 1 + modules/compliance/compare_one.py | 16 ++++++++---- modules/compliance/compliance_base.py | 10 ++++--- .../compliance/wrappers/conditionparser.py | 26 +++++++++++++++---- modules/compliance/wrappers/db_reader.py | 3 +++ 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index e9f1b51..bc062a7 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -1,4 +1,5 @@ { + "PROTOCOLS": "Protocol", "EXTENSION": "Extension", "CIPHER": "CipherSuite", "TRANSPARENCY": "Transparency", diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 6eed1b5..cb4be44 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -37,13 +37,24 @@ def _worker(self, sheets_to_check): valid_condition = True if condition: valid_condition = self._condition_parser.run(condition, enabled) + enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) + self._logging.debug(f"Condition: {condition} - enabled: {enabled}") if self._condition_parser.entry_updates.get("levels"): levels = self._condition_parser.entry_updates.get("levels") levels.insert(0, level) to_use = self.level_to_use(levels) level = levels[to_use] + has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") + + note = "" + if has_alternative and not enabled and isinstance(condition, str) and\ + condition.count(" ") > 1: + parts = entry[condition_index].split(" ") + # Tokens[1] is the logical operator + note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + if has_alternative or additional_notes: # This is to trigger the output condition. This works because I'm assuming that "THIS" is only # used in a positive (recommended, must) context. @@ -51,11 +62,6 @@ def _worker(self, sheets_to_check): # if it has multiple name_columns they get only shown in the output name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) - note = "" - if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: - parts = entry[condition_index].split(" ") - # Tokens[1] is the logical operator - note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" if additional_notes: note += "\nNOTE:" note += "\n".join(additional_notes) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index d732954..fdfbfd0 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -32,7 +32,7 @@ def __init__(self): self._security = True self._input_dict = {} self._database_instance = Database() - self.__logging = Logger("Compliance module") + self._logging = Logger("Compliance module") self._last_data = {} self._output_dict = {} self._user_configuration = {} @@ -114,7 +114,7 @@ def input(self, **kwargs): try: self._config_class = ApacheConfiguration(actual_configuration) except Exception as e: - self.__logging.debug( + self._logging.debug( f"Couldn't parse config as apache: {e}\ntrying with nginx..." ) self._config_class = NginxConfiguration(actual_configuration) @@ -352,7 +352,7 @@ def _retrieve_entries(self, sheets_to_check, columns): Given the input dictionary and the list of columns updates the entries field with a dictionary in the form sheet: data. The data is ordered by name """ - self.__logging.info("Retrieving entries from database") + self._logging.info("Retrieving entries from database") entries = {} tables = [] for sheet in sheets_to_check: @@ -436,12 +436,14 @@ def _evaluate_entries(self, sheets_to_check, original_columns): enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], entry, condition=condition) valid_condition = self._condition_parser.run(condition, enabled) + enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) + self._logging.debug(f"Condition: {condition} - enabled: {enabled}") if self._condition_parser.entry_updates.get("levels"): potential_levels = self._condition_parser.entry_updates.get("levels") level = potential_levels[self.level_to_use(potential_levels)] has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") - if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: + if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: parts = condition.split(" ") # Tokens[1] is the logical operator notes[-1] += f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index a3f9ca8..61ab384 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -1,5 +1,4 @@ import datetime -import pprint import re from utils.loader import load_configuration @@ -13,10 +12,12 @@ class ConditionParser: _splitting_regex = "|".join(_logical_separators) # same as above but also captures the separators _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + _sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") + __logging = Logger("Condition parser") + # mapping from field indicator used in the conditions to the field of the configuration dictionary def __init__(self, user_configuration): - self.__logging = Logger("Condition parser") self.expression = "" self._user_configuration = user_configuration self.instructions = load_configuration("condition_instructions", "configs/compliance/") @@ -102,15 +103,24 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = ConditionParser._partial_match_checker(field_value, name) if not enabled and check_first: enabled = name[:check_first] in field_value + else: + ConditionParser.__logging.warning(f"Invalid field: {config_field}, for name: {name}") return enabled @staticmethod def _prepare_to_search(field, to_search): new_to_search = to_search - if field == "TLS": + if field.lower().startswith("protocol"): new_to_search = "TLS " + to_search.strip() return new_to_search + @staticmethod + def prepare_field(field): + # this step is needed for the upper-case letters + field = field.replace("Certificate", "Certificate ").title() + field = field.replace(" ", "") + return ConditionParser._sheet_mapping.get(field, field) + @staticmethod def get_check_first(condition: str): check_first = None @@ -184,7 +194,7 @@ def _evaluate_condition(self, condition, next_condition=None): tokens = condition.split(" ") field = tokens[0] to_search = self._prepare_to_search(field, tokens[-1]) - config_field = self.instructions.get(field) + config_field = self.instructions.get(field.upper()) if config_field and config_field.startswith("FUNCTION"): assert config_field[8] == " " args = { @@ -194,6 +204,9 @@ def _evaluate_condition(self, condition, next_condition=None): "next_condition": next_condition } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) + elif config_field is None: + self.__logging.warning(f"Invalid field: {field} in expression: {self.expression}. Returning False") + result = False else: # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use # (None, None) @@ -312,14 +325,17 @@ def check_this(self, **kwargs): enabled = kwargs.get("enabled", False) second_condition = kwargs.get("next_condition", " ") tokens = second_condition.split(" ") - field = tokens[0] + field = tokens[0].title() name = " ".join(tokens[1:]) # only the first two fields of the entry matter, and entry is only needed for key lengths entry_data = name.split(",") if "," in name else (None, None) + # The field must be prepared + field = ConditionParser.prepare_field(field) second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, partial_match=True) enabled = second_enabled or enabled self._entry_updates["has_alternative"] = enabled + self._entry_updates["is_enabled"] = enabled return enabled def add_notes(self, **kwargs): diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index f36b469..d74c63a 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -2,6 +2,7 @@ import utils.database as db_utils from utils.loader import load_configuration +from utils.logger import Logger class Database: @@ -17,6 +18,7 @@ def __init__(self, file: str = database_file): self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") self.table_names = [table[0] for table in self.cursor.fetchall()] self.__input_dict = {} + self._logging = Logger("Database wrapper") def get_table_name(self, sheet, standard_name, version=""): """ @@ -84,6 +86,7 @@ def output(self, columns="*"): if other_filter: query += " " + other_filter self.cursor.execute(query) + self._logging.debug(query) return self.cursor.fetchall() def run(self, tables, join_condition="1==1", other_filter="", columns="*"): From 7c7634988588d10d9f549b4da61b65cf632419f1 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 5 Oct 2023 10:24:12 +0200 Subject: [PATCH 112/209] fixed issue with logical operators inside conditions --- DatabaseFiller/guidelines.xlsx | Bin 102555 -> 102582 bytes DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes .../compliance/condition_instructions.json | 1 + configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes modules/compliance/compare_one.py | 2 +- .../compliance/wrappers/conditionparser.py | 5 +++-- 6 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index f87617f217c15529d5102f7dc0603e9458e3d683..d9f7e59bde04a74ea6a11565f4efb8bba11f6ca8 100644 GIT binary patch delta 39174 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 Z!14>s&M?W=M4$m(ESRQPU}3#v{sHr)H&p-t diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index bc062a7..2293c68 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -5,6 +5,7 @@ "TRANSPARENCY": "Transparency", "TLS": "Protocol", "CERTSIG": "CertificateSignature", + "CERTIFICATEEXTENSIONS": "CertificateExtensions", "YEAR": "FUNCTION check_year", "YEARS": "FUNCTION check_year_in_days", "VLP": "FUNCTION check_vlp", diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 7eadd5f73591a5b7e4e2688809b7f2938a248f84..7b298988e39fdf89dc76bd3fe819555ac7f3db45 100644 GIT binary patch delta 1116 zcmZ8fO>7%g5Z@X)hcOL)s*Gr zFRR=+vZW4tl_}rdQZJOKitEbZR^L~wkw_1!Dq*r+3=DAk;QxRk+h;u>><3~h`CYcx z0|SEo?eBX|LD~Mn6Kweb6#Q9hy8%Idm7O6<1-U2ParzBtzl=~aNnUa4_xKZzYf(L@ zqj2|>k0?9Kc*;&%#Z2H1X2pd&8z%V@w60Gn-6GXFHT^RO)i~*2@q{6to&}A zCs=b`iV?&JVQA8I!`gITOo5-kfgHr}PY6QPq8f?CV=E*uacpwRp3RnLoOeFg|>vt8n378xPjMv!?`-BzP zAx8DPIPt13pZri?#ltD73PKudAR!mq^FNqf>kBQSu`K&#kFW6;K zS}4xtPvsZI;+!p($}^{JQOt>_MZQo*POn>DI`x)4yZHS4;>66`w#en}`D`Mjk?7bn zrV>OyXQiMII+=Cdlw)W0TQ_R7&bg!+)`is)YnF^qT83^LouP{fppw>&?vU5yy(Tod zxzX>mF0iotq90yH!Zb%hol#z+8NgKW()52-3DCR>UAWRR_^u_sE|xs=7FNx3obnCI zvB!bdXiuP^k?9gsAQ_?3jgo4AzlgepQ6-hpc-6*YRFa>dQi8va5D-X zz=M$*3BCBygP{ixU>Z6X6Nv{r83-D^m`I`r4`A^lVzIi;VuHoP`)A+0|8L$uyF0LO zWMJV)Y6}{@U46(oD(yXa>~VKC;`G8#_|A2PKZ1&&Uy`KPIqQrX2sBFY0iHnyu0jXw zht2c{eM6_{9eSB|&=$IfddLcSOP-LsML53}sM0BZngHc*5*6yUYv8 zbW=gz^{?fZ3i7=;c8Jh?`$EMlk_&_@Wf^&R(R6qi{`UU>@#2U*P|F8C8GYu(35UnP zrSfz8CWRN@*}ziyKmaS*qUA8qB471mY@kovYR0cYE!1KZLoMmaMYfY$5AZ7ZtZU*W z{s_xL5zGsfe#88fj-G@1YlGXw8pB%oo=Uk2xd&~~S;nJ^HGT$M$hF~ur??2Jw0czl zU$`}4=lNP@NLvxCId!pM{R3|j*^tDoS=%db@LqFh_|3*JF63JaL%3D2fyy#q*s`!F zrzk~~$}P*Kc2)!=cn=RD2?6mx7~nd9738b(ol9>s-Brrr6K%29qv;mY9of+&D{i0H z*K@oz)|N<~z1W}ZYintZ8D_#b^w&2$W4c9Eh}y(wOs$!1B{dSffV(gN?a%-Y`iMr! z5}779NIR(!8y$qdyr~T#ce|=0Z|zn+neM)&MMyvqZNqL|avqGerUx%+mFeh&rt^jo zZFZXDPEvyLC}Bf!l8;j*%#}I1>K~t^Mt+iDmc0Fm(#yFma z667-k8yu&Y%(#+*-@x&*I3CXsmX9-g6lEnC%K*;eunied@{i3?yVb>p6k}6ZFZsta Z!14>s&M?W=M4$m(ESRQPU}3#v{sHr)H&p-t diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index cb4be44..c66829f 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -63,7 +63,7 @@ def _worker(self, sheets_to_check): name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) if additional_notes: - note += "\nNOTE:" + note += "\nNOTE: " note += "\n".join(additional_notes) if self._output_dict[sheet].get(name) is not None: self._output_dict[sheet][name] += note diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index 61ab384..c27b0f1 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -7,7 +7,7 @@ class ConditionParser: - _logical_separators = ["and", "or"] + _logical_separators = [" and ", " or "] # simple regex to find all occurrences of the separators _splitting_regex = "|".join(_logical_separators) # same as above but also captures the separators @@ -164,7 +164,7 @@ def _solve(self, start, finish): tokens = [token for token in tokens if token] while len(tokens) >= 3: first_instruction = tokens.pop(0).strip() == "True" - logical_operation = self._operators[tokens.pop(0).lower()] + logical_operation = self._operators[tokens.pop(0).lower().strip()] second_instruction = tokens.pop(0).strip() == "True" result = logical_operation(first_instruction, second_instruction) # After calculating the result it is inserted at the beginning of the tokens list to substitute the three @@ -206,6 +206,7 @@ def _evaluate_condition(self, condition, next_condition=None): result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) elif config_field is None: self.__logging.warning(f"Invalid field: {field} in expression: {self.expression}. Returning False") + self.__logging.debug(f"Tokens: {tokens}") result = False else: # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use From 61fc4debcf39331961bcae1172e0808aaf5b0d0e Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 9 Oct 2023 10:20:34 +0200 Subject: [PATCH 113/209] Added SCSV check and fixed minor issues with conditions --- DatabaseFiller/guidelines.xlsx | Bin 102582 -> 104516 bytes DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes .../compliance/condition_instructions.json | 5 ++++- configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes modules/compliance/compare_one.py | 2 +- modules/compliance/compliance_base.py | 4 +++- .../compliance/wrappers/conditionparser.py | 12 ++++++++++-- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index d9f7e59bde04a74ea6a11565f4efb8bba11f6ca8..c7f70ef3ee856618b94dd5e659addfb699b416bd 100644 GIT binary patch delta 63238 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

HR+~e3Slk;B&WV7)B&dP@rGKu-zvs+aT<@v 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 I?`_Kb4cGF0=l}o! diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index 2293c68..1c0baf1 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -3,8 +3,10 @@ "EXTENSION": "Extension", "CIPHER": "CipherSuite", "TRANSPARENCY": "Transparency", + "GROUPS": "Groups", "TLS": "Protocol", "CERTSIG": "CertificateSignature", + "CERTIFICATESIGNATURE": "CertificateSignature", "CERTIFICATEEXTENSIONS": "CertificateExtensions", "YEAR": "FUNCTION check_year", "YEARS": "FUNCTION check_year_in_days", @@ -14,5 +16,6 @@ "NOTE": "FUNCTION add_notes", "CHECK_KEY_TYPE": "FUNCTION check_key_type", "CHECK_ONLY_FIRST": "FUNCTION always_true", - "VALUE": "FUNCTION check_value" + "VALUE": "FUNCTION check_value", + "VERIFY_SCSV": "FUNCTION verify_scsv" } \ No newline at end of file diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 7b298988e39fdf89dc76bd3fe819555ac7f3db45..a87126dc0436ede97955f440fff368a27b0d2e5b 100644 GIT binary patch delta 2859 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 I?`_Kb4cGF0=l}o! diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index c66829f..2edc100 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -47,7 +47,7 @@ def _worker(self, sheets_to_check): has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") - + self._condition_parser.entry_updates = {} note = "" if has_alternative and not enabled and isinstance(condition, str) and\ condition.count(" ") > 1: diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index fdfbfd0..725b3f1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -291,7 +291,7 @@ def prepare_testssl_output(self, test_ssl_output): # The supported groups are available as a list in this field elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ - else actual_dict["finding"] + else [actual_dict["finding"]] self._user_configuration["Groups"] = values # The transparency field describes how the transparency is handled in each certificate. @@ -325,6 +325,8 @@ def prepare_testssl_output(self, test_ssl_output): elif field in self.misc_fields: self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] + elif field == "fallback_SCSV": + self._user_configuration["fallback_SCSV"] = actual_dict["finding"] def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index c27b0f1..680b544 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -74,7 +74,9 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match if enabled is None: enabled = True if "all" in field_value else False - elif isinstance(field_value, dict) and field_value.get("1"): + # the extensions are saved using their IANA id as key in the dictionary, so I have to check that "1" is a dict + elif isinstance(field_value, dict) and field_value.get("1") and \ + isinstance(field_value["1"], dict): # Certificate case cert_data = field_value.get(certificate_index, {}) enabled = name in cert_data @@ -110,7 +112,7 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match @staticmethod def _prepare_to_search(field, to_search): new_to_search = to_search - if field.lower().startswith("protocol"): + if field.lower().startswith("protocol") or field.lower().startswith("tls"): new_to_search = "TLS " + to_search.strip() return new_to_search @@ -438,6 +440,12 @@ def check_year_in_days(self, **kwargs): days = years * 365 return validity.days < days + def verify_scsv(self, **kwargs): + scsv_finding = self._user_configuration.get("fallback_SCSV", "") + enabled = "offered" in scsv_finding and "not" not in scsv_finding + self._entry_updates["is_enabled"] = enabled + return True + @staticmethod def always_true(**kwargs): return True From 6b6cc0842e1f377177c07fcd3869f91d03062adc Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 2 Oct 2023 16:36:17 +0200 Subject: [PATCH 114/209] ConditionParser refactor and fix "this OR" conditions --- DatabaseFiller/guidelines.xlsx | Bin 101688 -> 102555 bytes DatabaseFiller/output.prisma | 14 + DatabaseFiller/requirements.db | Bin 1146880 -> 1159168 bytes configs/compliance/requirements.db | Bin 1146880 -> 1159168 bytes modules/compliance/compliance_base.py | 426 +----------------- .../compliance/wrappers/conditionparser.py | 422 +++++++++++++++++ 6 files changed, 443 insertions(+), 419 deletions(-) create mode 100644 modules/compliance/wrappers/conditionparser.py diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index 7c64d0008e4bae37ab9990689c42e36448b6bd27..f87617f217c15529d5102f7dc0603e9458e3d683 100644 GIT binary patch delta 64886 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 Dq>rwi delta 64366 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 KS5 zmzZw<@1T#zOd)#+M-Sp%pHP$L_eo|AsPc$kg=PTYzebXKY#Chrm*`0UN%Z(?S_VLL zJ_k~dpl~0aHo?&FCD5c0}Wf-wGtdd#^a<^|&; zxr(ReyG!JS9df#y_;AtwD@_4!3)Xuka2H*SzJ=6k{ z?Pf|JNbM}?6Nq2Li!ZYlwd?W)u&O}=C`dcdM6;=A)x5}AnLwl&+CIGZ^{4>H>9!?l zMw9%CrUzPtJ?H76{vqx`OLD7W0APJIhJc-;dnvve#Q?dO+8ry?N9)$$&X1we5!|hPR$KzHNGA)oAte!n>n7#RVrpdf=YiPY)|-^wKIY-pI5Q9 z+AkQgPQj*a;pcOyXE&RRg2z+4w*4=t`mer1YXeQ)#`{w}plc=Lxj8^_wrcINTvyv| zA2{9(!VXk0O@~oAJIs&UfLS*t$`|~2dclWGt(>RtA1RPcvltgh4SXszdTT|s-ZTP{ z7pX^xQbSP(?UEFrZHJO9IAzfO2Yc+Ji|k~WQDnak&f*SmgdMqAM+-?`7~=LFapr&z z4J!a?_awc&O@AAW*=V^7*w6^c1l6YxAb0sB*wCsI5}< z)o4$*k5Tv;g+foTjyvg3LHthZkrjbN1!v9ZDS%3u{o>Qp1!r|+Qe?tJO`(OGa5qh1 zxC{tr?Fy6?rry8_@At4+cFtZNvna-sWvMw_K-wULao4GT7(V`e#nHt@9i@kTn zy#W47bA#3c^WEjoK|ms&~GTxeTZzgmi1 z=`LHpRKq!T-PO+~oB;Mmo)Jd^m1G0S)2SQPs%^_ALmYH8TSW?qnYidrSA^}yO^tE= z9UVrMc;r_O5@0)N)zBjW(rm%a%_^yx^Sn}+Qk<+<)a%jq_vT8efb!FM4pPXZxNk=-2Q9i?Lz@+_?zbfwu9;NUhbJQ_#9*Lgv|` z?sig{e6XhO-on>l>4$E@`@6#=RaUH0f+n1@Q*y&C_788mm&SUGHg9qHasrQKnpyKW zJJmJ9u%z+5f+!xWoSZ4B>VXc5vNrR8vKc^40C;Uuzc;St=!Dk%x?4b<;Y(fXM>7UAKvz z=h5DUEu2b>UW;*_F>?{GTNC*4#=iqtjb4#=R*ApTu;;ha?{eDsTA@!s7~CZ|dp)}X z+zjKUm?>UM7V+_em*tWyrD(ZNP6IV<5LK0_{PUa--_<$1^`WJ=M@`ySHd>xPdgh=gx_~U;81HQ6! zF*0ByE6gK>kfFe>H9}QLM5x0=>I7sE2Xi2;;=bBk5Ur0y%tY!KBk&*xbIc==f8&~j z(ssk8sy5;xNWY_qIvT|nK*d=NlXY$i$97=(!Ak!UYITU1d&9?*w#*+e2Yw~g-BG3M z-0iJ3<`@U-kK-{t#YY5071Dc94c#dWr%^tpK@QJ7@}ZG>mKe>Wdd&wIS(FomIq)yk zUW9gD^wu`mAP$}ooUJ?!Y(4F0tn~#OMj*OWcpFxG+tb+`_cKie^CSs`CJS&SH9Z($ zor~gIWe->pqRE(A5&oJLSg3uH>UjDyN+)8!JK@pF8`;*|jt1S@R$)?f)uY7*3)8mT$c?_tt+S0M|caPF1wa|08BV?+4`g+Zsa(SxjD! zWn*#IW~Os{xsNvY4%-$EuAk`W$7{V`K~Pco(DSS!^iUS|&?gJ2TgS@+1{_;KS?Lb+ zH$opMsOfL=FQOpbfc9=3Q3aU^rwK4hXudd{pVD6|=Ik!FJPfEU$#oI@)l-iJJ%9%Z zMU<)`*F2gUmFUF)b+rNtZNpMsWhTa(j~QB~LwlwAYwjFn4s@n7pdK)Xf3&EhyRohTb0@ihx3M^@{VJYEnomH1-I&=O=)Y`_vQVK)I<7^}cJz>s)T*#5paFCdyiSL3sf*K#013l!KxvdWX6NP8-?5*8!BUY_ zb?3j(dVqqu0P{YgIo1hdMRBt`GIUSSbha9T+7EdZ-Hl+bjIoLCWIM@e!t~Ly*mNai ziH{|rjld?Xs2TCHmg*=Pr5Ip{=u@Syz|I0;!(fk2f85stg0ZuD`NTFC0Px*R$I+ap zH&G$j=bv=S)4!xLO4ZMk-U-fpMFJE4+CpT+6vAE31NseVGrBC3*Who)Zf8$~`CY%J z#e~aCCA^`sUR2ESl5IwjWIKyqU3e~9TFi=8bJ2F6QFi*{yBNxQ{YPP7w@V)YwiL55 z@(Tt2MfmU&)}TUd{I0Ax63|WKZWJr^9T#x4s;gq*4mu;Pb2d%L@X*o%EK|u2WpNvT z_GBqH=QUwD7n$j~aMHa;{oqn;dqNH6O6A@*rz6^Km1XuQ`&%wfM&gbp?wolv2cW%5 zj6F%*>PS{i;$i3}f~_eQI)j&#Yh)3phq?c5>CaM=p+_n)N7l`71UO!XB{Mr*ZY1?U zA2oAMJ3z{{0*x7mU3Ebi;f1`Ti3vv@DFK9C_`*#HL%e2_L*BiHz)2v7#sH5dF2=xl zA4uwoIdYSZy2DmqM>S6ua)SJO9J#r2@`U=i^732lU>2S~r&0;# z5Mw9oWA+Hr>IckWV-I9BpuALOVuiH7v2r`obJ(~GwAaN#wR49rT09)LtOrn5`5h2q z${$iPxkzFCJfM#%Mw}lA>PIp%R7m0-dFs5>u!+=fnZuVGJ`UQp=2bWJQu4@ldz zO=m(I{#zt^G+Vq#5<$bLC>L+p#5c*eXW-f6G!KdIDAW~a!;`_vaIkYEN2LwzI3Y9v zUYs^W)!pReg(Jtq>@h^Imx6%m6U+kn2XEf?`zJyCrHPV=$FQSz3Dj>GJerkk?%`BA8p(Lsiee+e?6?Ou+Hz(Pi{BOk!SLs5wc%VsH-PZ31zJB|z9Hju2S~j%(Lsv}QYi%!;!sg$r0X zNsRvNNLoF?pzBwpf{f%JVn3z)yD;0X3O?ZdA{0kb=EP$eV^)TkvS+H=FJvvAX7yKe zza_kb2bggONne!0rJ(xBu;=k80UJ*wu>Mlc{e{x6)I{V?qbPpBnS)OX2sPjgju$30 zv*&pobNWav?()AZv{Z@(u;+fe$fqA=p>wc-Dx@y%h}eCL^fs{A)Or3mg(*21LnC@D zAZfLfD@gtto;;O^xmBN$&{j_98d23$Kdl-_#bHs)WinQjO!}4jjA)tH= zKI)cwO*&8|E5_&TCgr}4I>I6Abc*Xj9xWyRqtrpgps<2tw_raA=`Xy)4XDG3ybhSd z3M0;Qg-88l9I|4-FurU)+>kJV$Zo09#)Y*n^!0x3BS%PWva;umJ`dF6E zN-o@k6vQzZKWQASjFLpcu0jG&>K%wQt0DuvG`=DOWZpd=d_s7vDs~G-;Z%_s_sV@l z(46@)>Wav;3)L}bLfCE!ohWHi(?WhCU2?xMH-=2UHTDX?Fj}q2S~#mfMlayd@xs}n zoL}?wuE&v?L8ji@;+)^J?fzHWf(P54jys1Tg{;=yENT%a+K~p+WP(Mwfah2Sd}68c z<1ItN;-8Llf#qJlcl^2H$*$9O9wx>FBOTI|rn~&&3H=h;%HSDHeH?1mhDO553~U_1 zwho0Dc4>o@T>6ibQAeHxlM>Q#*e^wW3K^}pS?QE{;jV?)L-089RhEWC!p_QR@4(9M zTpY4mud@NISbYoBqn=JWDf0322b_PMo$(ZCrt^>9i|PM|vlEL_)(=Yjm$UyxOwzIc za`wOILe}#Cpk!ekUkC$UHzwuYzD3pXTuG3N>6QfzU1Oea4;9^Jy15KP zuYXFlWO2YVyh>beiC?uh-?py~w0L>pbbEQx{OoS1x?wA4zq`)4> zK~B)?s3ZX*>7+7?o0Z;aEh`k1Yj7`7Ntz!4=dH|-cmzXNN?hP(vF&%2T+qmi6V$8e zgn{EEpP275d%v{pRlL~{J%(1YV>~YTMKw3Pe!&SLdJm(=DYtrzbY~z98Asih-%7r3 z&rEd7UJVs-uuRCing3j{lQpVvfixR5xTNg#+4u=K>tYm$Q3;Fu`dAjUnud)({QdaL zXR*VR-t$!>;H%BX=_l{U#%Uv@rLA*LnGPDE_wk@0bpGa#Gq>{RVsVm%2V8JOO@0E$ z)C44va4~cerxFr&%P=XqGf`HR>jxO+e&A{}P3h`^z?lG^XS9K}5l8)819}>SW#P&+ zaDI0Hag+H~yQDDeMQa$eCAUSireF9%S{oG$r||EuJjuHajRKqATkunX$in0~l8@kl zLIiktphP5#z;F0SlIjWuit*H;pjh8cRJiT*Zx@*h*Lco};$HR(5<_4wQVCXrT`KSiD5G`CIx@$qevMjA z_3zxtN;n~*(*39L#2H{eYyuy!s&Z{jh!p*lkd<1jQkzsoB$~Y01x#}el2-U)cxBiW zD@}xP7Y4OUW3mh#+#_wRWa>mnVZ5v=et@y%&wI-)Ba!8^CuWEFgX-WG1V02tSL|^5= zc@}NOT6;YQrz9p#e@7y645}O-3Pe(tH$yAg0_nw9#!X3*mM=9r)-SVBT9@0j8Az{C zfU4t9x_TON&C$7;3?k`=%u)Tw7&9vYD4FqqmQKBDO(j63=3ZXsjs?d@xzvD{6Xo0& z#d332;o=nXkYJW9)=BeFD(lm-hLm$yAd#<(C1fknQXlEJsv0UAJHopVsSiGlCA7ps zC~1y;h?2e(b4Riq+mU@FV6=v+j_RuNE0Fz4$XWO%8IGBIg`P$VOsx69rcS61XgnG@ zV+$?UbTanSoI&=l{vl*YVcWv6>}RB zVP!eGgeu7$nN~;jL|4Q!VSc7o@lGWgBr~W}eYK{QNXPuZ&0G0pr82NWZ!B!x|6ujHyn`$Od6&@0LMD5fbKj@1pkzU zg`GU_-bSiOU6~PG1;-%wrPs{dMA96``P<6ui4Jh=>|)8-$~Xe#Q}jS%`m8h0?AhVt z@k11&+c&Gi=VO;BB%D2!C3>p32L-k4Zan=8I^K+UwQ_NdADUd%ZdcoIfWw+^aWs+Y zB=}3*CRQQ!Q;II43FO7bZ|LP>I{pD^n5-BssRRky*gx2Y-PG!Mb=f4`ieyQ2{cIHW zWvjb3EVueJ7T^(lOo*eFtWAKEQXhm*nciwoY*$}fE9sd~h4^T{$Iqy)TZ^}B(US6qQ5-sd9okyF;F6~BK zbAHvGua*<#6xcNgU{!>ss4fd{FHYWrAf7i>zlbi=+RHCadE2uI1+*X2x8=|=K00}X z^LiEMt%z#vy1|FY++EeJpxh!!rgb$^Ptk1kKQpgsa&+^p{nB>jTiY}>6BEHbAJzV~ zB)w6~&VNv>KBkZwzlvZM=Jnl4eFcJtIXned^@}Z%j z*aDOEh88pgIPJ4HJS*a9R!wIn^>?>%m{FHQd827LprjIi#o50n0n1{f;VN+4MoCcc z(uOW?qw$bsLmQ+s8z?K*Z9?QCEzrQl(I+|4(44MZxaQVr0c_LPd%%rX!?~qd%s=hJ z)9zqjiw-OOk-oJH-wTPGq79i}q-hzv_t29qbS4|hPJ2Rd+w>D5T=cN|bio)d=UuH4 z(u}=_u}%kZupX@$vds(DYi*{mW_d9UCecH1I`55)5>hM#vP!X4!ZCK?0V~#HaKPQ;Um#n@MDIDK(0hs_gqItF>OVL zBR1?d?l&UCdA{+XOazc!O7P+BC1O`rA{F^z`4eg6K3VgqS(j##gx8OJUJ$uE*Cymi zFvpH&Uo;+8x3ogzy_?D$iO5( zfYfNWE`h1}ioTt>r?UkcO7y0nQK-#jL8ex9!-etNp6EBBvJR;5LYF?d-X-hXLmfUg ztzQP?yN(@IGIV`0^1@BsA-E@AJGbr03p$K*dOe)w!-X)VNOFL=m`|0U;U1h4fDv+CP#opJ`p$83f*1S8B9*thQ|Sb>5C+oAftbF{u@! zP8uDARRvB^R{^GoEw02887?+s&47PHk2dxi?N01m_@}5cd3Q4xn?M{}))aMs(*v2Rz<=h^R$gq*Clw*LWm@nAzz>UrEP^r27W{K6G~I&O*2Ef=LIO{P#P|z z0&ob@zQu(T{SKw8GIbAOWJHIYgM+(uN!=4l3pT*r-9fN$s$*S4a15>yTt#m@@*UVl zfzAnq-B$q-Y7Fn=LjNlH)n@}KewEbD@|$e#(YxaQoh6(Wn>M+OrCm9KKCo~loTn2- z(Jf{q%$N+7mj!0j4Go1ZuoWLWh=hl#5P+{Wt2ypevL`Bdqo}{mH3F+I(2?c;E#&=I z0_r5kB>0OGTfGXNMo-^w3P}YTh+pN>j`y>BQz_oYN_{Nbi^HTJYaIkW9M=NV5)oME zJ4Jm&dBGVuFk+HMzO@58Z&0T)%C-`A+S{9CxDebdGT{UllYhRdS&$$t>!NkYt_BEQ zV7pR{H7ZS@Gq2dzciM=My`WN6sE&-fE!M|GZ%>X8I?se9RHI;?jE)dj?cnT10<=hW zwQ6(1n-{A$4(ARlnV&i$4_V6!NG;(8IX3Fd8YN+SIhTLOP;_hR`oDfUvY6|Y%!Pel zyZ<@x__3eE+umV3l1`zRAY|!qC=Ot@c0u9P?C&!Db#4K6YjnE^ce@yGHzgMJh*Vu%<1ZPRRrEvL9OHd%zCa?pPIDXN$i!`OwtEnDLHMVR z1{H+TfZy!@Je(5{l3jxfmFfm$Zd$~r0@E;ot+#K-bI;>J&beyqO|(zgUSqu~jfSJk zy*CI^cWBgKvPjpZEgL?;-iu{#w$%-{cBM$aA#MhLQ_2o^EJ@?R(zi52=9iH3+;4B^ zjt7?4Xs+iJdNk7y&+0d}P)p&bqruRk$fC@z$G7guQNDRrCIO3=JHP@s(!#tu3)f-J zPn42nooFpIVmwxi_XX=XQmxd)ok(G`eXSr~M2E){xFBsl?#D6=Xg z{=z9}+--n~u@6Jb6g4}KZKN3=H zI`~qYMPRRSR?A1Z`eqA|OJjuM`V|2pY0()U1r=1sFePA8o*A2TYQN3E8gY8M^@uln zJ~le^=VPYHN~8pNRhssJ39t^^U)tYV7n&a7{Lc`L>yaTHXyB3Bt2%M^~@31 zR-H71xDD6UOHsIfu#~~@L7Y3h{T?gu?|WRdjRIgB5otfGBTRq50lvd+-C}*_Uga2A4MHdWu8nm+FLo@K+x~m_U_WBsdBrFOY;5a1oCq zU%Yk1@A`R`g8)AL5N#pjc8*qQVF~0JdJQVpd?f{5vUott)uFu(n+Q~dk7Fawg-HC- zdQ6&74(tsuAn^op*JSciA{DIj8$WV3+!HQ$UiNTZtz^dtoM@m0snZB}bX;TydTiEt zXup`N7#>3r$@K+9Qq?0rQ*Gyh-=_UY51t5$sB8PAcg-B-8)G>om=Wa)lb)@8NK~Oz zp!~LN3~}iS7ra`xR!LDpTE_6L%o-sE=_Sr(HlJHP1?BWSRoM2&KkqAQ=43A{{`?KV zSmi%YHInsgfQbR=Tu7hbPlQ+mSCxZY4k%C7Zc3CJ8!~oBap|q<5e^e*F^C6Q^Ll!~ zt{1vsN&4jUsXVOAL@OVzjhdBeeQDks8BP`O{MUUIjTkW{( zW2M;)A1MX93qKK@-u5q&rR1~sZw&B%Ef}KBd0SRfZ+ZjlGx#)uL$kXn)0s@jY2(NR zOo~_I9`i9sCM~6=?Mzd)_%#U2uemXn z(m-~IN9C`8igdROXb${TQ>s9~HC~Tz#l-|L#qiAW6hC#3_$&1JSib>kwcXg#rh~HV zdhaAkudA-B1x5vj%X^xpRU13Wgg>iD6qP@8?gs#Nj%E>>8uRazQwx zKm7WCAC!071emybIh{8ak%#GxoVDH96a>G`e)t@!PhCatJr{P3|M67zFWbhe3pF7lZpRHGsx#G_bdQ!9QOEwt;Kj%KQR{69X;USpDb7?D#}!7okcV1X!v=_`)FKr5C`1q|t5;9j`d=cZti$wi{iF(c zujhGX5VvfuEA#r)kiYb^LD#*qAMua;FldwOII!QdGt5GcA zd5=^%tRHbS2ok4;Iu=lfb-qzlkycyM3X4r zqQ#T(|0C`_z~SoNc5$6Vk1o1MMDHca5D8v238G{WL3E-MWfOwvoghlokRXU&BYG!M z!{{Y?H;kFHhvfaezwi70=ls9xoO7M)nrqCSz1F&)=YHx?2K;t z@kjZUZInRz@@XtN^9DAqXI8TAY4PBGs-^qSy6ww?%UYf z6Ecp4hEZDngG16Xk30G)ABS()s%Ljk9-Z2;gx;*>1H8x{(iXnINz<)+z1Z(rU@x8J zw~=8Q+Pj5ST5rkyV689}~NO^AS)EL_WZZ z6?2vfzOw$b#nKNWs9@?w^?RIx-pRMilD_A0A5-#N1>j}~g6qBAFD$s@_ObMxh6On8 zbtyua{ON$+QGzcVz2x#du9iOwUy9Lj>06yA6S^PrCJV`a`e_{hG?TC6T0vp?z?2#J zb0c_Y?cIpcd-jv<<(%t=ux_aC5AV8fFJI`sbbYIA-t=-aYb+%GUdxI0_t|?XEL^tf za@>nhIPM$8uUKZBHZSpU9-2`6S{DzbUlzR*@wRWv27nU3TAHs}YpK8y3V(Go%GODf zOtj2uM1zr}Q$V}(E_{nvm%DIHB0kWdFCZ(=lJMzbo&xu@tA!8P3{4^LclCvdL%$?v>N>0V|lAqm*beO^##)<(5s z^Jb65)$i^QHS}$zpG=nFNOyF0u^?%@R-fqdS4})p{7%trtPe)QwvCmavy1ihW;DP1 z{HlhtA;N8RSKyZAI#A;h$Qj4 z;dd7jd_}dySD;)Tt;^J~jpf%p26DddTgj71Yl zK8`8PV-N!tm^r)8yh^ZHa@s4gm`2F*+P3qnI!CRkFsL1pHm^V6gf?K5dX&`8M%1DR z_)8_mAZ_x`)MtALwP$@)kv^8sS!+vMi>{xg4c1D=D7Lt98_pn@Uhz!f_|W+fl$|LX zE_Y8j$a|KNFOyC^ZG_5{@m8*COC5M6!TCocBx5%=1bbfRNwf5}p7W;*SM<=W%+#`9^;#=kO)ri*X)ueq+ zr=s%wAieO6u%dZbK&D;<=cdM|1D5cHAH>)1dtL*G%Xt2B%FCyFkxIwNMRNKemyGeX z{oo4$F{+8BlykG9U3;TzIIZ^j2H%t6Ll{*pvp;`4@U6kkGu#?AEyc;WQ3 z-*fEM$&KP_L{oQCUO$PuIVY(~YH{R#;@;og+V$1hiPVBTz5M7!VvA&)QHgOib-~PqmMTIsaSCR18y_N4-NJiFQj$; z(l@EmM&r$>%jM;DQ!hOWE5wg>3VupOfTJ9$G%;BSezy4H)MX^!JmC2$l3J;oyK|51 zrmzk(kq+(+OzGgReYNv-&FyJD9jC=x7LZq0l0Udd7j3UJsg($v#WrF1QSwAD3LoNT z0pEXANPA#@&LDky^_uq*R{6NniHO(eo(MLw=4vQ=A1w(Z?@{YLj;r=JvoZOT1TeU1 zDFv0u$h+hg*`dD3Yk8j8)G&z}2_m87Rzi9lf#QT0xc+$dm<3nqkFSPezYTo`IeV0| zU1}Cubi$y>TC3~&@a@=J1G6_zZ0fVk;{^e}{_V#0hYF2riO^}N@U6)l-E;LSRBEj3 zcb9iDW!{Z6d**OVozNw-0`|j@-u#5c4qMsRxS?NhynEy8Lmcg^e!B#{x!Qz2Ho%f{3vqQf}kn>R-H4QNJgG7R{Rw6ZMQNb^5fHQoSI~6QM0dr zM3&I!zrJcyLFDGRPgaw97KHB+wCmvhsFkO!GuyeY+->bd=AX&%<4)e_px~hm9^azC zr@$A~@AsxlNpBK!la6R0%Np_Flg+pk%39VH_oo-~T#ZCgnjNzn^_5CWFi0olG;ovv<69(^Wn&fse4G3%%D7`B*VQXLgz@E3&*i3XykZGiBB zY%4oad8@!_IIoGG$Yvy|;2En|y2&A3aL`Z)6PzqH$g-NMf*Ef!xaBAS!%eAmE#Gi2 zoP6Z7xag>ZwMXEb3+;lesRC{GwvFv|yv;k9?Udu?&HlKN-i&d+iMNnOF3$rsutNFV z+H?Aob$WBIx2pYk^`M($$SGOSKm~a5_*&W*oQ}9z+j|VpORM4)BH|v*gh_~%n@KEX z-}|oBH^lrxBT7;xiuc#&Hm}}~@vk}NKI2$$wCJn8kiOEd_{~J7QX+~Y{+NwqT`=GK zBl&{7jHYOZceA9-@_}^ahYqoA!F`!8r5w`Kj>)Hwuide^!l-%If<3GJizL_$IKI}6 zuK~AI^{@pWgrmE98=nbp$Cjw~ix)_1(!qJKpqt>YR)$+8})lbyB3@x-^M8AkU- zg(J7_G32F#7i0KupdB&a7{URjmukzig8Ab3rg!j7eMypyGu!lLiQ~L3#;|P~y~g0s z>(#qFNg(l)A;d{=fLrR#*<=*QvK-skL$vYZ82 zuH4RO)zHiM?)jIUk>pgB_B;Ner%ddL#8%*ClJJjM`dv{{PrF9ENnmF%G1yJIVkHZk zl+knbO|XVc0ZjH(qi&E`zWknj&EsDVJALykC4FOJVF8uW6$@E1q>LV`oeG-L zF2Ce+^bIT=Zs1Kge;G^U#^kwnFDUi>)rps>)wlL2dZEndp^*C9U-$n^R(h-Tm?EDAF8=Z74fwt={yf%6&37I(jkx$S z2L<`j*JAL?QitI8HU9m+UPOrdDIvnGMh^QEuhuT`VcTjf?MQgMJV z@U%-?$$^#GhwxWmP5v`^n*|A+(EA34HLiOxtB@x(>xxSFZY3)R-FprK$*vK7cUvvI zi&kP(9bMn?ZyAeKt&2G)lh9-HV$ipuMB#bNGaXC>YZH4S&_V5pVN0VQ{sgS&+>6Mt>3bXiBo^w5uq zw!^s*f)WQ&)1ThwFC81b0B7WawYv{Wq;qp?Z^O3Mv;d`=atGV_$F7h;QfinGRxees3Nb)!_;^@%8aK zSvWd96l%{v2r(6VHe{dnIgJ=0pgn|yBp5lW@=*E&Os%wef2F? zd~>*`1!QA#@69xLGVJYd)`^Qlw)pvr-7_3Ww|t?%rZEEYxUMG^Hp8?<=uVD&xw~I= zhCjDDoyrbOFF=s%&yJ@I54Hn>g^*Kyef*>-K_Aa*@a(q5SX}@Nb$%w>wZypFvB!Kg z2ibCH%-Cq0DRECqav;^++L_%zAm_)XSIUE{I$-u&+YZIxrm>U88kmi-g9FmS{k${< zsGDBeLS$(t*(3_~&YT`kO^@XRz5s(Iq-TV4!05lc{+d5sLjeQ600IR|Zya_hiA*AdbGX%fJ5_Dh-E`LVt~=Q? zcW2~t>uKvZwv9B_KV0f_@}%I~W-{_0emS+2A_g7#Uc8oQ)9&$hpm=XBA^wT5VReDw z*5>N%pbigM#&kx5m$TRYRLf@Tk26+gwxwQTihAlRIb!ZT8J`pOC*BYLV8NDt6Zk@DgA9Atcgwc;;{%Nh=~mMg*~U)IyEKMz z(_fFeh7?KSrduqY2}<8y>6rv%roTk!rc!lT?|$PLG}9?FlI|O+C@Eu|DYx1SHIm*v zwOfX-b&YhiR_OQfw7TF9D9*jDSGt=*!;wC1_Twb!6ibsbE+ck~E#nu(zS)8;&A~e%XbKSAV>%ZgIx{f-(jp#4{NK`c1P z(^)O2l?N2Pabx$g3*MC7`tW9S?4GJM+jWm0u_>L6KR!)4saHIp;H_ETxc*&JlgWLf z$~UuF+&mE%51YQ}l)i9ETVVy+qcd}U-qpid08cICFsd}yY)^$Q@e}B!ccM9qPnno0iT~hnrd|t6B zt3K)nIXBD1CktyRm)`~dK@^U9r#c?uq713>aJBHRXq3m##a<{oTrWhTO@>jZ!)*yzn!;5Y%2ld|%c9N+^p(AuC&g&OLQ%*m zhWg@!Xd~#F?P-OpQhIrjINoCWa##B(ZaM9m8%BD6qio_d?DzgALMd#l=D4i+$QjxT z^(QcAp)e;I4jkr*<$b=z;vD=|N`3k2$S7HQ$XnqN=ePLX7U>cX;_6>J=dkB3@}>zh z3Vs>z$-X6gIKh1;52zV_#-P@!!4g4enx+t4lhc=f(}lQS-b*1Efje^BOJ`R1p7pCo zFNMBzypEC8RkON{D`82%xM4CNxAT8l>;53d33(We$z|NnS z%${8#50WZ|b7NjJZmE{wW*Fs=S*=@$ZYv2fb^w{fM11@8oh(*F>41Jra;%55-iX8*Ug0!3#3~djW97#o%~b5t0j3@&OaTuq*V`O zyPZ0`pnxf9@QNYWyPPvGM|rRB{U;WsuufpeIyzg2MTxEeBx!_Ftx@?So+{U4&sU}y zWx5!lfhl`zpXsma3+CRx#+k!G>C`D6miVBP6~;4p^>u|Z>!+Z*2Dq;)6t!+(T)h?l zyl5^qRP^2Kc>ad3zpka)6Y!Wy%ix;7s=-%|VDA8{D@hD{#Ccy5V&!gFi7F3sMdtt< zek7_`tQ;!kKjc&hqlejY%-n-H)p#HAW3$DJ+3 z5V=MB>6NCz-Stuo25JoDav~i*43f0S@1r& z8+Qk*&=OmxB9JSx^x+L{$H0hZuel6zLKEf0c`({M6~mIrbFbu1T_bC-1K;zb{!0KwsLcB zm#%?DJ6-il9qh1o5a8wa@_Vda#5GYGiL zs!f(*_NinKr$g42UZX(J?p+YYV>PQK9F0|iTOu9sE-@v!EXn?;%9^XUU6ilB3!=Q! z*VdtccL}_if~Yx5V3!!8jZ6CL z^{qRLZ!}sd-n~M<;8f@A0}rUc^`|3oq8}EK?{IRZh5{+r6$eYkC6I|8oA(-ltO$JV z{g>)=Ud+Yd$Yx<4Ok9XdC+WkK2Px*bOV=LkD9~~8VdBywas=(#wE6tYZu9y#lQPZ) z-wbBpR>FASl~n4M&XQP}6hi(EkNpm=qDu%lx-(--NOfI2RbZcaa1)@-=`ni?P5PB=78$q@B6oyzUgQ%PGqsq5^3p{wzg3~cKy)Oc?5S4WqPzN=!%PTiLG&t zODbF^cXe8>5)QAbOQ?=DzD;CH6x>}xY0H-iIbbQ?E1}fP6RV$Lez(k!-7bQw#TjP) zuuX{pFnegHO%q0mB4RlXcFhv7Gv68B&dvaN;JcuZp780Lv?n6e&5xF5-z&tfirg5& zM4hcFs*_vx-@YTEDj|hM^O+#=ep|gs%(I!ytT6hKQLEKwZShCaS6x1vxC||){ROom zUrhTT$wwm}@bvSLUmM)6*7^08i0A7NY7dBf$5~!QaPa7%rC(*j`M6h z8v9lIGH``bm;MDZU(S(A-1S+g)@T}q?JUoq-o!0FrNK6{WSKxX<>uV@u%H?7PaBaJ zCC^k8NM4kfyck#pPM<*6GO(e@{HAuRyNX}OED17rSV`zE<{0N}PZ3YSwjJY~lTT{t zFF4m!Rz}N$(z|w-7V!0o@ElrcgZbj(|Ne~UQvh11#F1qeM;_zU>W&sr1)*n2W{-R zH5LW2*41Zi*RFBh_p2x#8mz@*=1{Z>0A=)(Od=Q)Sjhgy)`n@bn$})nC*o54Oa+_7 zrP#!UZ=1Nmn)Y2!okB@!<28X-HHz5F^xny|xxc4a5?bb))VE3Wu;)ec?6NT(skG={ zU^o&>mRaoOO&0~FO$d9|ZGH*YnfW%CB@@$qyY*pR6sO$-bn1NF(_rjh?NZ*=FmzS~ zG)*T%KQK5=#0N1H7_=79A22)6>I9H1_7z;A8#2h2w;_K2<$h?)McGnbdUq$SD~{tE zt4+aMHQkwlsFaA86Y*}ZNxpxSxtY3Be|DwR}3ef7hVkFwdut#{wg6i{!W43)!PG{AA<#gcRb^l<1|#;pCIZSmCd7*4#{ zPM%69Iz4_+q>S_QqzXApn2jh;@qOyxVLWigso~%u!LFn&ZSxZq&@i2>qz7nq1#>a+ zX~%?U@go-767bSYQ$}^L6iLRcC7@vpB!BZgZqiJGJ(?CXl3e+W3m@@H*vW481+Nr4 zzTq6Y7wVxJv`r>0c|Kkgf5|{%=7{{Fr?YASy)RK*^SB1K&B9n?blOx zH0ehk;j4oTd9^|;>8DHc@c^+hTb z`P=b+t)w=20>0R|-(0&YB6WD7a$w2;Z)$IAt0=LhvYz*(_EYokR=i)uYKrT%bd&R* zUcH}*336RS?axpL;lUz~wh@&Bg&M|!QIm*SQ=bI}9jq`P^B6&i^A!Imt)|vzP`M5+ z4VDv2C5Swl{qTwOuopm`7WrT3)X$MOz|phyNT=s16hJO`O-Cm9#k78*CmCZ4R=7wF z9*RWl_5~YsV#tAs0Zz=bGz`Y;SRw1ZfQvJhcqH&_<|EI%n! zR4NNTQUWJd>JQag+eCr30%pXbaciO14>k5@ZQ9V8q2Ixz*f+IQLam$X9nj-dTw=~M zQLw_S*t^o*m<5pOI%e|`hKl`qmpAtAqPP9 zX)r5Yi7Mr>4&h$r<9+#kT@f@)8O+4Q#q2;kM8QZ1kr7-;W|q`qf%y$mkBOK92U1e@3$Lx-B1eW zqkx@M`vl7@jY!2hR`@z?c!1*iyb5-|U_^B~F|A}8puFTR=_FB8jNltfdqJ4@04IgQ zzO0~{U?=c8I@oFv9@0j^csj{q|y5xrx_hzize9X1zJ)2(`YPvler1o*lt&?31 z<120fnh3VORnI23x47dkq1>?i+5L>KM_N~`88hPe3X4^g>pr;(O zdVo&!yK;_<3XX%P^`@L8FEjXyw0z@|Qoyv=s z+C5GQux*$x=QPWdFzd5ed_)oWQF7ujTDpp3<#Bd{?og;zC*0o7k%M)?L@Xv0lH+xM zzlKS(Dhn(X$Mmy#dM$sE3T($uGv*abZr#jt`o%A?HHwclCX9vO*Y2e_n>lqdukuJ~ zb<`&FNy#e5*g_1CI66;_6vjcO*8FGQT~K8)`%-a)<&Y-Le;wOtoz5wMWI;y3e(+fw zrw=3W=h}!{!TCk81uY5J0xtf_Y4fj_?Y}a?FYOL@`<+9>68t@iZ<$@?&eqWm3L3LQ zUy^nY?%Zknb5Un*iIJY%ov@4LH-_&wChUh#-0g+6!z03J4E@WTrF>nqw^;WedR>$Z zyeNKkTRUjk<=wM{TM|dNy#U-&A(4T~&<{{Levis8q-iC$IyQMk1Zi{)=t`Y8J`3#x z8fu16KMs07kVd3kOw<=s_w;H)mb`zcPCEL+jHy zZUSbVDh}Xe0;LOo*}@;`vGK3cPsik+&=cyCG^6HQtpHrimwSW~#>^7Nni3hWi60Lm930&^!;-oU%z77#Q}eD#^QhtcD4cjC^S2PybpcV-8E`Kq(J-f-L45^yE{ zd|l)lSnRb(zuB{V>tEgmihjt|woP~kR5L}7qXz+7P=2$2k@7ueE=z^ebNIefOLEuk%ayA1E18#Y^=Ja}4~R@{;RESkj1t9X5L zHJGFW#2y}z)6C1om`(wBouJoSn4QEpofGvY=y_%fD5X+hxkb}nW8?SJZ)It*)@h3# zNv+1}{uL(RF3o@IQCi)r93Sv%;u+Tp66>cJ&QCG2pU?LbH$mh^*nZ;1I;P{4U$x!f ztk_$DenW*ZkvuE8S~0TPpqhp9BDS^~n{-1o;dpr$(0Rl}dGZTKZ3#;xsdoacu)hKo z^4KG0UnSg$rACtTFefJD#_5(Lql!W)SBgEMI;N8(^x9y`*5GT4325UrOw#BkO4+WH zC3NjLZlwr237MqTVHw)()LzNe58}!XANUwbng7wm9GutHmg0?@yMvm$@!GpHP0r+G zmww!BJ;KzV@cU@X!gEDS@v&HVN<0vgj;1-?r2AyHP}#AY-aAjw4xd;SR91Y4-+*G! zK&H)^vh0r^cgK#%cPBzlY;Sm6dBjAXk#+H}9i}U?0cuONoLVvO)g~rcwIZyftWWgY z;-Ax>Omv>miC8grSyUQW4$HDZuE%6V1oE5Ybq-w0IVv#2BySw(RnL*oB9;T!LD_hj zAV+zanGr;p@~THMm?D;4vTSnK$ulG9{lvI1CJ9(<(FlbgKG{ND-)8lwT#rh|T{*D+Mj-+vi&9D{CPbN7)nv?D%wsnGnDuJh+(NV{r>%CHdffds)&Q`Q_FU~| zWk0@ln?%30;bKM4+T`aI;qMg@?zO}V?C7#D71T|E}^lkj4(w_jjl zz%KNO{4Mnpspo|z2H8PYXny>yBEqef47YXJO$|Jq!G3Bs_wq!olSf0;{`8yEVc#nx z_oPDio72e-(!J=X8xgL%q`sv)uOj@~j%tsqMxfut5qRs76@7b;>oVEPf}HY{^Y4WU z{}{X-Va1*2l*gZ`WtyqQo~;ERDErZ=;j|>PKE^)$fqX5#y8A8LySo?bD`{)J?;$+a z_ozj!_2Szr7)5yspQ#9mc3>*XAX0Nu98&9fBSc~1Z+QEE7}Mjf`?P@@t&~(Nh=*>z z2l{+D+C18*-r?;A+=rjGgkly!pKNm}LY>X&QaMc(^D}uHsyOHrj9-4vrDM+jX?rJk#B(Y1cygXqP64g$13fOv&wz0d&>6`>Bk?&KCOOO0(it9E#$ zimg?9XHk7gtf1Ik_Q@biilOICu`2B(!ip+O_(f(P=d@>Zx=wJX`+FTJ%zO2y4)TsG zMHakMIVwd=zv;~Ac8$lqHahMW+*#r6MJ+y82 zi5FzogHY(;BRt0B5;5^NBGdbD8fctIL=Ob(DMuw= zAMG(T?9&@zqvy&(zQe;?-tZB@8T0vY`Ae(6JR`m9*rrZ!?(@a5bAw61?6uAM3Xd4u zVzGq+Q+71XV0=(`YH=GddQ0r53+vC&=OK4Tr3+R|)Wi%4szb!c?T*SH&vfoozh~z0 zWVe}&cBFa!aSke~oAUXMT$1E-y4E~ZFg&GeLO3q3>F%~G?k~P?66zl(8+?tH#(+Wp zILj({Zs>_$_yX^PL#^0Ag7zm+MT@1+&dl; zf;dgv0fB`?Gc`FHYf!=v8xB((Lm|Cb!8+VtJpi}%8(PHpW#J&NYkKX~E5DT62kT%{ zvEfp&f(wvjMzv`j`)o&+X6_V+qeR=2w>Tj#& z(oSJy0NwEoQ>@wtXzA;7)3L9{qLm*}T}=qOSRBgTBY|c3{3+dnVY&_YoK8q;_a?Df zZHS9*p@#Rp-S7AdF$tJT*SCSRj55H`^5vIkHdp&rL$m-Lbb*1t0TSfJ_qB#-4yxFO zehzx^cN{v=#|PALfx$e$QF)?pWoHc!`VAJ-U$4SHg*smtzET$MEZ^hfpehuUel`OV zB$1@!$fqii#F6&5zfR!sg}-ezAOG&QAxGo#ljcdH;0AhBAqB+#cp=yezxKhU_R#@N z>$%o{5m3O9uV@~Mj_9`<9;rdx^TW18b0RM^PH(i?$9ZTX1E>Rqir)nPe*%vD4G4B9 z+7{orj(@hwG&E6iVo}C73ff^7W9o$QuLRpUpvr!(Q;cW$n^y ztKpFZb$(II&`MZ@f=c)t>iiH)uYX#}xYd?uwvpL}F1~TthL8Vqzr+`OFA0;uuKH9f zh1F%@?EU6l{(Sfizd-lG(Qz>gx?xJSQU)x7y1T5q9}~i3ob|uftnxl>2OZE!E1K^X ziUgU9x_M{y4u2(&=}%|!eh%RqbVZjzK+g~tdDU5CR*D|&Jzp>A0y-f>ob~q#-UsCE zKf4e*It*w(^u3M#RQca%z(3(sgx$zjoEBT|;~TDChCvAo1DVB!c3MviKF!15@#D4F zh<27l8!FfS9hXk(kB3X00bwMQ!(qJe)WCqogGVstN`Ewy7U~RKYRxIo zS#nhi?c+Fl6W|QLoZ^$orQvxf{mIz#_0j0GckH zgvRSfkMVbMt$>67j{S!}u?L&fcq0<3asj&5bo3~lo_zW9(8x4&LO}likwyQ3$>%@s zr4^Z^5l*(D%iBf`{nv{J)rvL2%HK(w<`|Gx#F_?97-njQ(T>@F@`%2@Cex=LKvRP9 z04a&XGmHpz7R{0WH#MH!v82ukAe+cyBPA`JX9@--)@f_623LQC7U{U&ii6=R*l=-&4Di@Rr=tvRkN)0%u zHK>?>QUkhtDHYv+^DqhB@1nlch@k5Wy;C~jmwzNK@SihRl<*0}dywjF%& zYkvu&Ku??8I&kg;7wBVI*w z3~a!j2IGSpu7E0wWAU>j@8e@;i=?xnrY$9;qvz391`4^bj5IFs{W;AHh5dw_kEOE1 zf5`@R=rbW(9v`C0^eFg8M`^t3q?&4KCk=rKA9(P`vG#S>TAE|#vIjK$6(9Irj4|+s z!p`_nC-GZ^zK z@F})Mqg8+?$e5$s=PzBdyOz^@*jD&_mePI+d3F`wKFkj$R^9qUn_Unmv60xhcMQ0d zO%V5&Y2XVn5jIlOSH&yI%z>)GTihWQ~5Fy$gP* zFhYBq=1nSB#wVm^VF_3E!Aw!qXD?O+e(zG5qVEvx0_W194?hu$+7&r7>ohVU1A{MQ z&1=b(rz>1RH#L4yZ2m~H@>^lmA+x{pAWpf3UfM8q)xL6ENG^jZtv?vN2T7_|-?Fd3 zM4q!7K>f>}+eJP~!|5oa>Vmoh%sc92;I?=w@E%A1zHRVd+u|>N<1KA^DiPc!n>=@G zN9x6{ud*3KSz*NuPrB^z93daUo(>x#+=%L+8SA;_uiMnndR9l3^BqH%yV zobGCtGy=f>!aB?c&RhcXg=YY$4BxY3aR};cs;S=hd@Jmje`jF|Je}me?R(lcX54gs z#9sn<93B;kLmH5~=gjc^)C}-=>l_8&Z}K@m%Fi%vIvwFRK4?8Q?g^tgTxT}&IbKL& zHl7oQK=$YzkcgG-MxXOwaTxM2cns2b2Ar@#nvlpJb;nJJDgeHfUo}v2)Cw8yIp3MC zf;4%aEe~uro*wm)YL*-zb$2$$N>IC->&CwFE3N#i!4AxDs`H&+D*$R;6Lo;(0A8Ys zPR6zyQCrghVh{Z9ZZFLDc*z(*?uu)c>}rk~w+ENN`_9gr;8Ub92q`Li958L#I(Tl> z*vQ~}h~h+|j#ZZ0#&Ul)#5Z(rw=Fb%)P)%tuLXA)EuS)ap7QRU!H|2gsJ;Dd;B2`B zjngp(9?>|UPFv5!Q4pg>B(e_%950>0sx{}De2yIe&tq2a^D~&Q&v~sevK4i@bqIs; zq2OVpO%bTWzElnXwF4k_HJKY%kDJcdengctopb=GwR88qwI*`Z?g7vlj9Q3$7s~>S zEf~Y~oruKgPMOgc=wqK_PY_ z$b%lj2H4)NPNngY@xiG%Gjc(*2T9u0sJT(+GuKncUtf$kJ*EDHg2O+B8T+D|P}JUM zV^!Ntzf#ATVY|moCt#%ai<*1^)K&(}8`acwHd5DA@35!y!v+QJbo6CBD$zii{HSth z1kQR;X9v~;#wSZO5bxuK4ixGK#21c2A$Ow+d=BT=2;r)WYpRQ<94jULE1JG zu!HZcBflr5r8h3RoX0-PI7{gn8H4(^jm6UpY&UQ6dZ3oq_(=~AI{^3Xr0s5IqXw@- zFbas}ZDRytt_aq&7>wF6mPKrVq4!2TuLs6pjRU}0Lki+(y~*d~SAJHKra~>rbRS}W zB%IMmIZEMtAv)fCwasxcO2Dbgy18@u{a@_Q$FfRbO{8Pu3hA)$q`h4_S!Da!(uZ^v znfgfPq;vUn=5wHxna?<3f!W?2IN7qA-y(%InQH0f*hw?xecyfKRaC*hW$i z-N`2pk~941jdraM^4&>0dQe^BLmwxuydMCb3>Smy2DGKS#{uPo{Rv6 zDA2HW=nv8IPLpIt3N5$;uTbpu4^CwV%g(l2^Nm+_d?XB;Nb`*wl}UlMqq?T{dg)Iv zd{tQR2sSJj|M?`gu%iliumVeQ&^Yj^zJAJHKX|@6SMZ*rhFPF~5&o3n>u@@qd$3p5 zxF*`LmV`fZaEbSq!|9TL(@&$}R3w@e@DOchUJ^}J6ea)a!1=NJg59rHW=QVwIXsG= zMpc71zw-kmU$ZenQ(^t=q;rgzUIpks>E&jGbuWzo`dL+b-l)fkho7WZs>&9tgmM$L z7QbhVqBpF1iNJ~8?xa~>j11~d9Cc0lsF$2Mx3LVbb zGl2Sej8S3Qc9S|OkvjdPZ>l}>bwVHek89hT3=oNB%}7n`47iw#z;`CP&{&8w^~rcr z(``i>HYJS!+EPg7ZfeEgsqOf)c4pPtrU{`78y_QWGJ=3aZkWEcQ%vI!Qh6B|;eVZjX zQQk-1W(7$~vQe_cj7V!t<0O>GLGxfv*5;Yd8IFO*fiKK>ZfS?EVv#hf%75zY-J_n{ zDoAC0*>m!(wFc=WSRJYNbY>R9>sZ%2gr`a~W=U<^{LUxR40cr{3tYMsIin7ISfZ3X z+-ts7@1TNws%)Gj8wn8-fe5>J^E)t;5c=HtxK=6aEzJYppWAOfHOl+$6q@$Zb8*3_ zE9006j^_J#spO>dR#lYu-m96OvjqbZX1EHn_ZT6Rfhd|}SJ;fnibnkPaJBX4^!6Kv z^q|Q6_BABF{=uWQ)Vm66sleV_-8;To_RLJV&EiWgsN<(gJ9D2GHKPXj_tzY{vC;iK z%nAs79;m_c@-?%9A^PcCs4bobZKRk?se|uwLL@A23STRWG%x?G={|QbX&wh4lz`|G z$w%vGDMm9d7V=hh&ov-uIAa8|uhMb6chsdF51P7eK$4*bfUSL)^k6;l1{yJ1N%+2Q zClQOxk{2EJyx`ojvfY~t*@GP&Bj@jUf}^5hA+jpSF4GH9_pn2J;t>lSN@$THwRTHR zzTc|S^qD`%pFg{<7YPaM%{WarULFCsd>6G~>U_gw6Vk0Sb_6m)kuxR)==TO=Ma6y3-N z;4{H3l@+GITArPiwkj3f-`=XE;7`Z_i1eZ(>gu_eJvTBq*WkUF%}TrkX+wlY1v#Br zavo5$@1Mis6TsGe%*0DE0JD&+5Gd}eT@Z_SCD{imNI5tTNcS05y$TtC$Mb$R&NZ za@N%JDVX$KBI7BZ_Mz@+Ib31+5V#eV_FA?V8xp?lon9WwK+SCNJDcx`I(bhg?U4|nU zA^;{?E(7Fx?s8P9Ja^87MjlO$(tK=l`LzQN-N`?UF z&+~(wD%git?}^HXO)L2V&kNudK~WcJx`hml3VnH2P;l^Y*Bd2&pOl%3 z7X|*rBSaV-)6-5(fiz)Td?1&;yD4r5&7f_RwjjN$a{*bc zs1ug+)os!Xk4}_y>W1@c4sKbTC~57Vop%N+L6c^-jd$lBIxsT<3BJY2u)Q5_3|NNtxg~rl>@zq&{e<+W*7lI?}T=G*CC!Bobd#4rC z@GiMSMylFAyS4AVzNlmXESY;f$14@G@0XR-wAk}Kl{=K6$YZxe=Ak`y1K)4xx?tQ0 z$v-6Lg*;24m+VuxBRjvj@XFKo-aynUoLtLV^1d$|nKr6)fC#a&UKAl+I|->uogOAt zF*4|j(%b)s4d{apF(cAB3+yxfW~z`T4=W%a5duEY^H`HNps~)L4jTQZ4WE`OgvG;Z z1rY*1{?X(g!$iufqRKOp+`6JXx}uW*c4nUAkFiMp7Y@fs`NGS&P0CTL{KM>^U@s`) zjXH@RRif+t?Q^}MczVh8fw72{^_%F@nH2XioynLVCSwe~pDGPf+t^@_aGUmMw3ohk z3#fAlncl!?lWNCdl{!%3Q zo375ahT{3&zZ>= zGn^ak`+w6hVaf_7j#sO^})fg?Eh(W8Fk@(-FW65nA3aYegtdHZ5N}Cqfmt!yK zNCN4oF%(bo1{(qBR=@e+hu79DUeaOiH4O!23iiHe^(QQEoO^7~n8vnw7G)tG&-S~n+^>lFB>p7~u%|8l^ z$Td2oPo2h(RZ8Eiz~g+*wpDh>?|c~-x!}MY6)OU zoql0t63q3wrq5!JJo@4mLSk))Zca>fja^V(IGG1D7tWFHlz(?)@HN z4CuJ_cY{AJzAoY#`0e69`~UBr1}+Kw?~K0JWU{;j637SDsS&ljK=s4=Unj^O-=}Q2C6Q zByo%dOqg?V(%`dn+>-)E%-}dr@bykyGWg6CS5?4>)w&Qj2m1XlicYEtlMIEV&dX?_ zI(VQA|EQ22s^j@v=RG>OLavx8wSMZv7rYRje8=^(2Gi*I#PuKM@`Z$)KX8+OVQGk7 zWGT2Wh^D#F1ng_Rt&@cNk&opu4NDPW@{fxgZ18(xh*1m#futrj7#dR7*ZLV5`jzm_ z>MC4h7*>!g<~MpU#r2OOUG-Vkv0_~@yKDW-ey`vAg7L+%YPy)7{%3F$147V z{Dh|Y(a>9ZM-0=-8`#;DQG|lCg$QDVsgM2KOG%OW_w3EM)vkftZFOhQ%snzQI z@(le(_+|mC-^h7xnA(@f#cujfH?hjxFb(SbKK=1|64%e}(&s62`AS00Dcs~uto}d9 zb~MeEm&kTWxIg(=>}go42$QFNBi9*X%v>Tj7*c!J`868)!T4q!E`WgpEWH0g+*?4^ zwPb6fNC@ukuE8xxa0?P3xVr{-XCc8YNYLPJ!7ahvArRbMgS)<+kaPRo?$h1x-S_{u z$Jk?!wf358el<(x{Hk_|3;6pdWF!}Lk^=}d7x2)pK3#v}o%sj?&lMc0(FYpospTXtL&@9z(*AYX*tZS7 zcNxm;2!fOA$*yI8Y_RrUcG=s?5cir1mW%q&fn-m{2Li(6$-`f7c%dvv2Q2*>s2V4V~F@akVCM+ zx}K0j6QD4eGD5NSy5Q?UyJ>;qW)tAUpaX{egv_KPJD}|3SG@a7bprM;92Ft4OItU* z*&0;+F~s6;?Rt^ef;~^=6`24<#6%K>ozVk7z4qHI0Ua=sC*?vqSnnHsgmrvKnU+8N zHVexQ90-51i^xq~@C1Skw9RazkK}K=>er$4?0?xcw{G0l2fw}k6mP)gk$$m~pAHyG ziu;K&XepC?#Lno4SKIgvt*Qe?{{*eBgH_$=qoL!&z_c9y8`{|oyzwV8pnl=)rVe%r z;pYb4-sq$M8=7kqip1d;wC1L9+aSEzCKT5xL>l++N}7xgp8Y;Sb~tY{AhdWNGlM)tLAIGz{$hte=`Hvf5t!{Vn@rP$a?LS zK@$HWa?@3nH5`G*>Q)R_l{B3GynsS~m`93&uYIztrS{yeO(iI{zTP+Xp~jBXyrD?e zRylRKh^I~YAGyHV8e^P4q4OQ%wZv`$b;0l&kcsYrtb;nuw~Hx z>c9C1hy>XGK{!F=>Q8|<_NYlI)~A`5?Bm80RR9r|f5J7YGMhb~#oD&;qTe?V5@B09 z{U*tKYks9jvAmtWv8~N^B;)qK%>cFOe^k`d6C2&?fte0s=_^GLnuNbuc0e(lrBiHw zlY5m?YbVhP1?v2DHfvD!U^WK;t`+1#%Z`Gau$9TBV2>lp{H((al3 z`Gp9MapS=Iig9}PS3SC)o$P_qB;1T`8S#k)dhAH^H+~kwlTZ3beUVXLV}{%@1?tv* zdIC`XKXnA)RWYY`W8d_-R|)ihOlTiF1CL!EizPE|0P(MX#R4&77pJ4hwd4O4S>rnZ z#f8}8*_Tze^Y!3W_hSv`yOMlj{Ke{UoMXj-@(_5r+|IWR} zK8(3z0ib@-uTuCg+D!*i8~g3MTkOcJdThq@E}(<0+2mKM)233USNzn(;A-7}W*mrI zUDmp5Tb}>A2gy9O?HAL#n3lg<*uvbdQ^mxudyvdi3jirmEuZ+=ww-K>r@DSB1>J*W zp4#>=pr37fvZ?e582FDNKLG>(0h|A9GVs5e+kZxW0tWt<%K0CWpMZgXQ^S=1eGB^k zRe1qWZT_Fv-hXD5`@i?QzZ(YdPg}-6)%}08Ndixe|G%}#{~hEfVBkN7`~(dA_Cx=h zHvidV5dMG4_5K0|{u56BPbLFr|5*nJ{I@`N^SHN(6o3Be^J(}ZT&O<+Yo;B| zZvua5%&Mz0l#a5ms`9e(dF9W#bX~~j$*7~ubci+UZ5fsB@lR^g|M#Mk{eM+U|E3pI zI>!#jMSi6}3qYVIXr!wc#Qi{tO7}Vd{ap2J>oa&2%I+v>l2ch?$(kF!Ml*tI+w68O`J>9f}j|(Vqm193V7E~QwT7$rO;cIYw=|6k-mEYGFu@7Jv$(I0C-<@~#P3)Gx~+QC|^&An%= z_a!?>h>L{lcdfwu$E>U+o?F}cofuLgUC_;{GKk0jS9XI~d-eS^|Ly#mGE~*^RTg*+ z7x*oB0yZ4DEoEBd9|gp>@>ezPSK%hCz=DTT%JuwFQfWs;?*Glyzctf3a42vqhy+_W zaG8WTSn!o!Uz8v>$YXi#SNdS#aiulAYxb13t*v&Ip1+lI!UqU&j)d0d;Dw}ijYSZ@ zOYARzdPevXmSDl1h+1lUe%Hc3a-9yO{I=Dp?{>|K!&EhU#+T>mJ{c;bqmf&II@!pCbn7$=$zP?ziI|Qz7z8*h-=3fJM zS3i&e_wzN~2TtIAqWF9wC0*cl;;!cglBhK&$HV!L)3VdT#lf;Po0fMr-Vf*}yFY9|B6E+Ysy)Py8~KNeO*dCl z0xh=-*nsN;V$=O`nCjA9nLzXXri<>YN9~gl;%2YM6<{t3xL?2?0g&Auw_J#uAFdv* z^>RJ^aeBDfF=vzZ z=y`V{a!uTF_v3ot^d<#a_wjZ_pjNI<_ulhl;i~{>3VX}L>DJ>6(<8;r$OfZC81o-z$6F*A&hw*SeE7H3v#KY-@=$9%?^+WX0v{4pGo~GMdb)T8zETCx*O+?O%sP z6nl<_QD0T}(S7*z%~5^Vo(S)#i zj%P%tgBdYz*n{vsMBSeayd_XhLfi-mNp!2Q+f7%OyoU>wS=rNFTmU3!|t>0*gTTAw!&|FW@#fgEh}s{D~PG8sM>kPL!#4U^;_%A zZA07?*o9b$uEmnP9|+P;IW_JA+q?8X z;1#XM0xd7B&YuQ|OS76!~%d2XHI?U$V5xWFE(#joziAGe6LPRJsV8@U%)i^&D96AJ33vvoTRkF{o}KN>n;*z8zP z*OtXQb0~cfcLVMfMJ)(T#R;f}Y#vJ=?&e(bl-A8odE69lZMs0|c=Qvx5NfFLzqTUI%!nv5bcDdZ>6#Q&5N@>W1l=gs*KoMbBqr3z z9~AO>BzNhEIFzF@Y5FIWb_`6eaFE8hauYHIt>zy1MRpE|Hxl?Id;#Z4#Qt*1y^C=` zfXIcH3>mu5jb}>$7wNv|P~fI|=!H%OJ6!2sm9tFav2DWrevO1vI>KW~!G8#?LMZ+o zEmjb8hSJGN{4-c5WR2YqYP%m2Q6wo;2tzQ%guQsw5GCt_JEi=8e&C`7y;{ z&qwlV|FXO1 zl>z51AC{ss@qs*T-Z9jhq{vAIIpIEzI4_@+XL(u2!+Na%bW%Vn@a~T6_O$@mO(2*& zA^17M%kr4FrOb5mnB}d?Z&5d1i3i+5#1V_<{(OlDWdR~ws#qIL@$G0mFh$z~SHB~K8S@S&wwo70s7fLjZZ&xP4QJNqT#Ead)G#316Ki-uZF7fLr z-TD-cxX8RCW`@QFM8mgPb=h z@7zL%?-CazS-UGkK6fzRGy0n1&oEANa)?WJ;u1%EqY;6YsfmkpP-Du9Bwp;_O63j- zF(|pJ(at#=qa20r8^BR;q zCDz2|fYt&m2*nl>Jq38*m3(j$G(GnIJC8u#=W)c(vwvj;x%2@r{sn&66bNg_+EdR8pJi^H*9 zNVZrVMCOpDMJldsc4!=HB)xGR>hIvUZsAv-i5*B<_9dqLRUGuBCTtUtb$r6yE|gki zlkkPyB}4Nn4@<=Foi|L?fX}?@p;;d6D<&$U*ZEcH*DOq~@v4uK zq}BrGe0p5pSx$9}xp!C9K7uyH_C9J>7ASrh=5~pUQ}+n{lSK9(AbjbA0R!t~`TNDA zU|`6!4J>FGQBHwGI4CT^W$rBYbZ%9-Z9k#BG6l^?I}AG(!4Ho}Y1=oe z#TgWnXB%UaF(gtGJRLk8>w5W$7-O1PI%vsQPBG!#LTv*h=~)hoCeoi3zEHri#aAg- zz^IQItzy2%YYMEU>m|gDd!HB@L*mi=$;P#xt!&i+L!wJlSv6tPxLBvsabdlFr=P}f z((~b58m(^i1&f`N^pKW0Q2h2>)!_cCj8P#fSq>{?1NK#=U->}X@|dG&vV>7xp@R3I zGrlwJDorji8)XYcZDvf|m5QXJ&0b+(h;L{~bh*AVoS)HM!Zvftpj zLO77Iut|`k)bQZENs%Jgsd*~Qq>HhC_ks@-8m~vw$)-RG1?w9;aE}v595qrxqo$xN z?_~|g@7zDV#LiIMXD-um^47{Anc%&2i_w{qZd$RZXFShL(v{C*9ecG-mlLNWsP7X#N$`z+S!=u^=YvY9=PJJ{+g5vZ%;Kewzi>r;F&k1E zVE9onfPT0bNU^la0E#HyYCIx-o>-c?AzVqsd^}=(p@;Re@&_PscY2}TsstQ{bjjtCYJ|Va4%Xrm%)9K$9uPzh2 zu_s|YtJsvc=zW*is}?C%_lC@Z1dYO_rnAJlfuP>TpqE5E@fOHT^4LLwC$MN#VWS@{ z=ZWs-9j}o%oXwtkYcw-al@Nq{_t&HBo#7*C(=y{5OB zvKL#Evzgj^!CS~2QV1$y4AlBm@hRDC>$a;+nH6(LvG}8Ur>u9Y0k(T};!_VoZtbEj z&!2T-`GAp0n{@-X;%kv;+z^Q66vwosV#D?Zwa73L0;Ra$Sa^A;zPqYsK!(1T*w&LB zXJK)aK_C>Q_y)RYjS=Oc@vrdE}>1c^SC?lB`k>Njj zHJ*%0n4i6-r@|>u@!4{ioe)~2r)?Bm6b*C%tr)XKvQ5JNP;DaG^JBX{@gpKe$cq%- zH$7dWb}Wuy(W0P>wz%~M7@j9H)SVnvjL@4qTQ&B^DNc1-ufxKi`@*AOQy$D!-?ZQU%KG*RTC>U5Wh&tKHZGI@%Y*{Sv zi;d`n@9NJsp68%RbDb5H>3#E!4+hi^f*B3Lo3TFB#;CCSLlp^iXBn20h(3gyh37t^ z`3OE=A_J+W^x9)TP@(53AcPB)KgWgdzA=0fb$NVde-U5*t%J@kpPEe< z3}w_>IxLaa5FDYUF*uVzpy+v&-YWs|^^xFiHTO9=5@{PrP5y$ITHp?MOHMGILB9f& z=<$S#j1vBzqJ#XuMO)-Zn0|}i;SR_N`hz5m>3zCDlCOSAYW*RZEk}a>Q!=MAmjUSB zLmyR_RwKZLU;bfCPva0()JBfu5rz7c5i97uO><-M$VE8}0MNaTKB^$CriBZyw+j+g z{UxgVhv+PNZ|xtV-3#b(P1Kbw)Hwcf@<_zW{u0bY#6QbmZHd4mp-kjq0jthajnewM z39dU!1WEG-**?YCCzm+6bu2Wm!iVFvnq&AtPw*Y?Hyr816Q|ymxz%_8xww--H{I@< zXT98*zH(rSg;7_c!Sh$%Zan3!@ew-T`jVC10JL&s{l*^KF9B`mYo6s3POI4I(@`hXs@rg5-KzYNJZ-V+uoC8(`J>6naEbqQdRfb+;%h>>Bu$!Y>@*x`ZSK<~e zwPOeSK6pxFvHN$~HHRnhs#S5=@>Y-feMV}P{JMa>xJWz&JKxyaqgoA1$!ua-@}=4} zKiyQ&$CP@Bbr19E^Pz}`ojGY>)!4``j(^MFf>KkWF?o>pO;m8DN!;$WgKW#X2R;@`k zu=L1n&-r!uyJYq&^RO5Ow|=yMq#=&@m?mOftSI_SutX87w~~SJKR{cKD2oIWDw#`2 zijqjG1T3e$&nQuN9;}(qtOB=(_}=wA*Y-ndhLXWYAP>3y(WIP(je9-jTBO;i57o2K z&*)RwAYL}}XtIQLi6_r_x{@XakD^D4*VzL4ihv$f9)}8c1b?J~&Rne)Wcvp&(J!6g zq{46Dp^3ubj+Gy^+*AXEyfT_*Um+dMcfqxnKq^PCTSVkrpf4=Ui(Y0c_wiNR1?+ex zdKou2uuoY|j&nv7UYcf_=w!lwaY4%_{&ElTGyk{-I?}K~G-vCd(j07HATpZ^BQRZW z5xgR?>oumq0kfn=YK}qxdziwI)ei`2A*e)Fssy(imgn>HRCW;>yKfAT9=RXxaV5Cv zo3+M7xk(i|xipLPI8`y@0=Pn}Iu!0tqZa&&>7WACpP_%s%ykzFxG_E-Uk&f`uoDfY zD-l$opCHd|C^Hvx6{V#IOt>U)zgG271jK2__^YfxYYV2%4apnXs*P{wQrv^%k1q7R zyyX(rB#5W&H`9Kf@l3&_g{#}`n~F43w!Jw4n$|L3Y5r=8V8Lc%D%h2ZvXPP8&{Uzk8xC`R}eDXz!G_4+UEJZuFv@+ zoo~bQ){HpV9{P2wHhCqfjs9RlbE4=8zXVUxVkky)G}G+>8nGnSt0@ktBhU%H&~>nl z-9hA~5VP+xLtgP;y%68ZVpA#Vxk6mK+$9Sx%9$Vw!c|~w*|MZgLASGFJlpkKy?a@m zj~_=N?POH|hhXk03^0}R5ZNRue6okWo0rF$L%&1rr+Wz{C9lR;^hQ<@l6ePO=+-^) zRM;~9OFxk3#UZM}G01zW`d~eH$zaF#8bk3N#9Np7thwW@bSoh>Ec!j%iO!^Jm>>E{ zqlmH(k>u`1EM6w~WDlVu!j?k>#mRJ?l5GMh@2s+la_m%q4gmgr2QTZMU*cOu?t7i$%h zvz|4ZjGMU!guCfhTje|O6!~se(0>1jusO?tR=1bWV*PNbo3

9CDV6wX7q%WoIiPe_=(JZ`HfiS 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 e2*B%kmGsib*IY! diff --git a/DatabaseFiller/output.prisma b/DatabaseFiller/output.prisma index 116e317..da9409d 100644 --- a/DatabaseFiller/output.prisma +++ b/DatabaseFiller/output.prisma @@ -43,6 +43,7 @@ model Guideline { ExtensionBSI ExtensionBSI[] ExtensionANSSI ExtensionANSSI[] ExtensionAGID ExtensionAGID[] + ExtensionMOZILLA ExtensionMOZILLA[] GroupsNIST GroupsNIST[] GroupsBSIFEDERALAPPLICATIONS GroupsBSIFEDERALAPPLICATIONS[] GroupsBSICUSTOMERFACING GroupsBSICUSTOMERFACING[] @@ -131,6 +132,7 @@ model Extension { ExtensionBSI ExtensionBSI[] ExtensionANSSI ExtensionANSSI[] ExtensionAGID ExtensionAGID[] + ExtensionMOZILLA ExtensionMOZILLA[] } model Groups { @@ -587,6 +589,18 @@ model ExtensionAGID { @@id([id, name]) } +model ExtensionMOZILLA { + id Int + guideline Guideline @relation(fields: [guidelineName], references: [name]) + guidelineName String + Extension Extension @relation(fields: [name], references: [name]) + name String + level String? // put level as Nullable (it would be considered not mentioned) + condition String? @default("") + + @@id([id, name]) +} + model GroupsNIST { id Int guideline Guideline @relation(fields: [guidelineName], references: [name]) diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index f0a7000329090a06a79f98535ff5f3c2224ee790..8fc761af35e1254b5f5be539483ecd4b72ea7d42 100644 GIT binary patch delta 36547 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 N*JH%sou&S?{{fWjD`Efu diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index f0a7000329090a06a79f98535ff5f3c2224ee790..f60cd0a8ef7ad2f61992f14093b7a5680ad6fee1 100644 GIT binary patch delta 36915 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 diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index a7e0aa1..ad73a76 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -1,5 +1,5 @@ -import datetime import itertools +import json import pprint import re from pathlib import Path @@ -7,6 +7,7 @@ from modules.compliance.configuration.apache_configuration import ApacheConfiguration from modules.compliance.configuration.nginx_configuration import NginxConfiguration from modules.compliance.wrappers.certificateparser import CertificateParser +from modules.compliance.wrappers.conditionparser import ConditionParser from modules.compliance.wrappers.db_reader import Database from modules.server.wrappers.testssl import Testssl from utils.database import get_standardized_level @@ -23,217 +24,6 @@ def convert_signature_algorithm(sig_alg: str) -> str: return sig_alg.replace("-", "_").replace("+", "_").lower() -class ConditionParser: - _logical_separators = ["and", "or"] - # simple regex to find all occurrences of the separators - _splitting_regex = "|".join(_logical_separators) - # same as above but also captures the separators - _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" - - def __init__(self, user_configuration): - self.__logging = Logger("Condition parser") - self.expression = "" - self._user_configuration = user_configuration - self.instructions = load_configuration("condition_instructions", "configs/compliance/") - self._custom_functions = CustomFunctions(user_configuration) - self.entry_updates = {} - self._enabled = None - self._operators = { - "and": lambda op1, op2: op1 and op2, - "or": lambda op1, op2: op1 or op2, - } - - @staticmethod - def _partial_match_checker(field_value, name): - """ - Iters through field_value to check if name is contained in any of them - :param field_value: iterator to search - :param name: name to search - :return: True if the element is contained in the iterator - :rtype: bool - """ - enabled = False - for element in field_value: - if name in element: - enabled = True - break - return enabled - - @staticmethod - def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition="", - certificate_index="1"): - """ - Checks if a field is enabled in the user configuration - :param user_configuration: the configuration in which the data should be searched - :param config_field: the field of the configuration containing the target data - :param name: the value to search - :param entry: the database entry (only the first two elements are checked, they are needed for KeyLengths) - :param partial_match: Default to false, if True the - :param condition: Default to "", the condition that the field has. - :type condition: str - :param certificate_index: Default to "1", the certificate to check - :type certificate_index: str - :return: - """ - field_value = user_configuration.get(config_field, None) - check_first = None - - if condition: - check_first = ConditionParser.get_check_first(condition) - - enabled = False - if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): - # Protocols case - enabled = field_value.get(name, None) - if enabled is None: - enabled = True if "all" in field_value else False - - elif isinstance(field_value, dict) and field_value.get("1"): - # Certificate case - cert_data = field_value.get(certificate_index, {}) - enabled = name in cert_data - print(cert_data, name) - - elif isinstance(field_value, dict): - # Extensions and transparency case - if name.isnumeric(): - # Iana code case - enabled = name in field_value - else: - enabled = name in field_value.values() - if not enabled and partial_match: - enabled = ConditionParser._partial_match_checker(field_value.values(), name) - - elif field_value and isinstance(field_value, set) and isinstance(list(field_value)[0], tuple): - # KeyLengths case - enabled = entry[:2] in field_value - if not enabled and check_first: - for field in field_value: - if field[0] == entry[0] and str(field[1])[:check_first] == str(entry[1])[:check_first]: - enabled = True - - elif isinstance(field_value, list) or isinstance(field_value, set): - enabled = name in field_value - if not enabled and partial_match: - enabled = ConditionParser._partial_match_checker(field_value, name) - if not enabled and check_first: - enabled = name[:check_first] in field_value - - return enabled - - @staticmethod - def _prepare_to_search(field, to_search): - new_to_search = to_search - if field == "TLS": - new_to_search = "TLS " + to_search.strip() - return new_to_search - - @staticmethod - def get_check_first(condition: str): - check_first = None - conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) - for condition in conditions: - condition = condition.strip() - if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: - check_first = condition.split(" ")[1] - if check_first.isdecimal(): - check_first = int(check_first) - # If the condition value isn't a number it doesn't become an int and the validator gives the error. - Validator().int(check_first) - return check_first - - def _closing_parenthesis_index(self, start): - count = 0 - for i, c in enumerate(self.expression[start:]): - if c == "(": - count += 1 - elif c == ")": - count -= 1 - if count == 0: - return i + start - - def _solve(self, start, finish): - to_solve = self.expression[start: finish + 1] - - while "(" in to_solve: - # So that I'm sure that there aren't any parenthesis in the way - starting_index = to_solve.index("(") + start - end_index = self._closing_parenthesis_index(starting_index) - 1 - replacement = self._solve(starting_index + 1, end_index) - to_replace = self.expression[starting_index:end_index + 2] - to_solve = to_solve.replace(to_replace, replacement) - tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) - tokens = [token.strip() for token in tokens] - for i, token in enumerate(tokens): - next_token = tokens[i + 1] if i < len(tokens) - 1 else None - to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) - tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) - tokens = [token for token in tokens if token] - while len(tokens) >= 3: - first_instruction = tokens.pop(0).strip() == "True" - logical_operation = self._operators[tokens.pop(0).lower()] - second_instruction = tokens.pop(0).strip() == "True" - result = logical_operation(first_instruction, second_instruction) - # After calculating the result it is inserted at the beginning of the tokens list to substitute the three - # removed elements - tokens.insert(0, str(result)) - return tokens[0] - - def _evaluate_condition(self, condition, next_condition=None): - """ - Evaluates a condition and returns if it is True or False - :param condition: condition to evaluate - :type condition: str - :return: "True" or "False" accordingly - :rtype: bool - """ - negation = False - if condition[0] == "!": - condition = condition[1:] - negation = True - condition = condition.strip() - if condition in ["True", "False"]: - return condition - if condition not in self.instructions and \ - (" " not in condition and condition.split(" ")[0] not in self.instructions): - self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") - return "False" - tokens = condition.split(" ") - field = tokens[0] - to_search = self._prepare_to_search(field, tokens[-1]) - config_field = self.instructions.get(field) - if config_field and config_field.startswith("FUNCTION"): - assert config_field[8] == " " - args = { - "data": to_search, - "enabled": self._enabled, - "tokens": tokens[1:], - "next_condition": next_condition - } - result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) - else: - # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use - # (None, None) - enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) - result = enabled if not negation else not enabled - return result - - def input(self, expression, enabled): - self.expression = expression - self._enabled = enabled - - def run(self, expression, enabled): - if expression: - self.input(expression, enabled) - return self.output() - - def output(self): - solution = self._solve(0, len(self.expression)) == "True" - self.entry_updates = self._custom_functions.entry_updates.copy() - self._custom_functions.reset() - return solution - - class Compliance: def __init__(self): self._custom_guidelines = None @@ -299,7 +89,7 @@ def input(self, **kwargs): :type kwargs: dict :Keyword Arguments: - * *guidelines_to_check* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated + * *guidelines* (``str``) -- string containing the names of the guidelines that should be checked in the form: guideline_version1_version2 in the case of multiple guidelines they should be comma separated * *actual_configuration_path* (``str``) -- The configuration to check, not needed if generating * *hostname* (``str``) -- Hostname on which testssl should be used * *apache* (``bool``) -- Default to True, if false nginx will be used @@ -331,8 +121,10 @@ def input(self, **kwargs): self.prepare_configuration(self._config_class.configuration) if hostname and self._validator.string(hostname) and hostname != "placeholder": test_ssl_output = self.test_ssl.run(**{"hostname": hostname, "one": True}) - + #with open("unitn.json", "r") as f: + # test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) + if output_file and self._validator.string(output_file): if self._apache: self._config_class = ApacheConfiguration() @@ -562,6 +354,7 @@ def _retrieve_entries(self, sheets_to_check, columns): Given the input dictionary and the list of columns updates the entries field with a dictionary in the form sheet: data. The data is ordered by name """ + self.__logging.info("Retrieving entries from database") entries = {} tables = [] for sheet in sheets_to_check: @@ -795,211 +588,6 @@ def output(self): return self._config_class.configuration_output() -class CustomFunctions: - def __init__(self, user_configuration): - self._user_configuration = user_configuration - self._validator = Validator() - self._entry_updates = {"levels": [], "notes": []} - self._operators = { - ">": lambda op1, op2: op1 > op2, - "<": lambda op1, op2: op1 < op2, - ">=": lambda op1, op2: op1 >= op2, - "<=": lambda op1, op2: op1 <= op2, - "==": lambda op1, op2: op1 == op2, - "!=": lambda op1, op2: op1 != op2, - "in": lambda op1, op2: op1 in op2, - "not in": lambda op1, op2: op1 not in op2, - } - self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" - - # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: - # function(**kwargs) -> bool - # kwargs are defined in the _evaluate_condition method of the ConditionParser class. - - def check_year(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True if the year indicated has already passed - :rtype: bool - :Keyword Arguments: - * *data* (``str``) -- Year to check - """ - year = kwargs.get("data", None) - if not year: - raise ValueError("No year provided") - self._validator.string(year) - # This means that the guideline document didn't define a limit to this condition - if year[-1] == "+": - return True - - actual_date = datetime.date.today() - parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") - return parsed_date.date() > actual_date - - def check_vlp(self, **kwargs): - status = kwargs.get("data", "").lower() == "true" - result = False - for version in range(3): - enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) - if enabled and not status: - result = True - self._entry_updates["levels"].append("must not") - # This final operation should give True only if both status and result are True or both are False - return (result and status) or not (result or status) - - def check_ca(self, **kwargs): - to_check = kwargs.get("data", None) - if not to_check: - raise ValueError("No year provided") - self._validator.string(to_check) - if " " in to_check: - tokens = to_check.split(" ") - if tokens[0] == "count": - count = self._count_ca() - op = tokens[1] - num = tokens[2] - self._validator.int(num) - return self._operators[op](count, num) - elif tokens[0] == "publicly": - certs_trust_dict = self._user_configuration.get("TrustedCerts", {}) - trusted = True - if not certs_trust_dict: - trusted = False - for cert in certs_trust_dict: - if certs_trust_dict[cert] != "passed.": - trusted = False - return trusted - - def _count_ca(self): - cas = set() - for field in self._user_configuration: - if field.startswith("cert_caIssuers"): - cas.add(self._user_configuration[field]["finding"]) - return len(cas) - - def check_this(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True if either the one calling or the other is enabled - :rtype: bool - :Keyword Arguments: - * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not - """ - enabled = kwargs.get("enabled", False) - second_condition = kwargs.get("next_condition", " ") - tokens = second_condition.split(" ") - field = tokens[0] - name = " ".join(tokens[1:]) - # only the first two fields of the entry matter, and entry is only needed for key lengths - entry_data = name.split(",") if "," in name else (None, None) - second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, - partial_match=True) - enabled = second_enabled or enabled - self._entry_updates["has_alternative"] = enabled - return enabled - - def add_notes(self, **kwargs): - note = " ".join(kwargs.get("tokens", [])) - self._entry_updates["notes"].append(note) - return True - - def check_key_type(self, **kwargs): - """ - :param kwargs: Dictionary of arguments - :type kwargs: dict - :return: True always - :rtype: bool - :Keyword Arguments: - * *data* (``str``) -- The name of the algorithm that is using the condition - """ - note = "" - alg = kwargs.get("data", "").lower() - valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] - recommend_dsa = False - for cert in self._user_configuration["Certificate"]: - if cert.startswith("int"): - continue - cert_data = self._user_configuration["Certificate"][cert] - data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] - if cert_data["KeyAlg"] == "DH": - recommend_dsa = True - if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: - note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ - f"with an algorithm that isn't consistent with the public key" - - if note: - self._entry_updates["notes"].append(note) - if recommend_dsa: - self._entry_updates["levels"].append("recommended") - return True - - def check_value(self, **kwargs): - tokens = kwargs.get("tokens", []) - config_field = tokens.pop(0) - tokens_string = " ".join(tokens) - tokens = re.split(self._operators_regex, tokens_string) - tokens = [t.strip() for t in tokens if t] - value = tokens[0] - operator = tokens[1] - name = "".join(tokens[2:]) - # if there is a "[" and a corresponding "]" then the text inside is a level of a dictionary - levels = re.findall(r"\[(.*?)]", name) - if not levels: - levels = [name] - field = self._user_configuration.get(config_field, {}) - last_level = [levels[-1]] if "," not in levels[-1] else levels[-1].split(",") - last_level = map(str.strip, last_level) - # used the in because in this way is easier to edit if needed - if config_field in ["Certificate", "CertificateExtensions"]: - result = True - for cert in field: - for level in last_level: - levels[-1] = level - # I pass to the function that gets the value the certificate dictionary as field - configuration_value = self._get_configuration_field(field.get(cert, {}), levels) - reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" - partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result or len(configuration_value) == 0: - self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") - result = result and partial_result - else: - result = True - for level in last_level: - levels[-1] = level - configuration_value = self._get_configuration_field(field, levels) - partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result: - self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") - result = result and partial_result - return result - - @staticmethod - def _get_configuration_field(field, levels): - for level in levels: - field = field.get(level, {}) - return field - - def check_dict_value(self, **kwargs): - self.check_value(**kwargs) - - def check_year_in_days(self, **kwargs): - # todo implement this - return True - - @staticmethod - def always_true(**kwargs): - return True - - @property - def entry_updates(self): - return self._entry_updates - - def reset(self): - self._entry_updates = {"levels": [], "notes": []} - - class AliasParser: def __init__(self): self.__logging = Logger("Compliance module") diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py new file mode 100644 index 0000000..304257b --- /dev/null +++ b/modules/compliance/wrappers/conditionparser.py @@ -0,0 +1,422 @@ +import datetime +import re + +from utils.loader import load_configuration +from utils.logger import Logger +from utils.validation import Validator + + +class ConditionParser: + _logical_separators = ["and", "or"] + # simple regex to find all occurrences of the separators + _splitting_regex = "|".join(_logical_separators) + # same as above but also captures the separators + _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + # mapping from field indicator used in the conditions to the field of the configuration dictionary + + def __init__(self, user_configuration): + self.__logging = Logger("Condition parser") + self.expression = "" + self._user_configuration = user_configuration + self.instructions = load_configuration("condition_instructions", "configs/compliance/") + self._custom_functions = CustomFunctions(user_configuration) + self.entry_updates = {} + self._enabled = None + self._operators = { + "and": lambda op1, op2: op1 and op2, + "or": lambda op1, op2: op1 or op2, + } + + @staticmethod + def _partial_match_checker(field_value, name): + """ + Iters through field_value to check if name is contained in any of them + :param field_value: iterator to search + :param name: name to search + :return: True if the element is contained in the iterator + :rtype: bool + """ + enabled = False + for element in field_value: + if name in element: + enabled = True + break + return enabled + + @staticmethod + def is_enabled(user_configuration, config_field, name: str, entry, partial_match=False, condition="", + certificate_index="1"): + """ + Checks if a field is enabled in the user configuration + :param user_configuration: the configuration in which the data should be searched + :param config_field: the field of the configuration containing the target data + :param name: the value to search + :param entry: the database entry (only the first two elements are checked, they are needed for KeyLengths) + :param partial_match: Default to false, if True the + :param condition: Default to "", the condition that the field has. + :type condition: str + :param certificate_index: Default to "1", the certificate to check + :type certificate_index: str + :return: + """ + field_value = user_configuration.get(config_field, None) + check_first = None + + if condition: + check_first = ConditionParser.get_check_first(condition) + + enabled = False + if isinstance(field_value, dict) and isinstance(field_value.get(name), bool): + # Protocols case + enabled = field_value.get(name, None) + if enabled is None: + enabled = True if "all" in field_value else False + + elif isinstance(field_value, dict) and field_value.get("1"): + # Certificate case + cert_data = field_value.get(certificate_index, {}) + enabled = name in cert_data + print(cert_data, name) + + elif isinstance(field_value, dict): + # Extensions and transparency case + if name.isnumeric(): + # Iana code case + enabled = name in field_value + else: + enabled = name in field_value.values() + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value.values(), name) + + elif field_value and isinstance(field_value, set) and isinstance(list(field_value)[0], tuple): + # KeyLengths case + enabled = entry[:2] in field_value + if not enabled and check_first: + for field in field_value: + if field[0] == entry[0] and str(field[1])[:check_first] == str(entry[1])[:check_first]: + enabled = True + + elif isinstance(field_value, list) or isinstance(field_value, set): + enabled = name in field_value + if not enabled and partial_match: + enabled = ConditionParser._partial_match_checker(field_value, name) + if not enabled and check_first: + enabled = name[:check_first] in field_value + return enabled + + @staticmethod + def _prepare_to_search(field, to_search): + new_to_search = to_search + if field == "TLS": + new_to_search = "TLS " + to_search.strip() + return new_to_search + + @staticmethod + def get_check_first(condition: str): + check_first = None + conditions = re.split(ConditionParser._splitting_regex, condition, flags=re.IGNORECASE) + for condition in conditions: + condition = condition.strip() + if condition.startswith("CHECK_ONLY_FIRST") and " " in condition: + check_first = condition.split(" ")[1] + if check_first.isdecimal(): + check_first = int(check_first) + # If the condition value isn't a number it doesn't become an int and the validator gives the error. + Validator().int(check_first) + return check_first + + def _closing_parenthesis_index(self, start): + count = 0 + for i, c in enumerate(self.expression[start:]): + if c == "(": + count += 1 + elif c == ")": + count -= 1 + if count == 0: + return i + start + + def _solve(self, start, finish): + to_solve = self.expression[start: finish + 1] + + while "(" in to_solve: + # So that I'm sure that there aren't any parenthesis in the way + starting_index = to_solve.index("(") + start + end_index = self._closing_parenthesis_index(starting_index) - 1 + replacement = self._solve(starting_index + 1, end_index) + to_replace = self.expression[starting_index:end_index + 2] + to_solve = to_solve.replace(to_replace, replacement) + tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) + tokens = [token.strip() for token in tokens] + for i, token in enumerate(tokens): + next_token = tokens[i + 1] if i < len(tokens) - 1 else None + to_solve = to_solve.replace(token, str(self._evaluate_condition(token, next_token))) + tokens = re.split(self._splitting_capturing_regex, to_solve, flags=re.IGNORECASE) + tokens = [token for token in tokens if token] + while len(tokens) >= 3: + first_instruction = tokens.pop(0).strip() == "True" + logical_operation = self._operators[tokens.pop(0).lower()] + second_instruction = tokens.pop(0).strip() == "True" + result = logical_operation(first_instruction, second_instruction) + # After calculating the result it is inserted at the beginning of the tokens list to substitute the three + # removed elements + tokens.insert(0, str(result)) + return tokens[0] + + def _evaluate_condition(self, condition, next_condition=None): + """ + Evaluates a condition and returns if it is True or False + :param condition: condition to evaluate + :type condition: str + :return: "True" or "False" accordingly + :rtype: bool + """ + negation = False + if condition[0] == "!": + condition = condition[1:] + negation = True + condition = condition.strip() + if condition in ["True", "False"]: + return condition + if condition not in self.instructions and \ + (" " not in condition and condition.split(" ")[0] not in self.instructions): + self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") + return "False" + tokens = condition.split(" ") + field = tokens[0] + to_search = self._prepare_to_search(field, tokens[-1]) + config_field = self.instructions.get(field) + if config_field and config_field.startswith("FUNCTION"): + assert config_field[8] == " " + args = { + "data": to_search, + "enabled": self._enabled, + "tokens": tokens[1:], + "next_condition": next_condition + } + result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) + else: + # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use + # (None, None) + enabled = self.is_enabled(self._user_configuration, config_field, to_search, (None, None), True) + result = enabled if not negation else not enabled + return result + + def input(self, expression, enabled): + self.expression = expression + self._enabled = enabled + + def run(self, expression, enabled): + if expression: + self.input(expression, enabled) + return self.output() + + def output(self): + solution = self._solve(0, len(self.expression)) == "True" + self.entry_updates = self._custom_functions.entry_updates.copy() + self._custom_functions.reset() + return solution + + +class CustomFunctions: + def __init__(self, user_configuration): + self._user_configuration = user_configuration + self._validator = Validator() + self._entry_updates = {"levels": [], "notes": []} + self._operators = { + ">": lambda op1, op2: op1 > op2, + "<": lambda op1, op2: op1 < op2, + ">=": lambda op1, op2: op1 >= op2, + "<=": lambda op1, op2: op1 <= op2, + "==": lambda op1, op2: op1 == op2, + "!=": lambda op1, op2: op1 != op2, + "in": lambda op1, op2: op1 in op2, + "not in": lambda op1, op2: op1 not in op2, + } + self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" + + # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: + # function(**kwargs) -> bool + # kwargs are defined in the _evaluate_condition method of the ConditionParser class. + + def check_year(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if the year indicated has already passed + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- Year to check + """ + year = kwargs.get("data", None) + if not year: + raise ValueError("No year provided") + self._validator.string(year) + # This means that the guideline document didn't define a limit to this condition + if year[-1] == "+": + return True + + actual_date = datetime.date.today() + parsed_date = datetime.datetime.strptime(year + "-12-31", "%Y-%m-%d") + return parsed_date.date() > actual_date + + def check_vlp(self, **kwargs): + status = kwargs.get("data", "").lower() == "true" + result = False + for version in range(3): + enabled = ConditionParser.is_enabled(self._user_configuration, "Protocol", f"TLS 1.{version}", (None, None)) + if enabled and not status: + result = True + self._entry_updates["levels"].append("must not") + # This final operation should give True only if both status and result are True or both are False + return (result and status) or not (result or status) + + def check_ca(self, **kwargs): + to_check = kwargs.get("data", None) + if not to_check: + raise ValueError("No year provided") + self._validator.string(to_check) + if " " in to_check: + tokens = to_check.split(" ") + if tokens[0] == "count": + count = self._count_ca() + op = tokens[1] + num = tokens[2] + self._validator.int(num) + return self._operators[op](count, num) + elif tokens[0] == "publicly": + certs_trust_dict = self._user_configuration.get("TrustedCerts", {}) + trusted = True + if not certs_trust_dict: + trusted = False + for cert in certs_trust_dict: + if certs_trust_dict[cert] != "passed.": + trusted = False + return trusted + + def _count_ca(self): + cas = set() + for field in self._user_configuration: + if field.startswith("cert_caIssuers"): + cas.add(self._user_configuration[field]["finding"]) + return len(cas) + + def check_this(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True if either the one calling or the other is enabled + :rtype: bool + :Keyword Arguments: + * *enabled* (``bool``) -- Whether the entry with this condition is enabled or not + """ + enabled = kwargs.get("enabled", False) + second_condition = kwargs.get("next_condition", " ") + tokens = second_condition.split(" ") + field = tokens[0] + name = " ".join(tokens[1:]) + # only the first two fields of the entry matter, and entry is only needed for key lengths + entry_data = name.split(",") if "," in name else (None, None) + second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, + partial_match=True) + enabled = second_enabled or enabled + self._entry_updates["has_alternative"] = enabled + return enabled + + def add_notes(self, **kwargs): + note = " ".join(kwargs.get("tokens", [])) + self._entry_updates["notes"].append(note) + return True + + def check_key_type(self, **kwargs): + """ + :param kwargs: Dictionary of arguments + :type kwargs: dict + :return: True always + :rtype: bool + :Keyword Arguments: + * *data* (``str``) -- The name of the algorithm that is using the condition + """ + note = "" + alg = kwargs.get("data", "").lower() + valid_pairs = [["ECDSA", "ECDH"], ["DSA", "DH"]] + recommend_dsa = False + for cert in self._user_configuration["Certificate"]: + if cert.startswith("int"): + continue + cert_data = self._user_configuration["Certificate"][cert] + data_pair = [cert_data["SigAlg"], cert_data["KeyAlg"]] + if cert_data["KeyAlg"] == "DH": + recommend_dsa = True + if data_pair[0].lower() == alg and data_pair[0] != data_pair[1] and data_pair not in valid_pairs: + note = f"The certificate with index {cert} isn't compliant with the guideline because it is signed " \ + f"with an algorithm that isn't consistent with the public key" + + if note: + self._entry_updates["notes"].append(note) + if recommend_dsa: + self._entry_updates["levels"].append("recommended") + return True + + def check_value(self, **kwargs): + tokens = kwargs.get("tokens", []) + config_field = tokens.pop(0) + tokens_string = " ".join(tokens) + tokens = re.split(self._operators_regex, tokens_string) + tokens = [t.strip() for t in tokens if t] + value = tokens[0] + operator = tokens[1] + name = "".join(tokens[2:]) + # if there is a "[" and a corresponding "]" then the text inside is a level of a dictionary + levels = re.findall(r"\[(.*?)]", name) + if not levels: + levels = [name] + field = self._user_configuration.get(config_field, {}) + last_level = [levels[-1]] if "," not in levels[-1] else levels[-1].split(",") + last_level = map(str.strip, last_level) + # used the in because in this way is easier to edit if needed + if config_field in ["Certificate", "CertificateExtensions"]: + result = True + for cert in field: + for level in last_level: + levels[-1] = level + # I pass to the function that gets the value the certificate dictionary as field + configuration_value = self._get_configuration_field(field.get(cert, {}), levels) + reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result or len(configuration_value) == 0: + self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") + result = result and partial_result + else: + result = True + for level in last_level: + levels[-1] = level + configuration_value = self._get_configuration_field(field, levels) + partial_result = self._operators[operator](value, str(configuration_value)) + if not partial_result: + self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") + result = result and partial_result + return result + + @staticmethod + def _get_configuration_field(field, levels): + for level in levels: + field = field.get(level, {}) + return field + + def check_dict_value(self, **kwargs): + self.check_value(**kwargs) + + def check_year_in_days(self, **kwargs): + # todo implement this + return True + + @staticmethod + def always_true(**kwargs): + return True + + @property + def entry_updates(self): + return self._entry_updates + + def reset(self): + self._entry_updates = {"levels": [], "notes": []} From fb94349f533d09b53b1d8debc4f2c6ba00825abd Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 2 Oct 2023 16:53:50 +0200 Subject: [PATCH 115/209] implemented check_year_in_days --- modules/compliance/compliance_base.py | 2 -- modules/compliance/wrappers/conditionparser.py | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index ad73a76..d732954 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -121,8 +121,6 @@ def input(self, **kwargs): self.prepare_configuration(self._config_class.configuration) if hostname and self._validator.string(hostname) and hostname != "placeholder": test_ssl_output = self.test_ssl.run(**{"hostname": hostname, "one": True}) - #with open("unitn.json", "r") as f: - # test_ssl_output = json.load(f) self.prepare_testssl_output(test_ssl_output) if output_file and self._validator.string(output_file): diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index 304257b..a3f9ca8 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -1,4 +1,5 @@ import datetime +import pprint import re from utils.loader import load_configuration @@ -76,7 +77,6 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match # Certificate case cert_data = field_value.get(certificate_index, {}) enabled = name in cert_data - print(cert_data, name) elif isinstance(field_value, dict): # Extensions and transparency case @@ -407,8 +407,19 @@ def check_dict_value(self, **kwargs): self.check_value(**kwargs) def check_year_in_days(self, **kwargs): - # todo implement this - return True + for cert in self._user_configuration["Certificate"]: + if cert.startswith("int"): + continue + cert_data = self._user_configuration["Certificate"][cert] + validity = cert_data["validity"] + data = kwargs.get("data", None) + if not data: + raise ValueError("No amount of years provided") + if not data.isnumeric(): + raise ValueError("Amount of years must be a number") + years = int(data) + days = years * 365 + return validity.days < days @staticmethod def always_true(**kwargs): From cecd0ea0f0d54d1ce149a31552d390cd7fb794ee Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Tue, 3 Oct 2023 17:29:30 +0200 Subject: [PATCH 116/209] updated conditions for protocols checks --- DatabaseFiller/database_filler.py | 34 ++++++++++++++++++++--------- DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/DatabaseFiller/database_filler.py b/DatabaseFiller/database_filler.py index e438d0e..4ea9e0a 100755 --- a/DatabaseFiller/database_filler.py +++ b/DatabaseFiller/database_filler.py @@ -255,17 +255,31 @@ def fill_extra_table(sheet_name: str) -> bool: table_columns_count = len(cur.execute(f"PRAGMA table_info({table})").fetchall()) entries = values_dict[table] - # This is to prevent the "this or X" condition to appear in tables that don't need it - # this condition checks if the guideline has multiple versions for this sheet - if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: - for entry in entries: - entry = entries[entry] - # Since the problem is a condition, and it only verifies if there are four elements. - # Last element is the condition - # Second to last is the level - if len(entry) > 3 and pd.notna(entry[-1]): - if entry[-2][-1] != "°": + # # This is to prevent the "this or X" condition to appear in tables that don't need it + # # this condition checks if the guideline has multiple versions for this sheet + # if table.startswith("Protocol") and table[len("Protocol"):] not in [g.upper() for g in guidelines]: + # for entry in entries: + # entry = entries[entry] + # # Since the problem is a condition, and it only verifies if there are four elements. + # # Last element is the condition + # # Second to last is the level + # print(entry) + # if len(entry) > 3 and pd.notna(entry[-1]): + # if entry[-2][-1] != "°": + # entry[-1] = None + last_level = None + + # This is to prevent the "this or X" condition to appear in tables that don't need it, only works + # for the case of Protocol sheet and only if the conditions are in adjacent lines + if table.startswith("Protocol"): + for index, entry in entries.items(): + # skip first element + if index == 0: + continue + if len(entry) > 3 and pd.notna(entry[-1]) and pd.notna(entries[index-1][-1]): + if entry[-2] != entries[index-1][-2]: entry[-1] = None + entries[index-1][-1] = None if not values_groups.get(table): values_groups[table] = [] diff --git a/DatabaseFiller/requirements.db b/DatabaseFiller/requirements.db index 8fc761af35e1254b5f5be539483ecd4b72ea7d42..7eadd5f73591a5b7e4e2688809b7f2938a248f84 100644 GIT binary patch delta 443 zcmZp8;MVZKZGsdN&yKPZMCg?%)7IWSpJ{vdeCFH)#_!vI{%2-Q zVEnQDe>}@ieKF2P1_lO3RtF{!#eWGza`9i`VPw%46faIqE-*4ODKb>hR|wDrvWyJDYMK^oTfoTnfzOVC z|33dd{yF?r{4xA?K(n>@rzh4k$??drvM~xvD>JY-1$$0U@Mjiluba=by>32pRRZI$ z?LYrBvnDWp-~Kr{Yc+jJS7`T|wg9i^N_$NYZWqk{17Ed1Dm!0{(-#Jx;0L$n>PE3gSKVF7MKAL!V3 zk56@J8u6WK>2dbP28S-hj9^>GL}D~KKAyN9PsE1?24hBY!iZjs3`P1P=pSZ6UCu*{ z{>4K7fpr+v!pj1M~SU(K2sF zsO}U99zEm<#hk*2+zYFy1lCZMeOO!ekT>A^Wh14Tq`IKG{mgpxBⓈfleDwY7h9= z;3n>44I;DxT4fURU!}QZjrQ%BzevmYW7Xk2M3LrOE1=o}^X8O8ryIuyaFd3E0hlzm zEuh{8%$O#{a$;#%E-W`z4OT5yoit6|8I^Q|RUsQ?;(m%1Q(Oy6ZIxI2**rIjc}7K! z3P8bSj-~`k)LN!2Nn0{A?fi0^8z1hq-J9M6o2SLGqT77uX=+!DCi9#Z1^!P5uU#>ns8Q delta 901 zcmZ8eO-vI(6yDjsc1ycEZx*OEO6U?!mOoFM zI7mIn&xszXsdT)kob;$6{+?_+c|buiXlrGCTamOLzL(kezMnU~mT;98>TkuHrmi$6-8%`}sD1%?o^n zkMm)EoOf|C|j^S1$|HmT8vu zh9hXH6HM7Q5ZnY0K_hqxO#~moFKt_QCv%V&LPFB0;;pn%Omk0MdS3EMXf})>A$lx Date: Tue, 3 Oct 2023 17:47:18 +0200 Subject: [PATCH 117/209] added some debug logs and improved "this or" checks --- .../compliance/condition_instructions.json | 1 + modules/compliance/compare_one.py | 16 ++++++++---- modules/compliance/compliance_base.py | 10 ++++--- .../compliance/wrappers/conditionparser.py | 26 +++++++++++++++---- modules/compliance/wrappers/db_reader.py | 3 +++ 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index e9f1b51..bc062a7 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -1,4 +1,5 @@ { + "PROTOCOLS": "Protocol", "EXTENSION": "Extension", "CIPHER": "CipherSuite", "TRANSPARENCY": "Transparency", diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 6eed1b5..cb4be44 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -37,13 +37,24 @@ def _worker(self, sheets_to_check): valid_condition = True if condition: valid_condition = self._condition_parser.run(condition, enabled) + enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) + self._logging.debug(f"Condition: {condition} - enabled: {enabled}") if self._condition_parser.entry_updates.get("levels"): levels = self._condition_parser.entry_updates.get("levels") levels.insert(0, level) to_use = self.level_to_use(levels) level = levels[to_use] + has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") + + note = "" + if has_alternative and not enabled and isinstance(condition, str) and\ + condition.count(" ") > 1: + parts = entry[condition_index].split(" ") + # Tokens[1] is the logical operator + note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" + if has_alternative or additional_notes: # This is to trigger the output condition. This works because I'm assuming that "THIS" is only # used in a positive (recommended, must) context. @@ -51,11 +62,6 @@ def _worker(self, sheets_to_check): # if it has multiple name_columns they get only shown in the output name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) - note = "" - if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: - parts = entry[condition_index].split(" ") - # Tokens[1] is the logical operator - note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" if additional_notes: note += "\nNOTE:" note += "\n".join(additional_notes) diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index d732954..fdfbfd0 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -32,7 +32,7 @@ def __init__(self): self._security = True self._input_dict = {} self._database_instance = Database() - self.__logging = Logger("Compliance module") + self._logging = Logger("Compliance module") self._last_data = {} self._output_dict = {} self._user_configuration = {} @@ -114,7 +114,7 @@ def input(self, **kwargs): try: self._config_class = ApacheConfiguration(actual_configuration) except Exception as e: - self.__logging.debug( + self._logging.debug( f"Couldn't parse config as apache: {e}\ntrying with nginx..." ) self._config_class = NginxConfiguration(actual_configuration) @@ -352,7 +352,7 @@ def _retrieve_entries(self, sheets_to_check, columns): Given the input dictionary and the list of columns updates the entries field with a dictionary in the form sheet: data. The data is ordered by name """ - self.__logging.info("Retrieving entries from database") + self._logging.info("Retrieving entries from database") entries = {} tables = [] for sheet in sheets_to_check: @@ -436,12 +436,14 @@ def _evaluate_entries(self, sheets_to_check, original_columns): enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], entry, condition=condition) valid_condition = self._condition_parser.run(condition, enabled) + enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) + self._logging.debug(f"Condition: {condition} - enabled: {enabled}") if self._condition_parser.entry_updates.get("levels"): potential_levels = self._condition_parser.entry_updates.get("levels") level = potential_levels[self.level_to_use(potential_levels)] has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") - if has_alternative and isinstance(condition, str) and condition.count(" ") > 1: + if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: parts = condition.split(" ") # Tokens[1] is the logical operator notes[-1] += f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index a3f9ca8..61ab384 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -1,5 +1,4 @@ import datetime -import pprint import re from utils.loader import load_configuration @@ -13,10 +12,12 @@ class ConditionParser: _splitting_regex = "|".join(_logical_separators) # same as above but also captures the separators _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + _sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") + __logging = Logger("Condition parser") + # mapping from field indicator used in the conditions to the field of the configuration dictionary def __init__(self, user_configuration): - self.__logging = Logger("Condition parser") self.expression = "" self._user_configuration = user_configuration self.instructions = load_configuration("condition_instructions", "configs/compliance/") @@ -102,15 +103,24 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = ConditionParser._partial_match_checker(field_value, name) if not enabled and check_first: enabled = name[:check_first] in field_value + else: + ConditionParser.__logging.warning(f"Invalid field: {config_field}, for name: {name}") return enabled @staticmethod def _prepare_to_search(field, to_search): new_to_search = to_search - if field == "TLS": + if field.lower().startswith("protocol"): new_to_search = "TLS " + to_search.strip() return new_to_search + @staticmethod + def prepare_field(field): + # this step is needed for the upper-case letters + field = field.replace("Certificate", "Certificate ").title() + field = field.replace(" ", "") + return ConditionParser._sheet_mapping.get(field, field) + @staticmethod def get_check_first(condition: str): check_first = None @@ -184,7 +194,7 @@ def _evaluate_condition(self, condition, next_condition=None): tokens = condition.split(" ") field = tokens[0] to_search = self._prepare_to_search(field, tokens[-1]) - config_field = self.instructions.get(field) + config_field = self.instructions.get(field.upper()) if config_field and config_field.startswith("FUNCTION"): assert config_field[8] == " " args = { @@ -194,6 +204,9 @@ def _evaluate_condition(self, condition, next_condition=None): "next_condition": next_condition } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) + elif config_field is None: + self.__logging.warning(f"Invalid field: {field} in expression: {self.expression}. Returning False") + result = False else: # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use # (None, None) @@ -312,14 +325,17 @@ def check_this(self, **kwargs): enabled = kwargs.get("enabled", False) second_condition = kwargs.get("next_condition", " ") tokens = second_condition.split(" ") - field = tokens[0] + field = tokens[0].title() name = " ".join(tokens[1:]) # only the first two fields of the entry matter, and entry is only needed for key lengths entry_data = name.split(",") if "," in name else (None, None) + # The field must be prepared + field = ConditionParser.prepare_field(field) second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, partial_match=True) enabled = second_enabled or enabled self._entry_updates["has_alternative"] = enabled + self._entry_updates["is_enabled"] = enabled return enabled def add_notes(self, **kwargs): diff --git a/modules/compliance/wrappers/db_reader.py b/modules/compliance/wrappers/db_reader.py index f36b469..d74c63a 100644 --- a/modules/compliance/wrappers/db_reader.py +++ b/modules/compliance/wrappers/db_reader.py @@ -2,6 +2,7 @@ import utils.database as db_utils from utils.loader import load_configuration +from utils.logger import Logger class Database: @@ -17,6 +18,7 @@ def __init__(self, file: str = database_file): self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") self.table_names = [table[0] for table in self.cursor.fetchall()] self.__input_dict = {} + self._logging = Logger("Database wrapper") def get_table_name(self, sheet, standard_name, version=""): """ @@ -84,6 +86,7 @@ def output(self, columns="*"): if other_filter: query += " " + other_filter self.cursor.execute(query) + self._logging.debug(query) return self.cursor.fetchall() def run(self, tables, join_condition="1==1", other_filter="", columns="*"): From ca1eb92f82be295e0fc3e7319ed441e46a9a02d7 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Thu, 5 Oct 2023 10:24:12 +0200 Subject: [PATCH 118/209] fixed issue with logical operators inside conditions --- DatabaseFiller/guidelines.xlsx | Bin 102555 -> 102582 bytes DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes .../compliance/condition_instructions.json | 1 + configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes modules/compliance/compare_one.py | 2 +- .../compliance/wrappers/conditionparser.py | 5 +++-- 6 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index f87617f217c15529d5102f7dc0603e9458e3d683..d9f7e59bde04a74ea6a11565f4efb8bba11f6ca8 100644 GIT binary patch delta 39174 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 Z!14>s&M?W=M4$m(ESRQPU}3#v{sHr)H&p-t diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index bc062a7..2293c68 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -5,6 +5,7 @@ "TRANSPARENCY": "Transparency", "TLS": "Protocol", "CERTSIG": "CertificateSignature", + "CERTIFICATEEXTENSIONS": "CertificateExtensions", "YEAR": "FUNCTION check_year", "YEARS": "FUNCTION check_year_in_days", "VLP": "FUNCTION check_vlp", diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 7eadd5f73591a5b7e4e2688809b7f2938a248f84..7b298988e39fdf89dc76bd3fe819555ac7f3db45 100644 GIT binary patch delta 1116 zcmZ8fO>7%g5Z@X)hcOL)s*Gr zFRR=+vZW4tl_}rdQZJOKitEbZR^L~wkw_1!Dq*r+3=DAk;QxRk+h;u>><3~h`CYcx z0|SEo?eBX|LD~Mn6Kweb6#Q9hy8%Idm7O6<1-U2ParzBtzl=~aNnUa4_xKZzYf(L@ zqj2|>k0?9Kc*;&%#Z2H1X2pd&8z%V@w60Gn-6GXFHT^RO)i~*2@q{6to&}A zCs=b`iV?&JVQA8I!`gITOo5-kfgHr}PY6QPq8f?CV=E*uacpwRp3RnLoOeFg|>vt8n378xPjMv!?`-BzP zAx8DPIPt13pZri?#ltD73PKudAR!mq^FNqf>kBQSu`K&#kFW6;K zS}4xtPvsZI;+!p($}^{JQOt>_MZQo*POn>DI`x)4yZHS4;>66`w#en}`D`Mjk?7bn zrV>OyXQiMII+=Cdlw)W0TQ_R7&bg!+)`is)YnF^qT83^LouP{fppw>&?vU5yy(Tod zxzX>mF0iotq90yH!Zb%hol#z+8NgKW()52-3DCR>UAWRR_^u_sE|xs=7FNx3obnCI zvB!bdXiuP^k?9gsAQ_?3jgo4AzlgepQ6-hpc-6*YRFa>dQi8va5D-X zz=M$*3BCBygP{ixU>Z6X6Nv{r83-D^m`I`r4`A^lVzIi;VuHoP`)A+0|8L$uyF0LO zWMJV)Y6}{@U46(oD(yXa>~VKC;`G8#_|A2PKZ1&&Uy`KPIqQrX2sBFY0iHnyu0jXw zht2c{eM6_{9eSB|&=$IfddLcSOP-LsML53}sM0BZngHc*5*6yUYv8 zbW=gz^{?fZ3i7=;c8Jh?`$EMlk_&_@Wf^&R(R6qi{`UU>@#2U*P|F8C8GYu(35UnP zrSfz8CWRN@*}ziyKmaS*qUA8qB471mY@kovYR0cYE!1KZLoMmaMYfY$5AZ7ZtZU*W z{s_xL5zGsfe#88fj-G@1YlGXw8pB%oo=Uk2xd&~~S;nJ^HGT$M$hF~ur??2Jw0czl zU$`}4=lNP@NLvxCId!pM{R3|j*^tDoS=%db@LqFh_|3*JF63JaL%3D2fyy#q*s`!F zrzk~~$}P*Kc2)!=cn=RD2?6mx7~nd9738b(ol9>s-Brrr6K%29qv;mY9of+&D{i0H z*K@oz)|N<~z1W}ZYintZ8D_#b^w&2$W4c9Eh}y(wOs$!1B{dSffV(gN?a%-Y`iMr! z5}779NIR(!8y$qdyr~T#ce|=0Z|zn+neM)&MMyvqZNqL|avqGerUx%+mFeh&rt^jo zZFZXDPEvyLC}Bf!l8;j*%#}I1>K~t^Mt+iDmc0Fm(#yFma z667-k8yu&Y%(#+*-@x&*I3CXsmX9-g6lEnC%K*;eunied@{i3?yVb>p6k}6ZFZsta Z!14>s&M?W=M4$m(ESRQPU}3#v{sHr)H&p-t diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index cb4be44..c66829f 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -63,7 +63,7 @@ def _worker(self, sheets_to_check): name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) if additional_notes: - note += "\nNOTE:" + note += "\nNOTE: " note += "\n".join(additional_notes) if self._output_dict[sheet].get(name) is not None: self._output_dict[sheet][name] += note diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index 61ab384..c27b0f1 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -7,7 +7,7 @@ class ConditionParser: - _logical_separators = ["and", "or"] + _logical_separators = [" and ", " or "] # simple regex to find all occurrences of the separators _splitting_regex = "|".join(_logical_separators) # same as above but also captures the separators @@ -164,7 +164,7 @@ def _solve(self, start, finish): tokens = [token for token in tokens if token] while len(tokens) >= 3: first_instruction = tokens.pop(0).strip() == "True" - logical_operation = self._operators[tokens.pop(0).lower()] + logical_operation = self._operators[tokens.pop(0).lower().strip()] second_instruction = tokens.pop(0).strip() == "True" result = logical_operation(first_instruction, second_instruction) # After calculating the result it is inserted at the beginning of the tokens list to substitute the three @@ -206,6 +206,7 @@ def _evaluate_condition(self, condition, next_condition=None): result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) elif config_field is None: self.__logging.warning(f"Invalid field: {field} in expression: {self.expression}. Returning False") + self.__logging.debug(f"Tokens: {tokens}") result = False else: # At the moment there is no need to check if a KeyLength is enabled or not, so It is possible to use From 759f16f85cf7e8f02b76723bf91bf90ffde58e4e Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Mon, 9 Oct 2023 10:20:34 +0200 Subject: [PATCH 119/209] Added SCSV check and fixed minor issues with conditions --- DatabaseFiller/guidelines.xlsx | Bin 102582 -> 104516 bytes DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes .../compliance/condition_instructions.json | 5 ++++- configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes modules/compliance/compare_one.py | 2 +- modules/compliance/compliance_base.py | 4 +++- .../compliance/wrappers/conditionparser.py | 12 ++++++++++-- 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index d9f7e59bde04a74ea6a11565f4efb8bba11f6ca8..c7f70ef3ee856618b94dd5e659addfb699b416bd 100644 GIT binary patch delta 63238 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

HR+~e3Slk;B&WV7)B&dP@rGKu-zvs+aT<@v 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 I?`_Kb4cGF0=l}o! diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index 2293c68..1c0baf1 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -3,8 +3,10 @@ "EXTENSION": "Extension", "CIPHER": "CipherSuite", "TRANSPARENCY": "Transparency", + "GROUPS": "Groups", "TLS": "Protocol", "CERTSIG": "CertificateSignature", + "CERTIFICATESIGNATURE": "CertificateSignature", "CERTIFICATEEXTENSIONS": "CertificateExtensions", "YEAR": "FUNCTION check_year", "YEARS": "FUNCTION check_year_in_days", @@ -14,5 +16,6 @@ "NOTE": "FUNCTION add_notes", "CHECK_KEY_TYPE": "FUNCTION check_key_type", "CHECK_ONLY_FIRST": "FUNCTION always_true", - "VALUE": "FUNCTION check_value" + "VALUE": "FUNCTION check_value", + "VERIFY_SCSV": "FUNCTION verify_scsv" } \ No newline at end of file diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index 7b298988e39fdf89dc76bd3fe819555ac7f3db45..a87126dc0436ede97955f440fff368a27b0d2e5b 100644 GIT binary patch delta 2859 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 I?`_Kb4cGF0=l}o! diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index c66829f..2edc100 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -47,7 +47,7 @@ def _worker(self, sheets_to_check): has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") - + self._condition_parser.entry_updates = {} note = "" if has_alternative and not enabled and isinstance(condition, str) and\ condition.count(" ") > 1: diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index fdfbfd0..725b3f1 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -291,7 +291,7 @@ def prepare_testssl_output(self, test_ssl_output): # The supported groups are available as a list in this field elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ - else actual_dict["finding"] + else [actual_dict["finding"]] self._user_configuration["Groups"] = values # The transparency field describes how the transparency is handled in each certificate. @@ -325,6 +325,8 @@ def prepare_testssl_output(self, test_ssl_output): elif field in self.misc_fields: self._user_configuration["Misc"][self.misc_fields[field]] = "not" not in actual_dict["finding"] + elif field == "fallback_SCSV": + self._user_configuration["fallback_SCSV"] = actual_dict["finding"] def update_result(self, sheet, name, entry_level, enabled, source, valid_condition): information_level = None diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index c27b0f1..680b544 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -74,7 +74,9 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match if enabled is None: enabled = True if "all" in field_value else False - elif isinstance(field_value, dict) and field_value.get("1"): + # the extensions are saved using their IANA id as key in the dictionary, so I have to check that "1" is a dict + elif isinstance(field_value, dict) and field_value.get("1") and \ + isinstance(field_value["1"], dict): # Certificate case cert_data = field_value.get(certificate_index, {}) enabled = name in cert_data @@ -110,7 +112,7 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match @staticmethod def _prepare_to_search(field, to_search): new_to_search = to_search - if field.lower().startswith("protocol"): + if field.lower().startswith("protocol") or field.lower().startswith("tls"): new_to_search = "TLS " + to_search.strip() return new_to_search @@ -438,6 +440,12 @@ def check_year_in_days(self, **kwargs): days = years * 365 return validity.days < days + def verify_scsv(self, **kwargs): + scsv_finding = self._user_configuration.get("fallback_SCSV", "") + enabled = "offered" in scsv_finding and "not" not in scsv_finding + self._entry_updates["is_enabled"] = enabled + return True + @staticmethod def always_true(**kwargs): return True From 37298b9e65dea08ac09513763e021cf7a85f5f64 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Wed, 11 Oct 2023 15:11:58 +0200 Subject: [PATCH 120/209] Added conditional notes and fixed small issues with analysis --- DatabaseFiller/guidelines.xlsx | Bin 104516 -> 104846 bytes DatabaseFiller/requirements.db | Bin 1159168 -> 1159168 bytes .../compliance/condition_instructions.json | 5 ++ configs/compliance/requirements.db | Bin 1159168 -> 1159168 bytes modules/compliance/compare_one.py | 15 +++-- modules/compliance/compliance_base.py | 27 +++++++- .../compliance/wrappers/conditionparser.py | 63 +++++++++++++----- 7 files changed, 86 insertions(+), 24 deletions(-) diff --git a/DatabaseFiller/guidelines.xlsx b/DatabaseFiller/guidelines.xlsx index c7f70ef3ee856618b94dd5e659addfb699b416bd..006b1cd325f063d4e14a395bb36d2e7b7892a448 100644 GIT binary patch delta 28640 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 Wkd9s1scQ=7&L9!PW{v zx*fw&k;0JvngI8>-E6FzwT5h`1cpw63LM5%j5x+PtFQ znb@BzRkJ1K6n4|6=hL@4ra!qf=TBFDCx^o17&oR1%S}x@v z32H{kVE1vnDRg_}x2%pLCLBL&8`Ff}bNZdTW4~EO>s6a8;;erCCCb+_OX)dL$CrRo z8!SEJ1^+{2>ty`P{)cbb85o%sTIzy!Tiu7cuAb+aV|A-2dV0wS6YH3g>yfeNW(lZ~ zHxoBa07uUOJdMg7^M|PGYN2si6p;(5t|Z1?iVB8S{rkhVEoM)W+TOHxyye2TwVwGR zE&b%MxqX(S3ddfHA}1?cs|zmvmCwZ%^oHjevRV4D@E-cjEzmDXWQ5@}Djig%&M zt|P`%hl-hZ;zmCZy=C;uucTi0sNPbWaXf2bd_vDLJ4#EE44^d#*5E^lxj5d3pHI1c z83itQ+lq1zN_FvR3)ay4stBoLwmzcPA6cFbYlberWok${crG*EFA?cFNJ=Yxt#E*DWRf`36pvS zB7s11*dWmVq%zzOgY2I?uE+ZE&!h(TG5GJK7Q9aCrKjq4!@PJtW&~G1B9ty$be}mX8Lz-{*aH`V!YZIJ@tjrqPyf(t?k+AQZed&=EaqM zMS)L#SkqhTnm-%}n_xV)XwPRktd0HEgF?Q+p6a289B?wCyH7SSk0h!iXQM0{5GJd|XN;o1bO6E*3s0M`oAN`G|u1&Ow)g*@MRhEG?DR3-~QQ zL+|N|&aQY&=`YalbKtWh8r4r_{ggOLW!^|@{6vT1S3!V7EE#!$pu+Oyi&$S;Th396 zj}NV0F#XsjGoVPoWF+pDVC=>?ywFG9S+&v9*ZR=3Oo=irs53gsc8uUwAF;@cbpf`n%FOFgk>zhuzYOi5AO{M=@C;KbCB|JSgK8r+pPZrx2eVeW0N2OQ`Px7rrtLN{1z0dhs&fNEM>*`D z`t=zOmSfe_J5^djd}*#ld?lP6dlrjUBylHzLa~E)`yrl!bqx;cuYJAOEs>$aJtI-y zpXR+Kri!ldkaP8WNQ+xDWFbJdo{jY1i8K~geWPGT9 z$_ZiuE2KUDYYhKPSN*5X*t2NIQ{j$h?W-s5^rXsIASw?@hXmbasNqd!Oj+DpP0?XgmxRI=bHjJqLNvj6ReO5=axi81Z-9 zqFDd%{sesJYAMT514v_dhab@o2)9k5v)yBQnjY;cDpaSM_OoTHU$pIxU^WiNRs_?4 z6S_Hc!1DL1WGQv}(EPK?Utkf7DU;xol}?MUaDt1o?+Lu}LC>+PYKj7!NP)QgO#rO0 zvUezo25i2OV6zda4!e5Bb)~v`A;)csG_#wX8`cVHQi@Sf07$QN%sihK6jO8=w4u}B zssLX;vhY98iN=>OcSiM&{lPb}-SYbhr26W8*T}s$%tk4hi&?D0d2Hmv-n)gS1N?x} z-73aenY-kJTynAP(Ghrzq&y0xrF8Y>jC_n*iHu`tu|3HO++5v)F=7;N9(-LAnnFQK zC{E<`z9LWJzy$Vec;(!jeCzMPld8Yr>VMoXH6qhVNMbwe*yUGw72rO~*;vD?&ie_y zmkEVAoz!emCn-rsV4K@)0tA0~y5at6;!7 zuO|rtl7C-`6($7MXc;9#A)_OOu2Z=mhPgRV(>gv>#B8cn7R>SuR+VhT;c-j7F8B8F zij=Tc(%07#mScLFqJY1!CP&J^u93`F@pfmRajFOUXc8|5pBoyn^Ul&M-AnuVJyvNs z+3?y{ z7D?YVvagJb`4Q{yP?;ziplEOYQu*HZFWLz(h-rr;3f=FJbdxowaG2=?e_u;xh$&x7 znV#G4h!u}Jd4NWdGJF;^n2J&=T6o0=p@AMFQ(?Byno%rxeWa+ceB!=W#9E5Vrzozm z4dt+d=IUImWy-$Z@ZgI`Js_Izp}Bnk}oC9Rk^-fInE*VY3lv8z+-;J7Wz zrLu413Lmn0Dsx+tCVr^(-&gKQeaC(Z-fmzA4xS9L#MtI3E)((}WVH>XXUYzzJRY1T zuNHac`3hL%QdV$Ks>`qbRgaoGyQDcksiIp!Cg0K{dl&w~rn!`wz|-%9`pY1d)H~$6 ztG*>Y_KX*f@+xw%EHPM(j0FL)Sp9Ts5sa$z^{cV_Dftpu-P@G0@EDT}V~6j;%xAv) zDB{d33}3`kH%c1oab$K&`bKFK-pN$T3fjq^KISlA1v?y)GdoC=Zh?E^ko}u*HpTGB4qD(o@55oNmTPeof5d|3g|N(uRVea2BzKzZW<-rX&o)s*3E;`gVUC(Ay=w=JCcoAU7Go z0}uDVJjxYV;oH(0H`#POv5~vUCV06*{Y_e3pN&(tyje@Zs`5A8JXnXLSbbZ$7 z#lJ{C=>KbSVsJYFs}d!)ii7bI%cBr_C>XIo|CElbNOoAd+3BD0UnKCBS98qQXW&_p zMt`Y$b2pqVdL9!v-#_c!VD?FG9GJV@FV;H7Dz(6VAa>#5b2@sypQ_o%wx3*JkoRg} zUvmL5XwbMEWs|s=-}nY=D|0)oJ{v9@nhj_XFU>mKAv^gbAyKb4XF%k+?AbYhHTo*( z<2RVoz0;u01s3PV<5QmG2RqHnwkLEa$zy#6$Jvn?Jr@Ao#Mgx>PaB$yREaCM0SWQ1 zKIbtR2(i0MQ%VFENiA*)iCR6)ikc4!VurpmkxlUdv`HZoRrqLU;H0OS5 zOgJwcellRO$d9{rT)GwIpMUbg%4aNP)>-j>!Iwlkp0(}L`>h-NqmJaleGf%iHkJ%j zs?STgTCISdEs?ww&BdPDL?^eggjwg5b>WD#WjduUn^a|CXhC@}i*d-S8I+>Tf$QW(5JISv3!v zmGja8#Q7(Vp~e*sYTX|`5QRcYJ`i2qUDs<RF=-(D5lG86s-Hi8l{b}w4tcSvcO={s+^z87)kohv-eQYD6G_GbJ? z#^c%FW_{0z!hdkC@r|45~Tsb#=nO&N^HyAnqBM8YpRcu8`i8J~MgRX=mfTGRR)#dvEf?hpo%+-)nIlsV02| zqBeh$aQ^NqZYr9Y+#y*s#eV*V#QsrZ^#a!y3W{E5bEHYJnqb}XIsUTUA1+p1y3vAy z=ETq2$Tq)6*3y>Q@s4F-o9~TN+xl_W((=5nb!mdSM+7{}f~F1GZn^=OyWFV%h#~4J zZW{U{CLQM=F=15yh+$;A zKm40^+x3D4GW(1@>!4xZci4G+{%8Um{H+S+?&ZHU$?hy}g8sNR_yOKOn)J~Aqse>H zvRy^DYfTcR|D{RLUz*6YnTm3jQ<040U|I*)-zqZg51BH0w=sxPw<%FfRk;CfXbg-jVdK75QCj>E!`#mKp*3l^fO%M#oCr9gTncP5F1c9~Z zlmF*FsyhMZP1eDXsjM~^)^e)lTa@iFkzvOLT+ol-%c(G6D(|UaDDUZD32n5njsiNg zr0Se8Swm4G1V0MoFqyJ2SaKT`n$aPkPwKa+25W^=pdj9F2#6^V?!z3?yqmh$z-k?g zF=b?X<7%8*Nh4TDp%u)&FaU8#0SL(7l(Q!LyS>1wc^I(CTZ_Rp7w%#etZRtSs~*Qj zGPUc_9y4JRuPy|lF9YU<`~jK6_YsjAgJJ1Iib1o99(>a*28?lh=ynLi1G(y&!86Hd zR!RA`Kc1|7#rSnzp2(LGL!JQ2ivGt1A3!YXZ7|<@8~`D})J`!D8qb=kNMy)_QAhA6 zw4Z}@vq9Y8KM~hMox~c(tL~e`YN$*k$$S$lkU<%+1!P&I3T%_mETB$eo9!#84oYLC zRgo+ng!G$3N8UnyA^(X2PEU_)&O;fnSGKRXx^IT>AG9&>h@e;)Nx-#>bq;Z4b!bB& z1hU}JY+qUM2Wd>99O7+O3}2!@K?47!9$NXH2&x?l;g$s_{RM$0(IDF`1enC8bnF;+ zh@sJ;5Jl)M1VyZpPj4YuVgEY+lnBIu5kd_8f^iFhu6o-C0D-|D2(;p$WvrQ6L~|S% z$Hcb~e98vB{0o9g)-V%w-%3_P9ileQTL>~`!9#yRV5v^>J=@nx9aO_gt9uK9Qy8QP zdJDmPHfN_Scrn}8LEX3h76LU=C{Ti#bOXT;S)H~p$h<6gH`~|!76N-7j5g9+2zJ6M zS4pAAVUSB%FbL^33Ji(VcyA$4x?`Hfi=ll7${P+LN4bTdgH`h7Us1sKb^axhFCWI2 zJ5Y11TL_{6^|r3RATWu8cC%)h5EXTB;q-~8S0=xR$B902#6ve;V8EdDCTe`JOV4{_~xto4&OomM3Y06v2P(L zeWYU;0jYchexBp|;jbvTj{%~%g&-!Pl9d9Q6mgx^<2X{2zPI(&7m*#ehtr$jT%t=wKwoQVv{$@S;A#1P)18qH@t&H=a?Mg2gbH@5Atg4}Q2 z*d_Kg+ns1m48x5YDv5jR#&vbcgIh21c*gBjQoS=075n7k76jvYPY!@jS!unAGR1FQ zHZ2F<`peuCb&~xY-&1wp3)VTGTXTO!L5}}27nI8hkq1L^eZd+au-{GMO433Z@ovo} zm)8-BhEU6c@p669AUAodCWTQ>duy&mbmcfe3$>4ih{%Jfk#C|_GLVQ@`qo@gI#WMs z40*a*mpZ87n;Q+T$Dp7ad#{(F9v3ugd9YwEi1JoK+e3_1x?2g*m`Xf)C{_$4L>?@A zD}eSbQKrnT07rVjbVmlmmmVq;1Ib0Z0o0e6Z7%c{&|2BA{$WIevKZU+P%r!&Kz;c% zKxBS5l>s**PL`ZaGJ>f55r!edZA{tc`U*jAilaUi8>Xm68x&KmgbzJaE<^*~kUCXh@2(wWZ%e@C620{tJSm zT+VF;@JTMnTcb_<7J_3ID2V750yIS(qBsbaA{aFf6!=#Zs9~71-a;T7R~gL;RgQzO zDuRijH(^GbLzJnGfPmq8jof23-BHH?u|WglATk)Y5Gb(C<=#L5o*D(wf=uAF!z-%4BEj)$gut{**KJ14!*Y$xoBj;bP!yR1 z`d1e?MF+!cI>L0Hz!@mN$wk;zDGoB&JAX8@zYd^_Ax*?X@0l|Q`++_2+07}nNt`mdvr_A8>F10zac zo+M%ky>1ZOT+k5Gz&6xVpSZMZ9>P?=9AW;aK)`mo-q9?5$EgLC5dCrf=FRvI_J7*1 z2^{!N7ajzr)+G)%9=HbqbHM5lI^n^xzPh6-C){*B_V1~{$neZT9Wd-_@bq6tBW>JG z#co&h`=??fWZibe*1<6Rkt56%)0>*Lxs3%=e@_nfhw+W1UmIi#)@J(?bdxvZKR&usDqH2NPUv5(k2zpRoB)n@9xOa|o6CXT@PC3x3e+uD%N9J?}4bakcRqCjs?A>6zFphGH0J zQ_L&YBVp8-r}5)6R4zyCu5AQ#$)=&vlI4^$-a#77YZA+A4oy`goFE>YZ3?e@$R0pY(n_klFwv^MLSQ*3%!w;m&pZQ#L5kDXZe5Qc@1{d|Qm zY~laSFhaeuhw$iQA_!dK#1xEpQ3lg}{YP^{W1qiG5|}(0_t`-&9ixL89;}09U|k!u zw`jnk_Y-JOCdIuV+HB@jHh=CC2bT~{b5%vSh}Yag7QKH4hL!CLBr>3Y#S;6Fz*4o3 zk-@28ET0Db;0y%Z*Is}PZjjz`O>BdJ9obG!Jg9s+H@5E~Xm&)-Yn-e6B2KEk&yf)T zc-9X7qg7%#6kPTIBi?=>65kF^q(ON4+Q6zCqCZaj{V$s8cJLc8Ec4<+ zEd&qj3r?iM11GVGzqoPR$Bk{m|C@loL|a_&^F)0dFd5sa7sBHR?~#>#8u-6@JW&l7 z>{jc82Sx_hBBYt#zFtcP=yq@L`lu-NC(gQ5eaGk!%Kzh=#MhYMbqwOM4c=`LtR@_! zL>hE3DauXECaR-?6aGIVwKfX_oD2pqo_#gA+-W`E{xooPA}DdSYW1stnN zoL?RR=fCZM^I;mF%U#uTQmu>M1vHnxdq_|BFRl(x27n8mQvfkO4iuO0z5?EFsm|J6 zc=s1zOAk(iHlEmK9?teD{y?ab$P6M zC2~=`by(JNwI^}tx+mc!vDawOb;Qwz#N}t#){EB5-(#&8QNYfULEF{w@RrZ{nybXsUSh^x z>+$a{;QVL7?D_dZVnz(j<(g{i$wgx8=~m16AcEuUdEeEpDsZ~9a&3}4(AzdCB|MBuv#L|tv;N}T_!l{i0~T}C|_ zy^!)bn7yJk@GX>}vfAoa}x&UOPg_ z0xlK~&So$6FA(~e`Lr+vWL9_cw?ceNzJ-+J|P3)ri@ zI?n)3_*O2LBtn3bS%k5zmpy?0f! z4mJ0G67f%OU}!e@EX_eE|gQlVak&>WyI0O%)TLbmlFkL zjOAF;_iYRAc@lo=5?&Fd>|Rh!_Ii-__O)bg$ZLW7?S|&$teCHfUTtaaozWBr$5;>% znc*wj2jwr55igHT@W2?DX+BfaS@QaoB&B#N?g4F;q4rT4+F16HcMna16)@NunYNzA z5F#^3DH8Tj<9Kttd2RL#16(hMwB_Q^DNDfz%2D1HS!+|tZx=`dTEkO`t_PVD<}_qOpFs&?u#{%`mjT5W3SS7 ziW&^?*ZZorH4nJ5^j6%rt(O3AwX*qTL4E0$KH8ETP5Ea%QQU&XaA?@YOvuQ$L{A_m z^1r>eFCCt(taH2~thoK!vNA%KRDa>vf+SC@uuWRmxCMp|DQaR?}vxevT;=^1pBU;g@{OJEdstVZU9s zArCB+uc6%OliI_HmAR?}j@_nt#%$;kvsq20yitNC0vFMnkL5X@YdzXn&xR^i*@^<9 z_n#lRRM`rGbl0|X+v^x)&GW#1F|jqJ$tg8c87>kgo`l~Nr(hPI4OPURj%8#8GYV6U z=`~{;=iS3^Hrb7=B-6{C@8nH1%nsz!R5qhqyxHt2_kJ-Rx;<@|zvlGu&@Om$wxas} z9oIz;3j}f1R$Ix%Wz`woRBAN4O);?JnL)qHjZ*p%?RB!cip`Ss zW~`EF1Kv3bJg!`OFi{TIcA9*uSseqrL+HX!K!_*$2w$F0iP$@V*T~u@o1G94Iy-=O z>??ikq-Xe4)sU*4SiosHB-^V+u6h;lbg;_uNS7&uN_Sj`UwTN@euIBT)?v0bfLZ9Y-vRjQzsywhG{ zr<1biiP`I#_zJeDW{L~~so>%N_sh8JYUaof7X0681_=FmXzuw$Y>oiYh5va#Xz<9Q zk#&a|ZH=aJWAmJ?WfA-VA*ZY6r+M(SQj7RY!Z0E)Sdm!na$agnnxj*_oR;tEiK?A? zh`xLZRG}$)TP6YT?ZqNdhbrdUm_ZHAV|3Gc(u^kFx82enEXn)ByNxGSu|D1J(7W-8 zL*Q?K{j?QQPm#X!R|M`8jP=n;?}S+_7MJ!1_AOFgXw0W8Y7nUq>j>n>kalR~85ZD0 zEPCO1RVtiY$;>wqC@8Eh@n8HBt}}^vzTep`>zBc-x(Cvg3>JYN~MM!5E zXgZYQ@OTp^IK^lj)pdL7s)&1BT;A`2nX4A{KpQ<99oQ^J?- z5W2_tFQsTw`l5bR{G{`9xd=F^`-R{@Iad%edu6?M?KeK)!g(e5jP)}C!>m+?wY=zq~pFOYYt2W z_ToHV8+H-1;mNl6ZrFsR>Lepmttu*TCv52VMl`#tMZiyLay&D!TGAAa#H6PfaK%F2 z%5HNG@v7yAsUm?>JHLrsKF>7F`DE+9t3$#+659dMi-_%@(f=4@eY%#3qPZm%*pB3= zNB5rU!%D>0CUvW4cE5E4&>M=>p4q7Y*s*$JcdAof=%0@S4JuLF+)*Id>IejX3`Fc>3 z^)dt;q4fmF`q=C@sDD@PXF?i`L@|F&#D>i)8c>TZDZ0(y1>%gr`lNgh^a85p(#7|n z%gov$&ACr)iL6ziA7s`mX_X_X0FWt1&V)a8wzffLrIJ+a$1=yResJ~`nMf(+xO?@CY)n| zjg}RUs%!Gdu3n%Xe3RBFtY$n1d;iSQ{6T-kEBfy#t6vkv>>x zlZdU1MN_C!VwpPqh^OmK_pN_bF(l^+9xgOq;I7I%e4~SM48nJ{8k|M+*&+Rxa(l}& zfa8LIWceyt>=dZIR{zJi{X+grze2oam9}a4cwM0w;$A`J&rOAQUb0J0H-pz3i>fyB zrakOFVSGW8HI#wN@ztQX03pUM|HD4~1JcKf!BSHsNwad} z0u@89r8uvhl99}*44Dh#RaYRQxAzBe7Bw?4B-}wD`IGf9>)cq8f4Iv(Mg@0z2RicF!T7r z=;R@)H46+eVaM`+ne7<>5M@&T-5{Ali+*!-WtiR7*r2Y@)j`Ul#G^4zx!18;Kwm+h zj)+2!dMJ`Q{$*5k`^+S3trd(w;w?Qv#+Nl+)~HFYD33-lr|+3|B{GV&GUO$Vm#~H> zZ|noBd9Q%qFYCjU=BF{|t%*H$5)bLCf6K0?TQ#&gN}TVyxgD3Q1II>mS1=`ye58CM z$>250oDrN=x3!-#8_&yjmk6e1Gc>lU>%4-vu7>FtRo`8;kiFxubJ9F4d(qR1_*jC9 z=Hc+DKoRjEF*WmlR%9&@;t!S|$&^SHhxiQP_yBzWf7WI)AV&zXB%h#$edc^rgtw>o zkjAe4xJ71;aqOr=VD7dS5HZl@OrDsD4JF)EjOSCOmIvupe z!sY@spD4>m0{48F==o*CGrr~MFB0Xb`F@iT%-57tRZp|HAE4A_AI%zy)Wjr}JRd&F zNW>Ddvm2(FM#`n@u{o6YT2euZUI1Nwl6MCKLS=x&iIL)pp%}nNoEOp(e?nN$>rQO2 zz-T6@o|r3KRuyYz`)P-<=RGgC!R&(qg;cLM-|HGY%>eFw*@9D!sw)b@v~@i8CnW2h zd@Y zP@YpQDDBu|71^A}N7t?|3YSQ3m`2{kDg_psV0~2W@|U8K<0vfMihG$TYHjoPe56LH z+Id2WKLg$$-DtBHDf){>G1KNUCsVL9naA&l9i-2{7w>K4tXwV1?OLd@HLw;hm0-nu z^bM(WX}3t4GV87pMeI3Y@UITz&JI4)^LRaz$_L34qZ(iZjGd|SYDw(e*UzqUijP}I zw6I>itomXUK4I7^ilQPOB!t}&gJ=E&ld`jyy!RIn$;Z5R5}f27kbE>YWW^HrNw;Ir zL!lcwh|z;y955@Sq)sQ4qN}}q>~5o4pmd?vp2}I)_gu_`O*obL)pIS~H#HA=b1

LkREYL&H>@)I8(R_~qW1 z@GUMo=}z7f@Y960`j-kl{2RQ&p-c~z6_H#JOOk1hHiKT}DYwqT zi^@EuL&HX5R58y$rNhjcH}l^>um-4WPLh`=5UAub%jDUl6zSAW=73%lrTQvRssDX5 ze@E``JSpb)Zfl|pdJd=D!VG%fX!M2H^5yvGiD+p}JJ(;q!XbQoZi6!+T^|T$rCkMy zjD^ne~rbp z=1V=&_O2P3DxRjV2zBGb9XXXy^o=42-%`pm#pNNBZSu_(e8s9`iZ*6(*H9> z+2#l5^R~hV%6%>USP6udAa@W;zt1T#E;`)-<1Tg0a_soyBZCh#Ttgqj3`%8f#TJVC zf%)hP21^(B`!8Ic0(&3myA|PDNm?T0i%wrP zn&{T@+DwPTo(QRur}$jNrCa4c(<82@SK`osX*wpxzEsO5%Oz>auTx@8V|QzRGXT{KZe)nhdQSs^q7#LU15stf+{1!iP zf>b9KG>6GcMnBHKo(XQfd#-_*5m6H^LAaZD;UH8aRs30)t>X2?h4R*7IX$_=H#-^4zFNVM^|~RFV9>B!U6%~X}ZoOzIURLC|zz3!@G>5_wf;j_Qz%vc=!W2X1sJmARr&bQp{hdshk{PEl$;B8)!ST*%tq6lIro4bZAw)hXBpUpYb z-|nbY#ynA1wrJYI4d@OcOj2_YW+KgH1eTgz?z7f^ofarnMAwLAD^&E<%Ij$A{3gZ= zyC?nnAj^$16jgeMiS-={kv3cO_v|j=xB9W)N$#j}hoyga^Hj|%r1RMxN!i=qIi)Pa zWhPT_yEwYna5S1?%&bLJn-|@8Zu;YiLEDC;k=_p<^i715r;xLk4Q>{CrtloiHTP+=<(qIWWL zCDEUVg}&*s{>VTwW3MQ=zz6nQL6#GL zOqbcLg^4S~#+$l@A?HNe22R5U>GoQ~Yu|nF(sOJbe`eM!Tq)oyZjF|sNsi9Ua?1I{i!8hCOmUvhiTsgUV5sm zSc;81iVz!b^jZj7P*SqXwH{D)tqn7EJ=*i=RJxb98M(dkUghvJDr~pxA>?ddE;?` zVGVYtp-!x{(+8mH<9|T zr7sDKq*&vd5W#mGs%JRaIhmMu~=H-KQJDbxp(WMx~tY^d16w zQICEGUi!Ye>PJ_75k61?ahM33s*pEJKeiD#q**JFrzGoPA(P8~T2itKlX={ebmHon(Y7xi1`rI=!r>K4YOZtv5AGh9W)<{bYb*E!^5Kw7=uQ&n> zO6SE@e=;@~)pJ&1_i@6-Xf_tFMsWHoB45i*9~@m77TR z!=lr&{6)CK`EZ(Z;zGn-v&)amb-tGF4|+DphCaPr{W&7E=D@2z?yAW1u$V6JaY5&( zmu}yd14OKpG_9Ip4W6IF=yh0W0c6~xfi29HSbaD5)oW!CZO7qsu_va;FgnsX{-ES- z^1IoYtxgidR?0!nF;cUhPgEx0?$EO$hH7;afpFyDw``sys;`su!^-UI8b}Nl>}}?M z>*_SS6g7>U5gW~L=gi$R94{u2P=>^vnlA-g4R_4fn-UcoW1o@!8vBK>G7FrmIi0Z< znX)lb2AM%WwzIt@7`$^jNq}PM9aIzEA<Z|dEFG<#j>#kzS zqLQ1Bl9}ERVv0X>NV&=wzq~UA&`>ml``|g*g;P%pDETBk8)#`9ZS|7y?O)HH9E*)eG@+sTs`wq--K0WjPWLRl#R}zC-(~ z#jGV$2M_+qo+gX7aOJ4Hu6Z^{C9r- zq5UQwir$W2pm(&8!>5=Fz|}5A_$}wNn z#k2~leqF-L*6D2UxK4#y=>(D;TfmG1-me*#e@$@ z;jox(<6)|?izW4FsH;Zlm)Hi&BJZ=bC!d=&#-v{6ZYg+JeWAGjgwbH_eaObx-dZ7t z`+CE1{F2Vu>kXxN_o?0i=X#)F%uT~a&+*yJad9m@fZTd-!^#`htw-LYezetl3B-?o zG*sp={p`fu^5a?Bd&SRVU*8kg+r@02KHHURexbD@Fz|I>?txXmtl$X^kA?mVfj!%W zAMFFb?5)e%#!Io|UC*{v6ZBKsS0Im4NO_Dq2=MIIvb z&~wz67*L`&M6yKP_~iF3fucu`Cs}1bGJf_Bg@j6n(ud{>PrT0~ubz`O=Q!RCna^Rh zqGaM^M`f9uY{`C0r{YwV{w8Pob)1(>vI({IeO!STyrF@Qi8YASh`v3?m;Lt6dPF@! zq-qGY@)3N_qIjXL{<&orD7>=z6TSU=>G8n6D(I)BYR5++u)Vn=cY9z>OpTFs_r z%>q&)doFAC(h^#P$I+85>dkcep&X-}$%iYRPFLzg8bm*LCv(`;<34kGsqw^rWO^ze z<3{T_;(r)@rc@N29i1IQeiX`X5UtUYx^tVirAEh3|%Y7s? zI`r;&;m1|S&nRYo`eQtRFM6BT=b(l=){$!s=9b7o(V<}cW_80hHV`vtkBk>HG(y(? z1+qP!AzI-byrEPy6g^}^2(LlKER7@WeUr8qzCzdMDX`4I_;KhcTMk_#8IgPA9)3=Er%4BPl__+caXjW?j(TuC%+29gci9)p#NSt#+Y%4)^vnM>4uEB>e^bIK|whl^!AC?7}OfcFjjR6Mcl2`SD#mG{7YWLX+0l) zK7t?#Gi`ls1w}sMnm4S+)3M#J9;D1FV2c2>EeunQ8~HW9p#~pqwje2jwk`0bF}@xt zxEE?jXNWk@fVgm@&h>?chx2WEAQA`(n3))f-h-vx4|b>AD6v8tOa}Zo!^SyB3D#;@ zSzRxr*al;QFu`F&XU$Rb!z%tqD*i|;S>tXL>$8Ev&Pd|HTa;p(tk{y4Xxh0+g5w{4 z?AvbkK~a|>yx=5U?Y4GI^fvm>K~$=(FkVj!|m!OLOsp(lcta1NJGK<%1)t z)}X~TIu&uR3m~IW=OVoB0{6Of7v&b%n;?GO9!mLtEZtvqz!!fPzLNMt``&Xp-hAFYq~$xmhlA!vb^}Fr4*Fh3f!ZP znwTgsCbIPG70TMt3w~rUW*`1u=_OElb+A(}`29Y2+PJec`9i(VN4=}6V^fz*>n^15 zH_XtN;$uj3v1}kpsE|XQ6hKJXBS>u;!?38jjP#E8R~*z>B@6}?F5%KmB!(0y>I>;px^U1~H z8!Yy1?s#l;2yC9+^w9Bv=}y()YWy9Y-Y2CJGjcyCPS7@|>L@;^`2q@WF40i0RM8K? zf%n;_EWXdXaH7U54%ysYiX`HyOk(R6tVtSr63EzTr@Uan7zR9_kEUL3u%7-{PUk~= z7fHKUlB|Hb1>b8|B*HPVRphR2iK2B<__3J=6p_arq)!0?D&G z5F@dRgcQDBY5BTBk(8E{OG{vYLn4VCC6d`a+Hg0b*6s3a(_-&*_n^nF^)gjs=T(J! z@TYd#X+iDiprC+oCsdg)?N*^KEaqf>)92GyQRh(}SyCKV2WMD-8`<#0^E?9*M&oKy z_x{JjMK6mMP2z=h-r$~FsOT{h8!vzJ4MHvqNN1*t|A3{}d(MlZ`^$cZ;dK%(c;#ml z@NGQs(Y!~zOrk38*8kJiRe(j=HEos#rJE&{mPWdyq?hh4m2MD$UFnXc79^x3q@`0@ zX;h?9Nu^6b`d=2m?}PXKpX(x?`^?;D&Ybh)&dkz&Wlt_n$^DAu4BI&z0w#3q2RX0} zJXF}wP?vf!@?ARz+p5fY15_#%!O!A1Sh7N?Vh2%eqI!5#X={ihG676k$Jt&TD9eyw zaS3)A1q~&xtvF=S(7d*9&U4rdv(lE1-kP}+t;tjY=qen;+ZD)f3xrmGMR#k7e7R#P zT?}0KmV!Cx`-dex2GeKt(AxQ+6ughwS~gKKQOM@!RFVuHE8Q=u9$w__*)?H(ESu%+ zsRAMV@PTTH@n`!B2>8#QHsHNLW{0~Gj^FG?T7fP9)}W2%3UA{BGQHjpCgRo8rSBnS zoe#7&msCWG&EIzio;Np;&uMD7*d&*`IF1h-gz2IUIM%++B5N(T_^R6zLT3D&%*l&+ zqYk}Og=N;C%^7t8`Pm_cK6a6gSi68vhcW@?d~(lH#=@&9s6(0bwY_s&En>~ zFi5InG!bb!?^Kammn5mqi&K+-+j!Z;7@OdPY*cPEiwW;c@^a%pe?G z5}b%N6SsFzS~?>1b+zH4I2;>7GNdl84l4C+4l{Y7PfbI}ldr(vWqh=h^gWs0Oa5b} z*l%_7A0jv~VGLmhuHn2v#_qNtP{~Pi6*kx9yuleFP1t~>+MNRa!tvohOYLd;^v@~xy|bKNTjM>_O!2{07(IV#U&y8?N9VFLKkG6K_;@5KS%GpG z^C5yZ1couUI1E_K0s4R&TEw0=TzYtb+Y`pH#F@jT!9JDCeH|mT7n>0Og#5dqp?%98 zY^s6~VPVBt5;M_0*P3~HF$3A%X+?M(tC*qyk9Ui8k53aW=tbu6YTxv37s|?GgH%(R z-=SUefC1@C``)1z+vhxDe|98xC0o=aXF;W>N6f2XN6ZUH%0S=x@I3zYERTRE&O1Nt zUdN5+Pg~C#$}Vyxme@gNhUOq@1zLI2XUeE8*b`zZak<=+y0qyA36Je*XgR3Jg!KK- zB7DWXkXIZ80=7k1cf_QKp-1JAMCe|LH#8oTgVmD)&^SRqFdVO6G(9(8itNx zOr-9DhFzUvu|?}m?hMCG5T>tqwYCdY)zPBLuBjk3lxHTIW(E?>jle8?r4>&h=I;S zMu~o~BGZ7A+|!YU9lbNI1^GOqi5ip8a;n`B)K)W+NQ=ONchjAKm9x1Ac1(tQUnT+xx% zxUU!qO0~Q2SV+80AB%Tx-!6_H@mCbfjZgu13MZ5nb&QeN$jO*pa7L(cdm?EpIAYZgSE-XZg1sqy|NNw4;!2TuKQ*lT59V zC67^SNBa9m$Ye{a!9BoGl?EHr=)3n4wO_T1YilL)odQ>Aa}~-P`ISX2d&u&SYgSA_ zQ8l!Luxb7pByE{|62e*;I#sChRos013AIFJh3Va#$%4z-m6z{KiVDBN!jb=EB1OI~L4X8x zZ#{&EK^-K!vo;2heku||`5cl+m!7Qd+(EgZD?&)ZLP8(XIId{an0wbAKmw58L0K!vM7=8}bFLCWy~KKjo8XJ>&cbJy|_5 zHJ!0!vg-RgPZncKd)l&E5u83Y!HxRm0v2z=eB88ro%ncQ{ZicK2YTfv{{5iYqgZ@D4x#El*#f;;-`9cR;!x@+qIh#M#54se)UL*(d z8LMcY4yu)?djlOV|wwaN{Q2Z;l*#EjuhS6Vc63zhKf*=f;Hs> ze#_^ZX}Rmh(H}f~=%e9mpyHYOX}nbbTq6`4X6NZY*ILi2Z@I=^kMq_qgsfjAPNE=? zuyL`j2jUKT@w!fbSn++HmIUlBxJ~_}ohhBI*M20fjK1(?6doI@5_Z0rA%kiHtf^Gp zw<$21Nj|BLqwhEW^5;iyNJw9Z|G8U+i=`TLh$%{W{(z3RiIShycW(a2g>Ahn-zyMv zsHp>_z}v6!{#%lT*Q-?YHQmLfM(PX)>H@Fdw{$`#^}U4d*XqVpR@Jx^cyE2jUG~9Y z)**D3eNpb-KTcRUtl_Dg#9wHAN>b9u*^#r-6WzF#8^*8qcx7`pbKLsF{^2iBvQ#&) z5{G~I_wVBZpF39N>s08kU@upia2?dO4%=t#d-d{sQXxaXUYY3Kap-S?=MnS2rnlc_ zIF>kpR2}!D)1?e~Oy|x5=+J88$d@NLpMGZ+@&j=n>x`7~l^K?1Ex)#1DtS>fHsLUBDZ;pX z)Rzj9q+F?WOdEeH=J1@2tz#+E!L{&IbC%A!7qUrms^ueNcyS{3N=?F_-ZnN%izd<= zZL1#WL1tiT{}W3&Z!ad$4$n%s(Q+Hy5&=F6YM1g>36EO1$emtzyG70{7HYdWYM=*= z5G1)saG-*j(KuLgiqdR2L8{quYWE$uET6JEsm=iKV5`^xFGD(Ij+P_qGJAf}_)tDo zg!GHgcy2Ja$?2V|-iH7JBTj$cW#x{>MY9<(Eu$vG$r|j<6Gkbs0R5^O`9fhNB-OcG zG1GiXpscGw`dp=@u$@MDfN+Zk((#$(sc=bZL(uCGLU3o>=_W3P_6zyS#l^aiDrgY`bf`!S_D76yM~oO(jk6R0sD z)Gv@aB3zQsXYTF(bp~m+RuGE!F;Y)2$$Io`7o9)(=)jR#|1nRcSIjR(dc}Z0STnZ# zDCdKL4=r%du%@0#V026R4dwo3K0ZNLNC+FYYHw|U93M&F_DB((@rI zqh9$NwDp>Y-;WnH=pnn`!V<*-qL&iI9Z3b))ALLBVx{o?xta?!h>5+nxau}eBL@=I zcf(8#Vx zrS$5+3g8f2)BHIZH5@VV!pnIYlL;TPEen8bFo2-A6%9m%akEmrtw$#b)Ivs;6RK2i zpg=UQk1&lj(R^RDEg>@wN{* zy^}|}>s0CTQIiVtg8%gnR{tudv)W0{tPj8Ej8K+*gB(rMt``c8Q;$sZ_~Y7MrRcoVAatzzfnL>9=8NrF+_Ty^5e6}WC=5`O+H;|ebgy@AmZ|86yr>-!L1ml5 z#d%)Caw;#+vVn4H5-XZ}3{3BitDD_A8S5vYA3ja;IzS1j&o{vy_NrS>Q%H^lr_uz? zQI&YKU!wd*O#c8}2*i;;BupbgdQdpUmSsI!+O#o9HpuFCxE8Sb9DkUW`pxgRpT-5F zodNN>$$2Osq*=Xc9Uk6XwAf^9OwU~-E89anqQ8espbAxjI^r%-+H{r3^9e|ZmwpJj zZVV-+dxS_ywLtzf$<20?cA4_yHjBGy8dkqJd1a#_zke!S9ocgs`H5p5BA)-DGt0#} z{oT0>fsXI&D0JP44KsU=z2Msvdt>yX&6d-PR;gJVW(TjaF0SxC+^#S0P(Ucn*~s#x z$}Q-cEwGcQXs`u0GzNQZ)L5Nagm{c=r_T5d=n>IaSgVs1^@Y4vp?pCr_);;tMPh9p z#$g?{ZxZJIt}EMJ!-M<>1hy<2Hg8b>i@Q5H5IIQpvI<>hN-84fS9Y(u2OYPEXlsWC ze!!MlOvwniT!5YQTjY^QL+PoF^`Jq;7a8fK`zO?X3rw_k!}rv4@ERI8%i2jI<)ML2?SR32xguKSWL-)0iU?ZCqtLD`G|bLHNV{AgZP9?ai>Vj6 zxq#7|`*q!dc<~Sd34IMD0$TP_HqeskZvIZL=*a7A%aFZtZC!msov{jj5lS#)#Lg@_1&oqH=oQUc3Vpq}w-bM81{{2!w(z(ZG?j7UIHg9$~Up(NWo_lY2A^JY0nJc3eH zSl1Ao+50s**+?hz8*8ubtR^MH92lphIow8v*Ns4w7kk*7gsQZmk(wfeu8GB&O0=(^ z<$m8<7ry1D-ORL%F4F$|IkAv*w;;poUp8M#=p@5;3Gd|bqCwa#nqzs1BLf!R24=Lf zdV#VOy#~Uksp1ChPhb4*X}jbF@&v|*rLzTv)3^od##>V2gI`J?kjr+OJ7(1}HH5D=&I^JGBees#92@FBmi~VYv3W2xFv|QPm_`2yr65%o|M1MbSmR; zLiIBMgTDf8s~Awwd4D?SIUU%(f+l$0R~DDWqg2dX1{jenenIUPAjoqRLS)A#{J?AK zlJ;qs#1H5@KQ=o@&Wk}!gRmLX{mpKo1L{9!t{Hu2CcB<8_= z7%6-*3W%C(RdSn$xV3`x-#%w!OhOm&mss3VGC7fvc$2<^A%Y}$SzdG@}E5>^%le3uc$1wMG_C6cI z8CKyFw$YKoKOBN3rnt+>}wY9}|~$wh7t zBt^^$HU*f`+}rPtc}9HP=zOR3Z04Ql*2;X&5HB2A9uW#8`ZptkpUy&^1RaRVkT4mUUZM z1&KdvkPYHggHcGS2GJ}Yy9GtB;^jorLr~i3#zy>i={3pn|EwgY6S#jKbAA1*3dIZW zyddqg#%2zzIw5h&Zt1fccxWAP@02D!7_*COD~5lbR$-&rQIPp8yYwIC zfy+!P9v1&8{cdxIwZ&8aPF152sbPpuLfNuO{~2j$19oS5cnO{GKy!0IZL>eEwgZ=k zxgeD^cifL|;pQ1)fyrlyL9jWZj4HA+gVZuwEOflJAk4%zB32X4T31Sr%dPAM>vA{1 zhY#bgSMD!1O+Tmpvn~$KVo*GphXAf#`yLKZ49LNgK3a_a_u3Z?Q3)OP*b6`hF-uGr zz`sO2$GwZ^ugsmX9j7&u7PbBBH)H0i$ z>~r@nde8%AoQ1)1R=>%nE;>Is=9sPwVr#|kVGN!h-6+C7*(=?Dx`aBPrFrt|E(VBl z$xL9~J9gRp%@{AWg2jdO1Ac|nY;+aPVGjsZLhzFA z#lzQ=ZIVS7{T~eCAA9!Bh~XyeX`opjamVl_!0<9(*_~gWh0t(R^D=QL3j0RVjSZum z1`T2{&Q1tiCPUXvO^2UIZ`Ygq)QNyEd}V%d=}fH;d|}wyD&MY<=%~~UYN4~(Eob_K zWGpx4S1XYx)S27^2`Ztn+4YB*=_Z2)<{#~sQpx%Z`iOZvJRc_vmOZ8B7?*Hz@+UWB zcI|u6jCxo2MT6PgatRnaMG}+%E8plXr&ak-I+CZ6b-d9#Z6YaAk+x=2`nsY|`*?T> zv`|QL&X1SXW5%jL$fZQ3R=w@8`Rn3vE0=-KS;-~#8Jk=B@ln>uqL%^$X-W6ewSk(@ zywRtYt!1T78EMIj8)?4)Rl(`2BuzaGm9u^=tTt4*k8E&et{!8e`e-EMyvHL$R@qL6 zW~;m4!Ag$6G;85tKlspj3$p*t05hRz&+SNAIsfSYB+#*k-c{=SN+7BkT0;dMJl@h`dKAH^#eX`E4Q!W|apRu&Wb0~ROajLxH zy}?k6aZr%#Ya!%`tf1{yV0A5NwDVKE{EPnzXJbE%2yYIz{X(57p7@;fNh5uCT zJ*@OCEJ}ywsToZK?U%I+`=l>I>?Spa;JO1fno`<4$7JJ_C(Xoxkag*H6R6llZx-yi z^dVip8vVDQ?Ve}6f+$n(e)roUT(zNhAGFEl5xCF&=Ez$V@zU0O3kq-5;UdO zHtB-X$_1T5j(Ki4jVyy{P6ljdX%%>^by-Wpk%( z9^8O;5&$Defc)U4zOxb67G96voDmaq_^adWS<+r1L$lziS3w-aobwrR^q ztPBsd3WR&?L@->Vx`_OYduE$2tEIVe`bNV6|6@{Nl zG5hVdg!5;gq*KUOuW4Mye&?fU^mi;JCT7obd__|5o|8an$JV@fYy3%?&rzV!)y#NR ziRFfO@y+U`r1@0BERf_{8pU^&DtlV^+hs%FS*)N0Z2Ms!8~~+phzKiOSMc&0By z@e!ZgWnm>pxXAKR3%6%K8LZB0YO0>eg_ZI9$$aPJDD>IPy&?glXY5|uj%=i2gQ%4j zyP2z;L$NJFkh{~Fp{{4ycrk{Q`MByV(}54ZkXDWy3fAy06#>UkY#OA4ns%P{i(A!6 z)i`hd)TA<-p48ZX`o}l2NsU_YJ*MOtB}4Tmq?L&=LglV;-HtS|w%r4fdQ4uR8&F~+ z96OIy7+FOtt@`3KfOt~OGFzPwUo;F$xk93)2sgz!8xkMbL%I}TFx|?BeA4-2xw+11 ztPw!15D(UCnvOAgX_Vscyi(xHT8ncZlitD+73bL-V1Q!Ho^Mi3o_M73uE@l2sQ;+? zXMx4lS|K}`yw=Z_+aW;;59@3PWvHQ3q8rqFc0st>$LDK~EueeeW1lFqwi0@y9KFff zCkbXG$RR3L2c)=4Q8rj*XfJ!#4@`c{ks*<8zrp^de=fS*-qU!ZLB%kQN+bUT?OVO| zck%cNWCdq*0)gkMD7E(uC*VH3bNSXf(Ol+um_OcAz6*=* zTv)RxRrai~ViI&uI!85a42;i*bUrS0jw2Qr7<;QH-!kW1Us4guzD&C0qxO0$6GE%2 zAzOHfe09CWzr))92%gsQDExodTg2rSk=AjQ+HtkPOQIq@ci{4NuK(^25JFTrt*3km z@V^d~c@+Tob@f?neMlfc1kniV3*YW997VXyfGJE^w9msjdt_ zCPw=2_onFGEPj7mdj!6FLmQFd{ts;`J)$_~PeSm>p()~jcCHAE4+YTDURijv3j6Io z-ou9BLWp7QgaUxbIpi>$u)hFT7#!H6g{k}paDoE>Tv%infDWNib|tWQ57z%5fsHF* zg&|Rji=2mt)9c@Zl_bv;9D>)b4TM_@yx}M$q!i{WEtp+6fPn7GpqJdJ6%H+Kw?Ce^grhfL6xhI(87rQOSKaV zAI>IhHwHioQ;q#Q@GB5m*a#t+bPEhIz5)}?P0;@}P=wMa6NJ*exSLGWW(d&sKOi^` zPH$@t184(C$^XA&I@IM785WQPAVqGmfz8JOco4I>BG}i_0nbWPWC9?O-ChGoY8T@p8|aNH&V#a)wpgOecoBS@vDrGTH49>7lz z_iy!gNC@Le1^};$c90@O8KC?EpD>Y-8hDWYVVXHH%p3|p)KP{-K;c$-Q(xqq!0zf!$5`I|8}{INHK=Eeo&efekxO_`6PAubP-xt`+j&3J4QY!qSmL66b=wCSoY$-(iGR%iCBplX=es5dP5eu6Ge__?LCHe|0nh)p z0QHLA-0uUzO(g!8iK1fHJ-{;yuT8v}GIYC#Lh)-8MvIT z!iKfMn-Gpc#B=`*=cc$djSMo^RF_ErLWJm#;i$9;xQGJ$oWXEQ^sC|(H7`QyX0YjP z!V9HqLQWL`IxH*suWe=UrGnf6x+0*0tt9`Q_RA}DO%0*I%ftKs9R^4FVlg#vGakTw zcH5i0M+~2{KDZrMw=n2At6!zPZR!M7Z?CzdkH_zFH-+bB~BUuC@S+3fMn)oM?G{>X#BD=?fCSoQcN$jP-MZbH z7*e{WKodpFPheh*^o91Ji7bKkzF=aBL1SVh)u13htBDCD2>26iYh^sM1+$ALzWCt7 zJ-Ktw_nq%M_uNTk8d8~tLyMe6HHVAqAf9d;YvKy&@AOyZ(eO>`MzqJ_aC|jjwGLXX z2$s{dT3rfu0)~}kq%YMoRkejqCsNr=+*P3ApC3&)*vycf5ONbfC;ExaoN^WD_>bIk zyAsD{ZrFsoxd|XlkIq;w9o=EGr5NkzHnVE*CF=fR8AdJW1v7nH^P5%^RyYNS5BwK7 z?)QQJ*;*$=M5iD+EIKvrK_651Tn>|2ajacoH;D2P+DcjmFdHEA~2JTY=KL|1Fa~t5w0hm@F-0P)$0|N484HPizu1)af5iINu z7CGqml+pckEBT0=B)w!4SxyA?qB^1uDoLeTc~ZV5pOn|gvh=kyF1;y*r4^DQekYz2 zkBCX}IS~n0gfaIZ|1!UlFFI3Jd!93&;kkOT;&C_f)%f=zvybOiimmG!TLN|K>(`lY zRdQwKWuBXq%KSFOH_*GpOz~WaIqBvK^MNa|L2kiU?(64*jV&9uJXKw_Qh(h@bi-|S_H-w+-8Tf* zx76#q6Go^z9_!UZv53yDT5?6I__~9BM&G3=x|vo}7l{xNUh5lbK&8rc5ILu?FAge$ zsIS;9An&pgPk%2Drn`G~zG8%vfsSM_)M?lhvLimd&MU>6r(tIwYS}|Np?St-GjmK8*atFw9y8pg&6Q51 zRjiHTW(pxi^-# z$pv)^26J8|{jXto>4A~~pZz4wV3V?F+rv@(7lWvT$-r=T8VQ(YC}|#!c`BB|HrlPX zM~#k1_8C1oC_}HXN4+g;hugkwZE(AJC+6`hF{fLEe|W%ad;Ku(S@=KlYTKFj$}Yz} H<+XnTxx-~e delta 1513 zcmaJ>U2GIp6rQ_#XJ+T`p4o1fLVwa}7TL6d0VDcEx}|B=h~35l36iF*-6gEhorW$C zKJ>@d+85Fmjog@;v=6$C*3c59_f5bcyl8BSK~X9t#zZ53qS3alJKk9m*d@k?bCP?$ z?>pzqnUfhP5d*+2#yMZaAnA88*$nY4-a+P zygK%B4y#&Lzcx;9jTC!4sEDoF-XatK@hE`U>X-}2`9RQveq*b%-XaTU^6PFViLG9D z1-J46E?Aphb-Wh3$<`_{w$N?n)Z!~3->Jr^1wBXSZki$HOyV^NI{n=9Ul9+_2o`x3 zvaoo@k1m1yPXXJ;x{3AtImMn5q`vM7#XLRIa{7zlL%xCrXYne?!b%?(++)tH>*x18 z0(GKT7yW#|gMBL88xolFmCN-MaQpdQzbQ21L&2OGgE@CxL2-e=!E)0!0e%7o!?2HB zARmxkvWILTK4Za18zXwJzE6KjyQiJhp4E!g8|tU(+v;JpRt+k@DxWJ8O0Tj*QGGx9 zK9S#*j!L_wZKtauOCnt-i4AJiMi~WbLqm3m_Uqz$diOO^q=S-JM!%57Dtbv0&nxRg zE+;s2h#r^4Cm9Dm#EMLKaeHv6h4$&3$cxw6h#~eBMP;u5-@{4hg%+p*M7|@Z$uV+} z)RA?@ivF_xj2_SywX|kyJ2XxGMg2^DM@^^^wM4nc?Y^({D0NDi@0Q#vH_Mx4FE=|Y z(aFce7nR31N+?iUA%v(M7B{McUECOd4+Bkx#cDPg7H6*+WW@3gzR;>lP%w%*IJKuGX#tPm;jNQ#Sa;7EaFWNA#{p|Yhht-4*E5#TJm z099m$Pn#g@t*lmze6>jO1;fETbk@;=?3$z143u9M;7fQ924F922QS%YWQ_?UX*_LU z{W8~{(%BEk^bw?nWgiMuSNI15)E?E#DM{+(R*jXkQ&u8r^&EZW)p%#B?r^H9qbvS! zAqm@GQ_+)%#jVuA_@k@?iTL3d^N#80Jj3lq2GY%j`DV8<-%X?llNQKYWqOWS^K%9^ zr?Ck<@#i$|HhF4xpW|+7zn^&Ru4yuS4l*!5WnhcpanoZmX&kb^@LgU`PZ{QXx9LL7 zW(H170iMPYS7bq^^27p>INMySoL>fPp27Z1WqJzC`DN3cKWF9=*KYOm3Fiprx`-cH zDyb0QDwyOpVO8BIqw}9y5&sV~VSX`n6@yWFt;@fud~e;ZJq=cU{75QsAko>8ia*@m zema%aN=fVf`li;x-EDZVyZR<8w=c^+klSTWLN6x$A-&NfEBsyu>-kmC8(D1wCAQxe d_>Y0~+pM-pYQ2A~?zj1${%#|7)%J%Y{{n}@)foT) diff --git a/configs/compliance/condition_instructions.json b/configs/compliance/condition_instructions.json index 1c0baf1..f55b8e7 100644 --- a/configs/compliance/condition_instructions.json +++ b/configs/compliance/condition_instructions.json @@ -14,6 +14,11 @@ "CA": "FUNCTION check_ca", "THIS": "FUNCTION check_this", "NOTE": "FUNCTION add_notes", + "NOTE_ENABLED": "FUNCTION add_notes", + "NOTE_DISABLED": "FUNCTION add_notes", + "NOTE_TRUE": "FUNCTION add_notes", + "NOTE_FALSE": "FUNCTION add_notes", + "NOTE_ALWAYS": "FUNCTION add_notes", "CHECK_KEY_TYPE": "FUNCTION check_key_type", "CHECK_ONLY_FIRST": "FUNCTION always_true", "VALUE": "FUNCTION check_value", diff --git a/configs/compliance/requirements.db b/configs/compliance/requirements.db index a87126dc0436ede97955f440fff368a27b0d2e5b..1e7b864e533a9fbfc39b290b2c114891b385be2b 100644 GIT binary patch delta 1932 zcmd5*U2GIp6yCdgXLf$?o$Ypukd{taW1$wU#*z>M?G{>X#BD=?fCSoQcN$jP-MZbH z7*e{WKodpFPheh*^o91Ji7bKkzF=aBL1SVh)u13htBDCD2>26iYh^sM1+$ALzWCt7 zJ-Ktw_nq%M_uNTk8d8~tLyMe6HHVAqAf9d;YvKy&@AOyZ(eO>`MzqJ_aC|jjwGLXX z2$s{dT3rfu0)~}kq%YMoRkejqCsNr=+*P3ApC3&)*vycf5ONbfC;ExaoN^WD_>bIk zyAsD{ZrFsoxd|XlkIq;w9o=EGr5NkzHnVE*CF=fR8AdJW1v7nH^P5%^RyYNS5BwK7 z?)QQJ*;*$=M5iD+EIKvrK_651Tn>|2ajacoH;D2P+DcjmFdHEA~2JTY=KL|1Fa~t5w0hm@F-0P)$0|N484HPizu1)af5iINu z7CGqml+pckEBT0=B)w!4SxyA?qB^1uDoLeTc~ZV5pOn|gvh=kyF1;y*r4^DQekYz2 zkBCX}IS~n0gfaIZ|1!UlFFI3Jd!93&;kkOT;&C_f)%f=zvybOiimmG!TLN|K>(`lY zRdQwKWuBXq%KSFOH_*GpOz~WaIqBvK^MNa|L2kiU?(64*jV&9uJXKw_Qh(h@bi-|S_H-w+-8Tf* zx76#q6Go^z9_!UZv53yDT5?6I__~9BM&G3=x|vo}7l{xNUh5lbK&8rc5ILu?FAge$ zsIS;9An&pgPk%2Drn`G~zG8%vfsSM_)M?lhvLimd&MU>6r(tIwYS}|Np?St-GjmK8*atFw9y8pg&6Q51 zRjiHTW(pxi^-# z$pv)^26J8|{jXto>4A~~pZz4wV3V?F+rv@(7lWvT$-r=T8VQ(YC}|#!c`BB|HrlPX zM~#k1_8C1oC_}HXN4+g;hugkwZE(AJC+6`hF{fLEe|W%ad;Ku(S@=KlYTKFj$}Yz} H<+XnTxx-~e delta 1513 zcmaJ>U2GIp6rQ_#XJ+T`p4o1fLVwa}7TL6d0VDcEx}|B=h~35l36iF*-6gEhorW$C zKJ>@d+85Fmjog@;v=6$C*3c59_f5bcyl8BSK~X9t#zZ53qS3alJKk9m*d@k?bCP?$ z?>pzqnUfhP5d*+2#yMZaAnA88*$nY4-a+P zygK%B4y#&Lzcx;9jTC!4sEDoF-XatK@hE`U>X-}2`9RQveq*b%-XaTU^6PFViLG9D z1-J46E?Aphb-Wh3$<`_{w$N?n)Z!~3->Jr^1wBXSZki$HOyV^NI{n=9Ul9+_2o`x3 zvaoo@k1m1yPXXJ;x{3AtImMn5q`vM7#XLRIa{7zlL%xCrXYne?!b%?(++)tH>*x18 z0(GKT7yW#|gMBL88xolFmCN-MaQpdQzbQ21L&2OGgE@CxL2-e=!E)0!0e%7o!?2HB zARmxkvWILTK4Za18zXwJzE6KjyQiJhp4E!g8|tU(+v;JpRt+k@DxWJ8O0Tj*QGGx9 zK9S#*j!L_wZKtauOCnt-i4AJiMi~WbLqm3m_Uqz$diOO^q=S-JM!%57Dtbv0&nxRg zE+;s2h#r^4Cm9Dm#EMLKaeHv6h4$&3$cxw6h#~eBMP;u5-@{4hg%+p*M7|@Z$uV+} z)RA?@ivF_xj2_SywX|kyJ2XxGMg2^DM@^^^wM4nc?Y^({D0NDi@0Q#vH_Mx4FE=|Y z(aFce7nR31N+?iUA%v(M7B{McUECOd4+Bkx#cDPg7H6*+WW@3gzR;>lP%w%*IJKuGX#tPm;jNQ#Sa;7EaFWNA#{p|Yhht-4*E5#TJm z099m$Pn#g@t*lmze6>jO1;fETbk@;=?3$z143u9M;7fQ924F922QS%YWQ_?UX*_LU z{W8~{(%BEk^bw?nWgiMuSNI15)E?E#DM{+(R*jXkQ&u8r^&EZW)p%#B?r^H9qbvS! zAqm@GQ_+)%#jVuA_@k@?iTL3d^N#80Jj3lq2GY%j`DV8<-%X?llNQKYWqOWS^K%9^ zr?Ck<@#i$|HhF4xpW|+7zn^&Ru4yuS4l*!5WnhcpanoZmX&kb^@LgU`PZ{QXx9LL7 zW(H170iMPYS7bq^^27p>INMySoL>fPp27Z1WqJzC`DN3cKWF9=*KYOm3Fiprx`-cH zDyb0QDwyOpVO8BIqw}9y5&sV~VSX`n6@yWFt;@fud~e;ZJq=cU{75QsAko>8ia*@m zema%aN=fVf`li;x-EDZVyZR<8w=c^+klSTWLN6x$A-&NfEBsyu>-kmC8(D1wCAQxe d_>Y0~+pM-pYQ2A~?zj1${%#|7)%J%Y{{n}@)foT) diff --git a/modules/compliance/compare_one.py b/modules/compliance/compare_one.py index 2edc100..226438a 100644 --- a/modules/compliance/compare_one.py +++ b/modules/compliance/compare_one.py @@ -38,7 +38,7 @@ def _worker(self, sheets_to_check): if condition: valid_condition = self._condition_parser.run(condition, enabled) enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) - self._logging.debug(f"Condition: {condition} - enabled: {enabled}") + self._logging.debug(f"Condition: {condition} - enabled: {enabled} - valid_condition: {valid_condition}" ) if self._condition_parser.entry_updates.get("levels"): levels = self._condition_parser.entry_updates.get("levels") levels.insert(0, level) @@ -47,23 +47,26 @@ def _worker(self, sheets_to_check): has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") + conditional_notes = self.add_conditional_notes(enabled, valid_condition) self._condition_parser.entry_updates = {} note = "" - if has_alternative and not enabled and isinstance(condition, str) and\ + if has_alternative and not enabled and isinstance(condition, str) and \ condition.count(" ") > 1: parts = entry[condition_index].split(" ") # Tokens[1] is the logical operator note = f"\nNOTE: {name} {parts[1].upper()} {' '.join(parts[2:])} is needed" - - if has_alternative or additional_notes: - # This is to trigger the output condition. This works because I'm assuming that "THIS" is only - # used in a positive (recommended, must) context. valid_condition = True + + # if has_alternative or additional_notes: + # # This is to trigger the output condition. This works because I'm assuming that "THIS" is only + # # used in a positive (recommended, must) context. + # valid_condition = True # if it has multiple name_columns they get only shown in the output name = "_".join([str(entry[i]) for i in name_columns]) self.update_result(sheet, name, level, enabled, entry[-1], valid_condition) if additional_notes: note += "\nNOTE: " note += "\n".join(additional_notes) + note += conditional_notes if self._output_dict[sheet].get(name) is not None: self._output_dict[sheet][name] += note diff --git a/modules/compliance/compliance_base.py b/modules/compliance/compliance_base.py index 725b3f1..7513bc5 100644 --- a/modules/compliance/compliance_base.py +++ b/modules/compliance/compliance_base.py @@ -276,7 +276,7 @@ def prepare_testssl_output(self, test_ssl_output): tokens = el.split("+") # RSASSA-PSS is a subset of RSA signatures.append(tokens[0].replace("RSASSA-PSS", "RSA").lower()) - hashes.append(tokens[1]) + hashes.append(tokens[1].lower()) self._add_certificate_signature_algorithm(signatures) self._user_configuration["Hash"].update(hashes) @@ -292,6 +292,8 @@ def prepare_testssl_output(self, test_ssl_output): elif field[-12:] == "ECDHE_curves": values = actual_dict["finding"].split(" ") if " " in actual_dict["finding"] \ else [actual_dict["finding"]] + # secp256r1 is the same as prime256v1, it also happens with the 192 version + values = [re.sub(r"prime(\d+)v1", r"secp\1r1", val) for val in values] self._user_configuration["Groups"] = values # The transparency field describes how the transparency is handled in each certificate. @@ -349,6 +351,24 @@ def update_result(self, sheet, name, entry_level, enabled, source, valid_conditi else: self._output_dict[sheet][name] = "" + def add_conditional_notes(self, enabled, valid_condition): + conditional_notes = "\nNOTE: " + for entry in self._condition_parser.entry_updates.keys(): + if entry.startswith("note_"): + notes = self._condition_parser.entry_updates.get(entry) + note_type = entry.split("_")[1] + if note_type == "enabled" and enabled: + conditional_notes += "\n".join(notes) + elif note_type == "disabled" and not enabled: + conditional_notes += "\n".join(notes) + elif note_type == "true" and valid_condition: + conditional_notes += "\n".join(notes) + elif note_type == "false" and not valid_condition: + conditional_notes += "\n".join(notes) + if len(conditional_notes) == 7: + conditional_notes = "" + return conditional_notes + def _retrieve_entries(self, sheets_to_check, columns): """ Given the input dictionary and the list of columns updates the entries field with a dictionary in the form @@ -439,12 +459,14 @@ def _evaluate_entries(self, sheets_to_check, original_columns): entry, condition=condition) valid_condition = self._condition_parser.run(condition, enabled) enabled = self._condition_parser.entry_updates.get("is_enabled", enabled) - self._logging.debug(f"Condition: {condition} - enabled: {enabled}") + self._logging.debug(f"Condition: {condition} - enabled: {enabled} - valid: {valid_condition}") if self._condition_parser.entry_updates.get("levels"): potential_levels = self._condition_parser.entry_updates.get("levels") level = potential_levels[self.level_to_use(potential_levels)] has_alternative = self._condition_parser.entry_updates.get("has_alternative") additional_notes = self._condition_parser.entry_updates.get("notes", "") + conditional_notes = self.add_conditional_notes(enabled, valid_condition) + notes[-1] += conditional_notes if has_alternative and not enabled and isinstance(condition, str) and condition.count(" ") > 1: parts = condition.split(" ") # Tokens[1] is the logical operator @@ -455,6 +477,7 @@ def _evaluate_entries(self, sheets_to_check, original_columns): if additional_notes: notes[-1] += "\nNOTE:" notes[-1] += "\n".join(additional_notes) + else: enabled = self._condition_parser.is_enabled(self._user_configuration, sheet, entry[name_index], entry) diff --git a/modules/compliance/wrappers/conditionparser.py b/modules/compliance/wrappers/conditionparser.py index 680b544..c110450 100644 --- a/modules/compliance/wrappers/conditionparser.py +++ b/modules/compliance/wrappers/conditionparser.py @@ -7,11 +7,18 @@ class ConditionParser: + _instructions = load_configuration("condition_instructions", "configs/compliance/") + _instructions_keys = '|'.join(_instructions.keys()) _logical_separators = [" and ", " or "] + # this string ensures that the logical_separators are only matched if they are followed by a valid instruction + _concatenation_string = f"(?={_instructions_keys}|True|False)" + regex_separators = [] + for el in _logical_separators: + regex_separators.append(el + _concatenation_string) # simple regex to find all occurrences of the separators - _splitting_regex = "|".join(_logical_separators) + _splitting_regex = "|".join(regex_separators) # same as above but also captures the separators - _splitting_capturing_regex = "(" + ")|(".join(_logical_separators) + ")" + _splitting_capturing_regex = "(" + ")|(".join(regex_separators) + ")" _sheet_mapping = load_configuration("sheet_mapping", "configs/compliance/") __logging = Logger("Condition parser") @@ -20,7 +27,6 @@ class ConditionParser: def __init__(self, user_configuration): self.expression = "" self._user_configuration = user_configuration - self.instructions = load_configuration("condition_instructions", "configs/compliance/") self._custom_functions = CustomFunctions(user_configuration) self.entry_updates = {} self._enabled = None @@ -100,6 +106,9 @@ def is_enabled(user_configuration, config_field, name: str, entry, partial_match enabled = True elif isinstance(field_value, list) or isinstance(field_value, set): + if "/" in name: + # Groups case + name = name.split("/")[0].strip() enabled = name in field_value if not enabled and partial_match: enabled = ConditionParser._partial_match_checker(field_value, name) @@ -149,13 +158,14 @@ def _closing_parenthesis_index(self, start): def _solve(self, start, finish): to_solve = self.expression[start: finish + 1] - + self.__logging.debug(f"Expression to solve: {to_solve}") while "(" in to_solve: # So that I'm sure that there aren't any parenthesis in the way starting_index = to_solve.index("(") + start end_index = self._closing_parenthesis_index(starting_index) - 1 replacement = self._solve(starting_index + 1, end_index) to_replace = self.expression[starting_index:end_index + 2] + start += len(to_replace) - len(replacement) to_solve = to_solve.replace(to_replace, replacement) tokens = re.split(self._splitting_regex, to_solve, flags=re.IGNORECASE) tokens = [token.strip() for token in tokens] @@ -189,21 +199,22 @@ def _evaluate_condition(self, condition, next_condition=None): condition = condition.strip() if condition in ["True", "False"]: return condition - if condition not in self.instructions and \ - (" " not in condition and condition.split(" ")[0] not in self.instructions): + if condition not in self._instructions and \ + (" " not in condition and condition.split(" ")[0] not in self._instructions): self.__logging.warning(f"Invalid condition: {condition} in expression: {self.expression}. Returning False") return "False" tokens = condition.split(" ") field = tokens[0] to_search = self._prepare_to_search(field, tokens[-1]) - config_field = self.instructions.get(field.upper()) + config_field = self._instructions.get(field.upper()) if config_field and config_field.startswith("FUNCTION"): assert config_field[8] == " " args = { "data": to_search, "enabled": self._enabled, "tokens": tokens[1:], - "next_condition": next_condition + "next_condition": next_condition, + "original_text": field.upper() } result = self._custom_functions.__getattribute__(config_field.split(" ")[1])(**args) elif config_field is None: @@ -249,6 +260,7 @@ def __init__(self, user_configuration): "not in": lambda op1, op2: op1 not in op2, } self._operators_regex = "(" + ")|(".join(self._operators.keys()) + ")" + self._logger = Logger("Custom functions") # INSERT ALL THE CUSTOM PARSING FUNCTIONS HERE THEY MUST HAVE SIGNATURE: # function(**kwargs) -> bool @@ -334,16 +346,30 @@ def check_this(self, **kwargs): entry_data = name.split(",") if "," in name else (None, None) # The field must be prepared field = ConditionParser.prepare_field(field) - second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, - partial_match=True) + if ";" in name: + names = name.split(";") + second_enabled = any( + [ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, partial_match=True) for + name in names]) + else: + second_enabled = ConditionParser.is_enabled(self._user_configuration, field, name, entry_data, + partial_match=True) enabled = second_enabled or enabled - self._entry_updates["has_alternative"] = enabled + self._entry_updates["has_alternative"] = True self._entry_updates["is_enabled"] = enabled return enabled def add_notes(self, **kwargs): note = " ".join(kwargs.get("tokens", [])) - self._entry_updates["notes"].append(note) + note_type = kwargs.get("original_text", "").lower() + if not note_type or note_type == "note": + self._logger.warning(f"No note type provided for note: `{note}`") + elif note_type == "note_always": + self._entry_updates["notes"].append(note) + else: + if not self._entry_updates.get(note_type): + self._entry_updates[note_type] = [] + self._entry_updates[note_type].append(note) return True def check_key_type(self, **kwargs): @@ -402,8 +428,10 @@ def check_value(self, **kwargs): configuration_value = self._get_configuration_field(field.get(cert, {}), levels) reason = f"field {level} is missing" if not configuration_value else f"{value} {operator} {name}" partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result or len(configuration_value) == 0: - self._entry_updates["notes"].append(f"Certificate {cert} failed check, reason: {reason}") + for key in self.entry_updates.keys(): + if key.startswith("note"): + for i, entry in enumerate(self.entry_updates[key]): + self._entry_updates[key][i] = entry.replace("{cert}", cert).replace("{reason}", reason) result = result and partial_result else: result = True @@ -411,8 +439,11 @@ def check_value(self, **kwargs): levels[-1] = level configuration_value = self._get_configuration_field(field, levels) partial_result = self._operators[operator](value, str(configuration_value)) - if not partial_result: - self._entry_updates["notes"].append(f"Failed check {name} {operator} {value} for {config_field}") + reason = f"Failed check {name} {operator} {value} for {config_field}" + for key in self.entry_updates.keys(): + if key.startswith("note"): + for i, entry in enumerate(self.entry_updates[key]): + self._entry_updates[key][i] = entry.replace("{reason}", reason) result = result and partial_result return result From 0dae9dae140fd12b57334cda9a46188eea885acc Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 13 Oct 2023 16:04:53 +0200 Subject: [PATCH 121/209] Fixed memory leak related to hsts_preloading --- modules/server/hsts_base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/server/hsts_base.py b/modules/server/hsts_base.py index 651e39d..e22db89 100644 --- a/modules/server/hsts_base.py +++ b/modules/server/hsts_base.py @@ -1,7 +1,7 @@ -from utils.validation import Validator -from utils.urls import url_domain, port_parse -from utils.mitigations import load_mitigation from modules.server.wrappers.https_hsts import Https +from utils.mitigations import load_mitigation +from utils.urls import url_domain +from utils.validation import Validator class Hsts_base: @@ -10,10 +10,11 @@ class Hsts_base: It is used to obtain the results of the analysis. """ + _instance = Https() + def __init__(self): self._input_dict = {} self._arguments = [] - self._instance = Https() self._output_dict = {} self._mitigations = {} self._set_arguments() From 60b85cc93a3817651c43c07b0bc9bc49a02bd281 Mon Sep 17 00:00:00 2001 From: Odinmylord Date: Fri, 13 Oct 2023 17:52:44 +0200 Subject: [PATCH 122/209] Fixed hsts list from Google and fixed raw-output --- configs/out_template/hosts_report.html | 10 ++++++---- configs/out_template/modules_report.html | 16 +++++++++------- dependencies.json | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/configs/out_template/hosts_report.html b/configs/out_template/hosts_report.html index c0e408a..6391aa4 100644 --- a/configs/out_template/hosts_report.html +++ b/configs/out_template/hosts_report.html @@ -13,6 +13,7 @@ +{% set ns = namespace(accordion_count=0) %}