From 91e8ce62db04248126845b5200f71230009e5cf5 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 26 Jul 2022 15:57:48 +0300 Subject: [PATCH 1/5] Common: Freeze lists to tuples in agent configuration --- .../agent_configuration.py | 14 ++++--- .../agent_sub_configuration_schemas.py | 6 +++ .../default_agent_configuration.py | 39 ++++++++++--------- monkey/common/agent_configuration/utils.py | 13 +++++++ monkey/common/utils/code_utils.py | 7 ++++ .../configuration/test_agent_configuration.py | 18 ++++----- vulture_allowlist.py | 1 + 7 files changed, 65 insertions(+), 33 deletions(-) create mode 100644 monkey/common/agent_configuration/utils.py diff --git a/monkey/common/agent_configuration/agent_configuration.py b/monkey/common/agent_configuration/agent_configuration.py index 11ed22718..ae21e87f7 100644 --- a/monkey/common/agent_configuration/agent_configuration.py +++ b/monkey/common/agent_configuration/agent_configuration.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, List, Mapping -from marshmallow import Schema, fields +from marshmallow import Schema, fields, post_load from marshmallow.exceptions import MarshmallowError from .agent_sub_configuration_schemas import ( @@ -16,6 +16,7 @@ from .agent_sub_configurations import ( PluginConfiguration, PropagationConfiguration, ) +from .utils import freeze_lists class InvalidConfigurationError(Exception): @@ -58,8 +59,7 @@ class AgentConfiguration: """ try: - config_dict = AgentConfigurationSchema().load(config_mapping) - return AgentConfiguration(**config_dict) + return AgentConfigurationSchema().load(config_mapping) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) @@ -74,8 +74,7 @@ class AgentConfiguration: AgentConfiguration """ try: - config_dict = AgentConfigurationSchema().loads(config_json) - return AgentConfiguration(**config_dict) + return AgentConfigurationSchema().loads(config_json) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) @@ -107,3 +106,8 @@ class AgentConfigurationSchema(Schema): credential_collectors = fields.List(fields.Nested(PluginConfigurationSchema)) payloads = fields.List(fields.Nested(PluginConfigurationSchema)) propagation = fields.Nested(PropagationConfigurationSchema) + + @post_load + @freeze_lists + def _make_agent_configuration(self, data, **kwargs): + return AgentConfiguration(**data) diff --git a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py index bf4d3b8c7..0e4654406 100644 --- a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py +++ b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py @@ -11,6 +11,7 @@ from .agent_sub_configurations import ( ScanTargetConfiguration, TCPScanConfiguration, ) +from .utils import freeze_lists class CustomPBAConfigurationSchema(Schema): @@ -40,6 +41,7 @@ class ScanTargetConfigurationSchema(Schema): subnets = fields.List(fields.Str()) @post_load + @freeze_lists def _make_scan_target_configuration(self, data, **kwargs): return ScanTargetConfiguration(**data) @@ -57,6 +59,7 @@ class TCPScanConfigurationSchema(Schema): ports = fields.List(fields.Int()) @post_load + @freeze_lists def _make_tcp_scan_configuration(self, data, **kwargs): return TCPScanConfiguration(**data) @@ -68,6 +71,7 @@ class NetworkScanConfigurationSchema(Schema): targets = fields.Nested(ScanTargetConfigurationSchema) @post_load + @freeze_lists def _make_network_scan_configuration(self, data, **kwargs): return NetworkScanConfiguration(**data) @@ -76,6 +80,7 @@ class ExploitationOptionsConfigurationSchema(Schema): http_ports = fields.List(fields.Int()) @post_load + @freeze_lists def _make_exploitation_options_configuration(self, data, **kwargs): return ExploitationOptionsConfiguration(**data) @@ -86,6 +91,7 @@ class ExploitationConfigurationSchema(Schema): vulnerability = fields.List(fields.Nested(PluginConfigurationSchema)) @post_load + @freeze_lists def _make_exploitation_options_configuration(self, data, **kwargs): return ExploitationConfiguration(**data) diff --git a/monkey/common/agent_configuration/default_agent_configuration.py b/monkey/common/agent_configuration/default_agent_configuration.py index 9045429ca..712e7d458 100644 --- a/monkey/common/agent_configuration/default_agent_configuration.py +++ b/monkey/common/agent_configuration/default_agent_configuration.py @@ -13,7 +13,7 @@ from .agent_sub_configurations import ( TCPScanConfiguration, ) -PBAS = [ +PBAS = ( "CommunicateAsBackdoorUser", "ModifyShellStartupFiles", "HiddenFiles", @@ -23,14 +23,14 @@ PBAS = [ "Timestomping", "AccountDiscovery", "ProcessListCollection", -] +) -CREDENTIAL_COLLECTORS = ["MimikatzCollector", "SSHCollector"] +CREDENTIAL_COLLECTORS = ("MimikatzCollector", "SSHCollector") -PBA_CONFIGURATION = [PluginConfiguration(pba, {}) for pba in PBAS] -CREDENTIAL_COLLECTOR_CONFIGURATION = [ +PBA_CONFIGURATION = tuple(PluginConfiguration(pba, {}) for pba in PBAS) +CREDENTIAL_COLLECTOR_CONFIGURATION = tuple( PluginConfiguration(collector, {}) for collector in CREDENTIAL_COLLECTORS -] +) RANSOMWARE_OPTIONS = { "encryption": { @@ -40,13 +40,13 @@ RANSOMWARE_OPTIONS = { "other_behaviors": {"readme": True}, } -PAYLOAD_CONFIGURATION = [PluginConfiguration("ransomware", RANSOMWARE_OPTIONS)] +PAYLOAD_CONFIGURATION = tuple([PluginConfiguration("ransomware", RANSOMWARE_OPTIONS)]) CUSTOM_PBA_CONFIGURATION = CustomPBAConfiguration( linux_command="", linux_filename="", windows_command="", windows_filename="" ) -TCP_PORTS = [ +TCP_PORTS = ( 22, 80, 135, @@ -64,37 +64,38 @@ TCP_PORTS = [ 8983, 9200, 9600, -] +) TCP_SCAN_CONFIGURATION = TCPScanConfiguration(timeout=3.0, ports=TCP_PORTS) ICMP_CONFIGURATION = ICMPScanConfiguration(timeout=1.0) -HTTP_PORTS = [80, 443, 7001, 8008, 8080, 8983, 9200, 9600] -FINGERPRINTERS = [ +HTTP_PORTS = (80, 443, 7001, 8008, 8080, 8983, 9200, 9600) +FINGERPRINTERS = ( PluginConfiguration("elastic", {}), - PluginConfiguration("http", {"http_ports": HTTP_PORTS}), + # Plugin configuration option contents are not converted to tuples + PluginConfiguration("http", {"http_ports": list(HTTP_PORTS)}), PluginConfiguration("mssql", {}), PluginConfiguration("smb", {}), PluginConfiguration("ssh", {}), -] +) -SCAN_TARGET_CONFIGURATION = ScanTargetConfiguration([], [], True, []) +SCAN_TARGET_CONFIGURATION = ScanTargetConfiguration(tuple(), tuple(), True, tuple()) NETWORK_SCAN_CONFIGURATION = NetworkScanConfiguration( TCP_SCAN_CONFIGURATION, ICMP_CONFIGURATION, FINGERPRINTERS, SCAN_TARGET_CONFIGURATION ) EXPLOITATION_OPTIONS_CONFIGURATION = ExploitationOptionsConfiguration(HTTP_PORTS) -BRUTE_FORCE_EXPLOITERS = [ +BRUTE_FORCE_EXPLOITERS = ( PluginConfiguration("MSSQLExploiter", {}), PluginConfiguration("PowerShellExploiter", {}), PluginConfiguration("SSHExploiter", {}), PluginConfiguration("SmbExploiter", {"smb_download_timeout": 30}), PluginConfiguration("WmiExploiter", {"smb_download_timeout": 30}), -] +) -VULNERABILITY_EXPLOITERS = [ +VULNERABILITY_EXPLOITERS = ( PluginConfiguration("Log4ShellExploiter", {}), PluginConfiguration("HadoopExploiter", {}), -] +) EXPLOITATION_CONFIGURATION = ExploitationConfiguration( EXPLOITATION_OPTIONS_CONFIGURATION, BRUTE_FORCE_EXPLOITERS, VULNERABILITY_EXPLOITERS @@ -116,5 +117,5 @@ DEFAULT_AGENT_CONFIGURATION = AgentConfiguration( ) DEFAULT_RANSOMWARE_AGENT_CONFIGURATION = dataclasses.replace( - DEFAULT_AGENT_CONFIGURATION, post_breach_actions=[] + DEFAULT_AGENT_CONFIGURATION, post_breach_actions=tuple() ) diff --git a/monkey/common/agent_configuration/utils.py b/monkey/common/agent_configuration/utils.py new file mode 100644 index 000000000..21816076a --- /dev/null +++ b/monkey/common/agent_configuration/utils.py @@ -0,0 +1,13 @@ +from functools import wraps +from typing import Callable + +from common.utils.code_utils import freeze_lists_in_dict + + +def freeze_lists(function: Callable): + @wraps(function) + def wrapper(self, data, **kwargs): + data = freeze_lists_in_dict(data) + return function(self, data, **kwargs) + + return wrapper diff --git a/monkey/common/utils/code_utils.py b/monkey/common/utils/code_utils.py index 21c0ce175..7a5c34975 100644 --- a/monkey/common/utils/code_utils.py +++ b/monkey/common/utils/code_utils.py @@ -48,3 +48,10 @@ def del_key(mapping: MutableMapping[T, Any], key: T): del mapping[key] except KeyError: pass + + +def freeze_lists_in_dict(mapping: MutableMapping[str, Any]) -> MutableMapping[str, Any]: + for key, value in mapping.items(): + if type(value) == list: + mapping[key] = tuple(value) + return mapping diff --git a/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py b/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py index 6eacbe9f0..d2029d84a 100644 --- a/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py +++ b/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py @@ -73,10 +73,10 @@ def test_scan_target_configuration(): config = schema.load(SCAN_TARGET_CONFIGURATION) - assert config.blocked_ips == BLOCKED_IPS - assert config.inaccessible_subnets == INACCESSIBLE_SUBNETS + assert config.blocked_ips == tuple(BLOCKED_IPS) + assert config.inaccessible_subnets == tuple(INACCESSIBLE_SUBNETS) assert config.local_network_scan == LOCAL_NETWORK_SCAN - assert config.subnets == SUBNETS + assert config.subnets == tuple(SUBNETS) def test_icmp_scan_configuration_schema(): @@ -93,7 +93,7 @@ def test_tcp_scan_configuration_schema(): config = schema.load(TCP_SCAN_CONFIGURATION) assert config.timeout == TIMEOUT - assert config.ports == PORTS + assert config.ports == tuple(PORTS) def test_network_scan_configuration(): @@ -101,15 +101,15 @@ def test_network_scan_configuration(): config = schema.load(NETWORK_SCAN_CONFIGURATION) - assert config.tcp.ports == TCP_SCAN_CONFIGURATION["ports"] + assert config.tcp.ports == tuple(TCP_SCAN_CONFIGURATION["ports"]) assert config.tcp.timeout == TCP_SCAN_CONFIGURATION["timeout"] assert config.icmp.timeout == ICMP_CONFIGURATION["timeout"] assert config.fingerprinters[0].name == FINGERPRINTERS[0]["name"] assert config.fingerprinters[0].options == FINGERPRINTERS[0]["options"] - assert config.targets.blocked_ips == BLOCKED_IPS - assert config.targets.inaccessible_subnets == INACCESSIBLE_SUBNETS + assert config.targets.blocked_ips == tuple(BLOCKED_IPS) + assert config.targets.inaccessible_subnets == tuple(INACCESSIBLE_SUBNETS) assert config.targets.local_network_scan == LOCAL_NETWORK_SCAN - assert config.targets.subnets == SUBNETS + assert config.targets.subnets == tuple(SUBNETS) def test_exploitation_options_configuration_schema(): @@ -118,7 +118,7 @@ def test_exploitation_options_configuration_schema(): config = schema.load({"http_ports": ports}) - assert config.http_ports == ports + assert config.http_ports == tuple(ports) def test_exploiter_configuration_schema(): diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 4b4262b47..bca89e07b 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -194,6 +194,7 @@ _make_icmp_scan_configuration # unused method (monkey/common/configuration/agen _make_tcp_scan_configuration # unused method (monkey/common/configuration/agent_configuration.py:122) _make_network_scan_configuration # unused method (monkey/common/configuration/agent_configuration.py:110) _make_propagation_configuration # unused method (monkey/common/configuration/agent_configuration.py:167) +_make_agent_configuration # \common\agent_configuration\agent_configuration.py:110: unused method '_make_agent_configuration' # Credentials _strip_credential_type # unused method (monkey/common/credentials/password.py:18) From 1252ad3b874a7a37be6649c9329ceab2f23da3d9 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 26 Jul 2022 17:18:40 +0300 Subject: [PATCH 2/5] Common: Revert circular dependency in agent_configuration.py --- .../agent_configuration/agent_configuration.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/monkey/common/agent_configuration/agent_configuration.py b/monkey/common/agent_configuration/agent_configuration.py index ae21e87f7..9dbc5b3a4 100644 --- a/monkey/common/agent_configuration/agent_configuration.py +++ b/monkey/common/agent_configuration/agent_configuration.py @@ -16,7 +16,7 @@ from .agent_sub_configurations import ( PluginConfiguration, PropagationConfiguration, ) -from .utils import freeze_lists +from ..utils.code_utils import freeze_lists_in_dict class InvalidConfigurationError(Exception): @@ -59,7 +59,9 @@ class AgentConfiguration: """ try: - return AgentConfigurationSchema().load(config_mapping) + config_dict = AgentConfigurationSchema().load(config_mapping) + config_dict = freeze_lists_in_dict(config_dict) + return AgentConfiguration(**config_dict) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) @@ -74,7 +76,9 @@ class AgentConfiguration: AgentConfiguration """ try: - return AgentConfigurationSchema().loads(config_json) + config_dict = AgentConfigurationSchema().loads(config_json) + config_dict = freeze_lists_in_dict(config_dict) + return AgentConfiguration(**config_dict) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) @@ -106,8 +110,3 @@ class AgentConfigurationSchema(Schema): credential_collectors = fields.List(fields.Nested(PluginConfigurationSchema)) payloads = fields.List(fields.Nested(PluginConfigurationSchema)) propagation = fields.Nested(PropagationConfigurationSchema) - - @post_load - @freeze_lists - def _make_agent_configuration(self, data, **kwargs): - return AgentConfiguration(**data) From 0ab90aeaf5d2332380fc0b1e128c1e2502cf63d6 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 26 Jul 2022 17:20:10 +0300 Subject: [PATCH 3/5] Common: Fix configuration typehints to tuples Configuration types need to be tuples, because that's what we expect on the object. They can stay as lists in the marshmallow schemas, because marshmallow schemas expect to take lists. --- .../agent_configuration/agent_configuration.py | 12 ++++++------ .../agent_sub_configurations.py | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/monkey/common/agent_configuration/agent_configuration.py b/monkey/common/agent_configuration/agent_configuration.py index 9dbc5b3a4..ee8ac48b6 100644 --- a/monkey/common/agent_configuration/agent_configuration.py +++ b/monkey/common/agent_configuration/agent_configuration.py @@ -1,11 +1,12 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, List, Mapping +from typing import Any, Mapping, Tuple -from marshmallow import Schema, fields, post_load +from marshmallow import Schema, fields from marshmallow.exceptions import MarshmallowError +from ..utils.code_utils import freeze_lists_in_dict from .agent_sub_configuration_schemas import ( CustomPBAConfigurationSchema, PluginConfigurationSchema, @@ -16,7 +17,6 @@ from .agent_sub_configurations import ( PluginConfiguration, PropagationConfiguration, ) -from ..utils.code_utils import freeze_lists_in_dict class InvalidConfigurationError(Exception): @@ -34,9 +34,9 @@ class InvalidConfigurationError(Exception): class AgentConfiguration: keep_tunnel_open_time: float custom_pbas: CustomPBAConfiguration - post_breach_actions: List[PluginConfiguration] - credential_collectors: List[PluginConfiguration] - payloads: List[PluginConfiguration] + post_breach_actions: Tuple[PluginConfiguration] + credential_collectors: Tuple[PluginConfiguration] + payloads: Tuple[PluginConfiguration] propagation: PropagationConfiguration def __post_init__(self): diff --git a/monkey/common/agent_configuration/agent_sub_configurations.py b/monkey/common/agent_configuration/agent_sub_configurations.py index d93b4d774..7a3b88898 100644 --- a/monkey/common/agent_configuration/agent_sub_configurations.py +++ b/monkey/common/agent_configuration/agent_sub_configurations.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Dict, List +from typing import Dict, Tuple @dataclass(frozen=True) @@ -18,10 +18,10 @@ class PluginConfiguration: @dataclass(frozen=True) class ScanTargetConfiguration: - blocked_ips: List[str] - inaccessible_subnets: List[str] + blocked_ips: Tuple[str] + inaccessible_subnets: Tuple[str] local_network_scan: bool - subnets: List[str] + subnets: Tuple[str] @dataclass(frozen=True) @@ -32,27 +32,27 @@ class ICMPScanConfiguration: @dataclass(frozen=True) class TCPScanConfiguration: timeout: float - ports: List[int] + ports: Tuple[int] @dataclass(frozen=True) class NetworkScanConfiguration: tcp: TCPScanConfiguration icmp: ICMPScanConfiguration - fingerprinters: List[PluginConfiguration] + fingerprinters: Tuple[PluginConfiguration] targets: ScanTargetConfiguration @dataclass(frozen=True) class ExploitationOptionsConfiguration: - http_ports: List[int] + http_ports: Tuple[int] @dataclass(frozen=True) class ExploitationConfiguration: options: ExploitationOptionsConfiguration - brute_force: List[PluginConfiguration] - vulnerability: List[PluginConfiguration] + brute_force: Tuple[PluginConfiguration] + vulnerability: Tuple[PluginConfiguration] @dataclass(frozen=True) From eaeb78a8218d4f44d3327075315af4cca904dc94 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Tue, 26 Jul 2022 17:29:43 +0300 Subject: [PATCH 4/5] Common: Rename and improve freeze_lists_in_dict function --- monkey/common/agent_configuration/agent_configuration.py | 6 +++--- monkey/common/agent_configuration/utils.py | 4 ++-- monkey/common/utils/code_utils.py | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/monkey/common/agent_configuration/agent_configuration.py b/monkey/common/agent_configuration/agent_configuration.py index ee8ac48b6..e3e6caccf 100644 --- a/monkey/common/agent_configuration/agent_configuration.py +++ b/monkey/common/agent_configuration/agent_configuration.py @@ -6,7 +6,7 @@ from typing import Any, Mapping, Tuple from marshmallow import Schema, fields from marshmallow.exceptions import MarshmallowError -from ..utils.code_utils import freeze_lists_in_dict +from ..utils.code_utils import freeze_lists_in_mapping from .agent_sub_configuration_schemas import ( CustomPBAConfigurationSchema, PluginConfigurationSchema, @@ -60,7 +60,7 @@ class AgentConfiguration: try: config_dict = AgentConfigurationSchema().load(config_mapping) - config_dict = freeze_lists_in_dict(config_dict) + config_dict = freeze_lists_in_mapping(config_dict) return AgentConfiguration(**config_dict) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) @@ -77,7 +77,7 @@ class AgentConfiguration: """ try: config_dict = AgentConfigurationSchema().loads(config_json) - config_dict = freeze_lists_in_dict(config_dict) + config_dict = freeze_lists_in_mapping(config_dict) return AgentConfiguration(**config_dict) except MarshmallowError as err: raise InvalidConfigurationError(str(err)) diff --git a/monkey/common/agent_configuration/utils.py b/monkey/common/agent_configuration/utils.py index 21816076a..3470e57a1 100644 --- a/monkey/common/agent_configuration/utils.py +++ b/monkey/common/agent_configuration/utils.py @@ -1,13 +1,13 @@ from functools import wraps from typing import Callable -from common.utils.code_utils import freeze_lists_in_dict +from common.utils.code_utils import freeze_lists_in_mapping def freeze_lists(function: Callable): @wraps(function) def wrapper(self, data, **kwargs): - data = freeze_lists_in_dict(data) + data = freeze_lists_in_mapping(data) return function(self, data, **kwargs) return wrapper diff --git a/monkey/common/utils/code_utils.py b/monkey/common/utils/code_utils.py index 7a5c34975..52fdcbaac 100644 --- a/monkey/common/utils/code_utils.py +++ b/monkey/common/utils/code_utils.py @@ -1,4 +1,5 @@ import queue +from collections.abc import MutableSequence from typing import Any, List, MutableMapping, TypeVar T = TypeVar("T") @@ -50,8 +51,8 @@ def del_key(mapping: MutableMapping[T, Any], key: T): pass -def freeze_lists_in_dict(mapping: MutableMapping[str, Any]) -> MutableMapping[str, Any]: +def freeze_lists_in_mapping(mapping: MutableMapping[str, Any]) -> MutableMapping[str, Any]: for key, value in mapping.items(): - if type(value) == list: + if isinstance(value, MutableSequence): mapping[key] = tuple(value) return mapping From 7be4ea28938e1a48bbfdb0d8d7a98bd9f90e1da7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 26 Jul 2022 10:42:20 -0400 Subject: [PATCH 5/5] Common: Fix type hint for variable-length homogeneous tuples See https://docs.python.org/3/library/typing.html#typing.Tuple --- .../agent_configuration/agent_configuration.py | 6 +++--- .../agent_sub_configurations.py | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/monkey/common/agent_configuration/agent_configuration.py b/monkey/common/agent_configuration/agent_configuration.py index e3e6caccf..00b39dce4 100644 --- a/monkey/common/agent_configuration/agent_configuration.py +++ b/monkey/common/agent_configuration/agent_configuration.py @@ -34,9 +34,9 @@ class InvalidConfigurationError(Exception): class AgentConfiguration: keep_tunnel_open_time: float custom_pbas: CustomPBAConfiguration - post_breach_actions: Tuple[PluginConfiguration] - credential_collectors: Tuple[PluginConfiguration] - payloads: Tuple[PluginConfiguration] + post_breach_actions: Tuple[PluginConfiguration, ...] + credential_collectors: Tuple[PluginConfiguration, ...] + payloads: Tuple[PluginConfiguration, ...] propagation: PropagationConfiguration def __post_init__(self): diff --git a/monkey/common/agent_configuration/agent_sub_configurations.py b/monkey/common/agent_configuration/agent_sub_configurations.py index 7a3b88898..ad321b3d0 100644 --- a/monkey/common/agent_configuration/agent_sub_configurations.py +++ b/monkey/common/agent_configuration/agent_sub_configurations.py @@ -18,10 +18,10 @@ class PluginConfiguration: @dataclass(frozen=True) class ScanTargetConfiguration: - blocked_ips: Tuple[str] - inaccessible_subnets: Tuple[str] + blocked_ips: Tuple[str, ...] + inaccessible_subnets: Tuple[str, ...] local_network_scan: bool - subnets: Tuple[str] + subnets: Tuple[str, ...] @dataclass(frozen=True) @@ -32,27 +32,27 @@ class ICMPScanConfiguration: @dataclass(frozen=True) class TCPScanConfiguration: timeout: float - ports: Tuple[int] + ports: Tuple[int, ...] @dataclass(frozen=True) class NetworkScanConfiguration: tcp: TCPScanConfiguration icmp: ICMPScanConfiguration - fingerprinters: Tuple[PluginConfiguration] + fingerprinters: Tuple[PluginConfiguration, ...] targets: ScanTargetConfiguration @dataclass(frozen=True) class ExploitationOptionsConfiguration: - http_ports: Tuple[int] + http_ports: Tuple[int, ...] @dataclass(frozen=True) class ExploitationConfiguration: options: ExploitationOptionsConfiguration - brute_force: Tuple[PluginConfiguration] - vulnerability: Tuple[PluginConfiguration] + brute_force: Tuple[PluginConfiguration, ...] + vulnerability: Tuple[PluginConfiguration, ...] @dataclass(frozen=True)