From a94389156762e49554e85c302387c02fd70c85be Mon Sep 17 00:00:00 2001 From: vakarisz Date: Wed, 27 Jul 2022 17:07:52 +0300 Subject: [PATCH 01/12] Common: Add validators for ScanTargetConfigurationSchema --- .../agent_sub_configuration_schemas.py | 7 +-- .../validators/__init__.py | 0 .../validators/ip_ranges.py | 24 +++++++++ monkey/tests/unit_tests/common/__init__.py | 0 .../common/agent_configuration/__init__.py | 0 .../test_agent_configuration.py | 0 .../validators/__init__.py | 0 .../validators/test_ip_ranges.py | 50 +++++++++++++++++++ 8 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 monkey/common/agent_configuration/validators/__init__.py create mode 100644 monkey/common/agent_configuration/validators/ip_ranges.py create mode 100644 monkey/tests/unit_tests/common/__init__.py create mode 100644 monkey/tests/unit_tests/common/agent_configuration/__init__.py rename monkey/tests/unit_tests/common/{configuration => agent_configuration}/test_agent_configuration.py (100%) create mode 100644 monkey/tests/unit_tests/common/agent_configuration/validators/__init__.py create mode 100644 monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py diff --git a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py index d47730fec..30c49c27f 100644 --- a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py +++ b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py @@ -14,6 +14,7 @@ from .agent_sub_configurations import ( TCPScanConfiguration, ) from .utils import freeze_lists +from .validators.ip_ranges import validate_ip, validate_subnet_range valid_windows_custom_pba_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$") valid_linux_custom_pba_filename_regex = re.compile(r"^[^\0/]*$") @@ -73,10 +74,10 @@ class PluginConfigurationSchema(Schema): class ScanTargetConfigurationSchema(Schema): - blocked_ips = fields.List(fields.Str()) - inaccessible_subnets = fields.List(fields.Str()) + blocked_ips = fields.List(fields.Str(validate=validate_ip)) + inaccessible_subnets = fields.List(fields.Str(validate=validate_subnet_range)) local_network_scan = fields.Bool() - subnets = fields.List(fields.Str()) + subnets = fields.List(fields.Str(validate=validate_subnet_range)) @post_load @freeze_lists diff --git a/monkey/common/agent_configuration/validators/__init__.py b/monkey/common/agent_configuration/validators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/common/agent_configuration/validators/ip_ranges.py b/monkey/common/agent_configuration/validators/ip_ranges.py new file mode 100644 index 000000000..238ee215f --- /dev/null +++ b/monkey/common/agent_configuration/validators/ip_ranges.py @@ -0,0 +1,24 @@ +import re + +from marshmallow import ValidationError + +ip_regex = r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +cird_notation_regex = r"([0-9]|1[0-9]|2[0-9]|3[0-2])" +hostname_regex = r"([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)" + + +def validate_subnet_range(subnet_range: str): + range_regexes = [ + "^" + ip_regex + "$|", + "^" + ip_regex + r"\s*-\s*" + ip_regex + "$|", + "^" + ip_regex + "/" + cird_notation_regex + "$|", + "^" + hostname_regex + "$", + ] + range_regexes = re.compile("".join(range_regexes)) + if not re.match(range_regexes, subnet_range): + raise ValidationError(f"Invalid subnet range {subnet_range}") + + +def validate_ip(ip: str): + if not re.match(re.compile("".join(["^", ip_regex, "$"])), ip): + raise ValidationError(f"Invalid ip address {ip}") diff --git a/monkey/tests/unit_tests/common/__init__.py b/monkey/tests/unit_tests/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/tests/unit_tests/common/agent_configuration/__init__.py b/monkey/tests/unit_tests/common/agent_configuration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/tests/unit_tests/common/configuration/test_agent_configuration.py b/monkey/tests/unit_tests/common/agent_configuration/test_agent_configuration.py similarity index 100% rename from monkey/tests/unit_tests/common/configuration/test_agent_configuration.py rename to monkey/tests/unit_tests/common/agent_configuration/test_agent_configuration.py diff --git a/monkey/tests/unit_tests/common/agent_configuration/validators/__init__.py b/monkey/tests/unit_tests/common/agent_configuration/validators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py new file mode 100644 index 000000000..4af8f0b4a --- /dev/null +++ b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py @@ -0,0 +1,50 @@ +import pytest +from marshmallow import ValidationError + +from common.agent_configuration.validators.ip_ranges import validate_ip, validate_subnet_range + + +def test_validate_ip(): + for good_input in ["192.168.56.1", "0.0.0.0"]: + validate_ip(good_input) + + for bad_input in ["1.1.1", "257.256.255.255", "1.1.1.1.1"]: + with pytest.raises(ValidationError): + validate_ip(bad_input) + + +def test_validate_subnet_range__ip(): + _test_subent_range( + good_inputs=["192.168.56.1", "0.0.0.0"], + bad_inputs=["1.1.1", "257.256.255.255", "1.1.1.1.1"], + ) + + +def test_validate_subnet_range__ip_range(): + _test_subent_range( + good_inputs=["1.1.1.1 - 2.2.2.2", "1.1.1.255-1.1.1.1"], + bad_inputs=["1.1.1-2.2.2.2", "0-.1.1.1-2.2.2.2", "a..1.1.1-2.2.2.2", "257.1.1.1-2.2.2.2"], + ) + + +def test_validate_subnet_range__hostname(): + _test_subent_range( + good_inputs=["infection.monkey", "1nfection-Monkey"], + bad_inputs=["hy&!he.host", "čili-peppers.are-hot"], + ) + + +def test_validate_subnet_range__cidr(): + _test_subent_range( + good_inputs=["1.1.1.1/24", "1.1.1.1/0"], + bad_inputs=["1.1.1/24", "1.1.1.1/-1", "1.1.1.1/33", "1.1.1.1/222"], + ) + + +def _test_subent_range(good_inputs, bad_inputs): + for good_input in good_inputs: + validate_subnet_range(good_input) + + for bad_input in bad_inputs: + with pytest.raises(ValidationError): + validate_subnet_range(bad_input) From 96af1e0135e99d187e8b2f525fb2c1711855e865 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Wed, 27 Jul 2022 17:08:54 +0300 Subject: [PATCH 02/12] Common: Add documentation to ScanTargetConfiguration dataclass --- .../agent_configuration/agent_sub_configurations.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monkey/common/agent_configuration/agent_sub_configurations.py b/monkey/common/agent_configuration/agent_sub_configurations.py index f58bcd9b0..310c88e18 100644 --- a/monkey/common/agent_configuration/agent_sub_configurations.py +++ b/monkey/common/agent_configuration/agent_sub_configurations.py @@ -54,6 +54,18 @@ class PluginConfiguration: @dataclass(frozen=True) class ScanTargetConfiguration: + """ + Configuration of network targets to scan and exploit + + Attributes: + :param blocked_ips: Tuple of IP's that won't be scanned. Example: ("1.1.1.1", "2.2.2.2") + :param inaccessible_subnets: Tuple of subnet ranges that shouldn't be accessible for the + agent. Example: ("1.1.1.1", "2.2.2.2/24", "myserver") + :param local_network_scan: Boolean indicating if the agent should scan the local network + :param subnets: Tuple of subnet ranges to scan. Example: ("192.168.1.1-192.168.2.255", + "3.3.3.3", "2.2.2.2/24", "myHostname") + """ + blocked_ips: Tuple[str, ...] inaccessible_subnets: Tuple[str, ...] local_network_scan: bool From a1d9118433f867c3df3bf1a77e4c29b7e0a452d9 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 28 Jul 2022 11:23:56 +0300 Subject: [PATCH 03/12] UT: Split up ip range validation tests more --- .../validators/test_ip_ranges.py | 84 +++++++++++-------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py index 4af8f0b4a..cebb5a9f1 100644 --- a/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py +++ b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py @@ -4,47 +4,65 @@ from marshmallow import ValidationError from common.agent_configuration.validators.ip_ranges import validate_ip, validate_subnet_range -def test_validate_ip(): - for good_input in ["192.168.56.1", "0.0.0.0"]: - validate_ip(good_input) - - for bad_input in ["1.1.1", "257.256.255.255", "1.1.1.1.1"]: - with pytest.raises(ValidationError): - validate_ip(bad_input) +@pytest.mark.parametrize("ip", ["192.168.56.1", "0.0.0.0"]) +def test_validate_ip_valid(ip): + validate_ip(ip) -def test_validate_subnet_range__ip(): - _test_subent_range( - good_inputs=["192.168.56.1", "0.0.0.0"], - bad_inputs=["1.1.1", "257.256.255.255", "1.1.1.1.1"], - ) +@pytest.mark.parametrize("ip", ["1.1.1", "257.256.255.255", "1.1.1.1.1"]) +def test_validate_ip_invalid(ip): + with pytest.raises(ValidationError): + validate_ip(ip) -def test_validate_subnet_range__ip_range(): - _test_subent_range( - good_inputs=["1.1.1.1 - 2.2.2.2", "1.1.1.255-1.1.1.1"], - bad_inputs=["1.1.1-2.2.2.2", "0-.1.1.1-2.2.2.2", "a..1.1.1-2.2.2.2", "257.1.1.1-2.2.2.2"], - ) +@pytest.mark.parametrize("ip", ["192.168.56.1", "0.0.0.0"]) +def test_validate_subnet_range__ip_valid(ip): + validate_subnet_range(ip) -def test_validate_subnet_range__hostname(): - _test_subent_range( - good_inputs=["infection.monkey", "1nfection-Monkey"], - bad_inputs=["hy&!he.host", "čili-peppers.are-hot"], - ) +@pytest.mark.parametrize("ip", ["1.1.1", "257.256.255.255", "1.1.1.1.1"]) +def test_validate_subnet_range__ip_invalid(ip): + with pytest.raises(ValidationError): + validate_subnet_range(ip) -def test_validate_subnet_range__cidr(): - _test_subent_range( - good_inputs=["1.1.1.1/24", "1.1.1.1/0"], - bad_inputs=["1.1.1/24", "1.1.1.1/-1", "1.1.1.1/33", "1.1.1.1/222"], - ) +@pytest.mark.parametrize("ip_range", ["1.1.1.1 - 2.2.2.2", "1.1.1.255-1.1.1.1"]) +def test_validate_subnet_range__ip_range_valid(ip_range): + validate_subnet_range(ip_range) -def _test_subent_range(good_inputs, bad_inputs): - for good_input in good_inputs: - validate_subnet_range(good_input) +@pytest.mark.parametrize( + "ip_range", + [ + "1.1.1-2.2.2.2", + "0-.1.1.1-2.2.2.2", + "a..1.1.1-2.2.2.2", + "257.1.1.1-2.2.2.2", + "1.1.1.1-2.2.2.2-3.3.3.3", + ], +) +def test_validate_subnet_range__ip_range_invalid(ip_range): + with pytest.raises(ValidationError): + validate_subnet_range(ip_range) - for bad_input in bad_inputs: - with pytest.raises(ValidationError): - validate_subnet_range(bad_input) + +@pytest.mark.parametrize("hostname", ["infection.monkey", "1nfection-Monkey"]) +def test_validate_subnet_range__hostname_valid(hostname): + validate_subnet_range(hostname) + + +@pytest.mark.parametrize("hostname", ["hy&!he.host", "čili-peppers.are-hot"]) +def test_validate_subnet_range__hostname_invalid(hostname): + with pytest.raises(ValidationError): + validate_subnet_range(hostname) + + +@pytest.mark.parametrize("cidr_range", ["1.1.1.1/24", "1.1.1.1/0"]) +def test_validate_subnet_range__cidr_valid(cidr_range): + validate_subnet_range(cidr_range) + + +@pytest.mark.parametrize("cidr_range", ["1.1.1/24", "1.1.1.1/-1", "1.1.1.1/33", "1.1.1.1/222"]) +def test_validate_subnet_range__cidr_invalid(cidr_range): + with pytest.raises(ValidationError): + validate_subnet_range(cidr_range) From ce33a7b2f4748d980d15d530c745e34d55b4d532 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 28 Jul 2022 11:29:43 +0300 Subject: [PATCH 04/12] Common: Improve network range validation (less regex) --- .../validators/ip_ranges.py | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/monkey/common/agent_configuration/validators/ip_ranges.py b/monkey/common/agent_configuration/validators/ip_ranges.py index 238ee215f..82207941a 100644 --- a/monkey/common/agent_configuration/validators/ip_ranges.py +++ b/monkey/common/agent_configuration/validators/ip_ranges.py @@ -1,24 +1,64 @@ import re +from ipaddress import AddressValueError, IPv4Address, IPv4Network, NetmaskValueError from marshmallow import ValidationError -ip_regex = r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" -cird_notation_regex = r"([0-9]|1[0-9]|2[0-9]|3[0-2])" -hostname_regex = r"([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)" +hostname_pattern = r"([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)" +hostname_regex = re.compile(hostname_pattern) def validate_subnet_range(subnet_range: str): - range_regexes = [ - "^" + ip_regex + "$|", - "^" + ip_regex + r"\s*-\s*" + ip_regex + "$|", - "^" + ip_regex + "/" + cird_notation_regex + "$|", - "^" + hostname_regex + "$", - ] - range_regexes = re.compile("".join(range_regexes)) - if not re.match(range_regexes, subnet_range): + try: + return validate_ip(subnet_range) + except ValidationError: + pass + + try: + return validate_ip_range(subnet_range) + except ValidationError: + pass + + try: + return validate_ip_network(subnet_range) + except ValidationError: + pass + + try: + return validate_hostname(subnet_range) + except ValidationError: raise ValidationError(f"Invalid subnet range {subnet_range}") +def validate_hostname(hostname: str): + match = re.match(hostname_regex, hostname) + if match and match.group() == hostname: + return + else: + raise ValidationError(f"Invalid hostname {hostname}") + + +def validate_ip_network(ip_network: str): + try: + IPv4Network(ip_network, strict=False) + return + except (NetmaskValueError, AddressValueError): + raise ValidationError(f"Invalid IPv4 network {ip_network}") + + +def validate_ip_range(ip_range: str): + try: + ip_range = ip_range.replace(" ", "") + ips = ip_range.split("-") + validate_ip(ips[0]) + validate_ip(ips[1]) + if len(ips) != 2: + raise ValidationError(f"Invalid IP range {ip_range}") + except (AddressValueError, IndexError): + raise ValidationError(f"Invalid IP range {ip_range}") + + def validate_ip(ip: str): - if not re.match(re.compile("".join(["^", ip_regex, "$"])), ip): - raise ValidationError(f"Invalid ip address {ip}") + try: + IPv4Address(ip) + except AddressValueError: + raise ValidationError(f"Invalid IP address {ip}") From a1760a8701478ed87c51050f4a48f752100f1810 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 28 Jul 2022 16:35:04 +0300 Subject: [PATCH 05/12] Common: Improve hostname pattern matching in scan target configuration --- .../validators/ip_ranges.py | 23 ++++++++++++------- .../validators/test_ip_ranges.py | 6 +++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/monkey/common/agent_configuration/validators/ip_ranges.py b/monkey/common/agent_configuration/validators/ip_ranges.py index 82207941a..e48939a16 100644 --- a/monkey/common/agent_configuration/validators/ip_ranges.py +++ b/monkey/common/agent_configuration/validators/ip_ranges.py @@ -3,9 +3,6 @@ from ipaddress import AddressValueError, IPv4Address, IPv4Network, NetmaskValueE from marshmallow import ValidationError -hostname_pattern = r"([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)" -hostname_regex = re.compile(hostname_pattern) - def validate_subnet_range(subnet_range: str): try: @@ -30,11 +27,21 @@ def validate_subnet_range(subnet_range: str): def validate_hostname(hostname: str): - match = re.match(hostname_regex, hostname) - if match and match.group() == hostname: - return - else: - raise ValidationError(f"Invalid hostname {hostname}") + # Based on hostname syntax: https://www.rfc-editor.org/rfc/rfc1123#page-13 + hostname_segments = hostname.split(".") + if any((part.endswith("-") or part.startswith("-") for part in hostname_segments)): + raise ValidationError(f"Hostname segment can't start or end with a hyphen: {hostname}") + if not any((char.isalpha() for char in hostname_segments[-1])): + raise ValidationError(f"Last segment of a hostname must contain a letter: {hostname}") + + valid_characters_pattern = r"^[A-Za-z0-9\-]+$" + valid_characters_regex = re.compile(valid_characters_pattern) + matches = ( + re.match(valid_characters_regex, hostname_segment) for hostname_segment in hostname_segments + ) + + if not all(matches): + raise ValidationError(f"Hostname contains invalid characters: {hostname}") def validate_ip_network(ip_network: str): diff --git a/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py index cebb5a9f1..80f24497c 100644 --- a/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py +++ b/monkey/tests/unit_tests/common/agent_configuration/validators/test_ip_ranges.py @@ -46,12 +46,14 @@ def test_validate_subnet_range__ip_range_invalid(ip_range): validate_subnet_range(ip_range) -@pytest.mark.parametrize("hostname", ["infection.monkey", "1nfection-Monkey"]) +@pytest.mark.parametrize("hostname", ["infection.monkey", "1nfection-Monkey", "1.1.1.1a"]) def test_validate_subnet_range__hostname_valid(hostname): validate_subnet_range(hostname) -@pytest.mark.parametrize("hostname", ["hy&!he.host", "čili-peppers.are-hot"]) +@pytest.mark.parametrize( + "hostname", ["hy&!he.host", "čili-peppers.are-hot", "one.two-", "one-.two", "one@two", ""] +) def test_validate_subnet_range__hostname_invalid(hostname): with pytest.raises(ValidationError): validate_subnet_range(hostname) From 16ac6b2e15f6858c67aad7c5d1a71ba251e23ebc Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 10:50:29 +0300 Subject: [PATCH 06/12] Common: Improve documentation of ScanTargetConfiguration --- .../agent_configuration/agent_sub_configurations.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/common/agent_configuration/agent_sub_configurations.py b/monkey/common/agent_configuration/agent_sub_configurations.py index 310c88e18..d2b4c5aa8 100644 --- a/monkey/common/agent_configuration/agent_sub_configurations.py +++ b/monkey/common/agent_configuration/agent_sub_configurations.py @@ -58,11 +58,11 @@ class ScanTargetConfiguration: Configuration of network targets to scan and exploit Attributes: - :param blocked_ips: Tuple of IP's that won't be scanned. Example: ("1.1.1.1", "2.2.2.2") - :param inaccessible_subnets: Tuple of subnet ranges that shouldn't be accessible for the + :param blocked_ips: IP's that won't be scanned. Example: ("1.1.1.1", "2.2.2.2") + :param inaccessible_subnets: Subnet ranges that shouldn't be accessible for the agent. Example: ("1.1.1.1", "2.2.2.2/24", "myserver") - :param local_network_scan: Boolean indicating if the agent should scan the local network - :param subnets: Tuple of subnet ranges to scan. Example: ("192.168.1.1-192.168.2.255", + :param local_network_scan: Indicating if the agent should scan the local network + :param subnets: Subnet ranges to scan. Example: ("192.168.1.1-192.168.2.255", "3.3.3.3", "2.2.2.2/24", "myHostname") """ From 3a6fcd670c075d5a70ac576ced61ab662a7450a5 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 10:56:45 +0300 Subject: [PATCH 07/12] Common: Style improvements in ip_ranges.py --- monkey/common/agent_configuration/validators/ip_ranges.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/monkey/common/agent_configuration/validators/ip_ranges.py b/monkey/common/agent_configuration/validators/ip_ranges.py index e48939a16..5fca61b3d 100644 --- a/monkey/common/agent_configuration/validators/ip_ranges.py +++ b/monkey/common/agent_configuration/validators/ip_ranges.py @@ -47,7 +47,6 @@ def validate_hostname(hostname: str): def validate_ip_network(ip_network: str): try: IPv4Network(ip_network, strict=False) - return except (NetmaskValueError, AddressValueError): raise ValidationError(f"Invalid IPv4 network {ip_network}") @@ -56,11 +55,11 @@ def validate_ip_range(ip_range: str): try: ip_range = ip_range.replace(" ", "") ips = ip_range.split("-") - validate_ip(ips[0]) - validate_ip(ips[1]) if len(ips) != 2: raise ValidationError(f"Invalid IP range {ip_range}") - except (AddressValueError, IndexError): + validate_ip(ips[0]) + validate_ip(ips[1]) + except IndexError: raise ValidationError(f"Invalid IP range {ip_range}") From 2503e83dde0e0f2ec09cb815d1a8a7cde76f22da Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 13:01:37 +0300 Subject: [PATCH 08/12] Common: Separate filename validation out of config schemas --- .../agent_sub_configuration_schemas.py | 44 +++---------------- .../validators/filenames.py | 19 ++++++++ 2 files changed, 25 insertions(+), 38 deletions(-) create mode 100644 monkey/common/agent_configuration/validators/filenames.py diff --git a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py index 30c49c27f..b6a803eb6 100644 --- a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py +++ b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py @@ -1,6 +1,4 @@ -import re - -from marshmallow import Schema, ValidationError, fields, post_load, validate, validates +from marshmallow import Schema, fields, post_load, validate from .agent_sub_configurations import ( CustomPBAConfiguration, @@ -14,11 +12,12 @@ from .agent_sub_configurations import ( TCPScanConfiguration, ) from .utils import freeze_lists +from .validators.filenames import ( + valid_linux_custom_pba_filename_regex, + validate_windows_custom_pba_filename, +) from .validators.ip_ranges import validate_ip, validate_subnet_range -valid_windows_custom_pba_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$") -valid_linux_custom_pba_filename_regex = re.compile(r"^[^\0/]*$") - class CustomPBAConfigurationSchema(Schema): linux_command = fields.Str() @@ -26,38 +25,7 @@ class CustomPBAConfigurationSchema(Schema): validate=validate.Regexp(regex=valid_linux_custom_pba_filename_regex) ) windows_command = fields.Str() - windows_filename = fields.Str( - validate=validate.Regexp(regex=valid_windows_custom_pba_filename_regex) - ) - - @validates("windows_filename") - def validate_windows_filename_not_reserved(self, windows_filename): - # filename shouldn't start with any of these and be followed by a period - if windows_filename.split(".")[0].upper() in [ - "CON", - "PRN", - "AUX", - "NUL", - "COM1", - "COM2", - "COM3", - "COM4", - "COM5", - "COM6", - "COM7", - "COM8", - "COM9", - "LPT1", - "LPT2", - "LPT3", - "LPT4", - "LPT5", - "LPT6", - "LPT7", - "LPT8", - "LPT9", - ]: - raise ValidationError("Invalid Windows filename: reserved name used") + windows_filename = fields.Str(validate=validate_windows_custom_pba_filename) @post_load def _make_custom_pba_configuration(self, data, **kwargs): diff --git a/monkey/common/agent_configuration/validators/filenames.py b/monkey/common/agent_configuration/validators/filenames.py new file mode 100644 index 000000000..33ccb8cd9 --- /dev/null +++ b/monkey/common/agent_configuration/validators/filenames.py @@ -0,0 +1,19 @@ +import re +from pathlib import PureWindowsPath + +from marshmallow import ValidationError + +valid_windows_custom_pba_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$") +valid_linux_custom_pba_filename_regex = re.compile(r"^[^\0/]*$") + + +def validate_windows_custom_pba_filename(windows_filename: str): + validate_windows_filename_not_reserved(windows_filename) + if not re.match(valid_windows_custom_pba_filename_regex, windows_filename): + raise ValidationError(f"Invalid Windows filename {windows_filename}: illegal characters") + + +def validate_windows_filename_not_reserved(windows_filename: str): + # filename shouldn't start with any of these and be followed by a period + if PureWindowsPath(windows_filename).is_reserved(): + raise ValidationError(f"Invalid Windows filename {windows_filename}: reserved name used") From 068a7e3b1ff45b99bcd37f20276459145faa100f Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 14:33:19 +0300 Subject: [PATCH 09/12] Common: Improve the style of filename validation --- .../agent_sub_configuration_schemas.py | 11 +++-------- .../agent_configuration/validators/filenames.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py index b6a803eb6..bf5f31c61 100644 --- a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py +++ b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py @@ -12,20 +12,15 @@ from .agent_sub_configurations import ( TCPScanConfiguration, ) from .utils import freeze_lists -from .validators.filenames import ( - valid_linux_custom_pba_filename_regex, - validate_windows_custom_pba_filename, -) +from .validators.filenames import validate_linux_filename, validate_windows_filename from .validators.ip_ranges import validate_ip, validate_subnet_range class CustomPBAConfigurationSchema(Schema): linux_command = fields.Str() - linux_filename = fields.Str( - validate=validate.Regexp(regex=valid_linux_custom_pba_filename_regex) - ) + linux_filename = fields.Str(validate=validate_linux_filename) windows_command = fields.Str() - windows_filename = fields.Str(validate=validate_windows_custom_pba_filename) + windows_filename = fields.Str(validate=validate_windows_filename) @post_load def _make_custom_pba_configuration(self, data, **kwargs): diff --git a/monkey/common/agent_configuration/validators/filenames.py b/monkey/common/agent_configuration/validators/filenames.py index 33ccb8cd9..897fa2f13 100644 --- a/monkey/common/agent_configuration/validators/filenames.py +++ b/monkey/common/agent_configuration/validators/filenames.py @@ -3,13 +3,18 @@ from pathlib import PureWindowsPath from marshmallow import ValidationError -valid_windows_custom_pba_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$") -valid_linux_custom_pba_filename_regex = re.compile(r"^[^\0/]*$") +_valid_windows_filename_regex = re.compile(r"^[^<>:\"\\\/|?*]*[^<>:\"\\\/|?* \.]+$|^$") +_valid_linux_filename_regex = re.compile(r"^[^\0/]*$") -def validate_windows_custom_pba_filename(windows_filename: str): +def validate_linux_filename(linux_filename: str): + if not re.match(_valid_linux_filename_regex, linux_filename): + raise ValidationError(f"Invalid Unix filename {linux_filename}: illegal characters") + + +def validate_windows_filename(windows_filename: str): validate_windows_filename_not_reserved(windows_filename) - if not re.match(valid_windows_custom_pba_filename_regex, windows_filename): + if not re.match(_valid_windows_filename_regex, windows_filename): raise ValidationError(f"Invalid Windows filename {windows_filename}: illegal characters") From d2d9d4c4c3ccc1ac6c18296828118edd820a75ed Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 1 Aug 2022 09:16:00 -0400 Subject: [PATCH 10/12] Common: Reformat ScanTargetConfiguration docstrings --- .../agent_sub_configurations.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/monkey/common/agent_configuration/agent_sub_configurations.py b/monkey/common/agent_configuration/agent_sub_configurations.py index d2b4c5aa8..4ed94d7a8 100644 --- a/monkey/common/agent_configuration/agent_sub_configurations.py +++ b/monkey/common/agent_configuration/agent_sub_configurations.py @@ -58,12 +58,14 @@ class ScanTargetConfiguration: Configuration of network targets to scan and exploit Attributes: - :param blocked_ips: IP's that won't be scanned. Example: ("1.1.1.1", "2.2.2.2") - :param inaccessible_subnets: Subnet ranges that shouldn't be accessible for the - agent. Example: ("1.1.1.1", "2.2.2.2/24", "myserver") - :param local_network_scan: Indicating if the agent should scan the local network - :param subnets: Subnet ranges to scan. Example: ("192.168.1.1-192.168.2.255", - "3.3.3.3", "2.2.2.2/24", "myHostname") + :param blocked_ips: IP's that won't be scanned + Example: ("1.1.1.1", "2.2.2.2") + :param inaccessible_subnets: Subnet ranges that shouldn't be accessible for the agent + Example: ("1.1.1.1", "2.2.2.2/24", "myserver") + :param local_network_scan: Whether or not the agent should scan the local network + :param subnets: Subnet ranges to scan + Example: ("192.168.1.1-192.168.2.255", "3.3.3.3", "2.2.2.2/24", + "myHostname") """ blocked_ips: Tuple[str, ...] From 42e6039463d67966d931b4562c045594c4df0483 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 16:49:23 +0300 Subject: [PATCH 11/12] Common: Shorten the validator import statements --- .../agent_sub_configuration_schemas.py | 8 ++++++-- monkey/common/agent_configuration/validators/__init__.py | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py index bf5f31c61..401c04d90 100644 --- a/monkey/common/agent_configuration/agent_sub_configuration_schemas.py +++ b/monkey/common/agent_configuration/agent_sub_configuration_schemas.py @@ -12,8 +12,12 @@ from .agent_sub_configurations import ( TCPScanConfiguration, ) from .utils import freeze_lists -from .validators.filenames import validate_linux_filename, validate_windows_filename -from .validators.ip_ranges import validate_ip, validate_subnet_range +from .validators import ( + validate_ip, + validate_linux_filename, + validate_subnet_range, + validate_windows_filename, +) class CustomPBAConfigurationSchema(Schema): diff --git a/monkey/common/agent_configuration/validators/__init__.py b/monkey/common/agent_configuration/validators/__init__.py index e69de29bb..f2c6dfad5 100644 --- a/monkey/common/agent_configuration/validators/__init__.py +++ b/monkey/common/agent_configuration/validators/__init__.py @@ -0,0 +1,8 @@ +from .filenames import validate_linux_filename, validate_windows_filename +from .ip_ranges import ( + validate_ip, + validate_hostname, + validate_ip_range, + validate_subnet_range, + validate_ip_network, +) From 22b3874b5a4accd2fbc996ecc0f5f5ecfdb35785 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 1 Aug 2022 16:54:28 +0300 Subject: [PATCH 12/12] Common: Do small style fixes in validators --- .../agent_configuration/validators/filenames.py | 4 ++-- .../agent_configuration/validators/ip_ranges.py | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/monkey/common/agent_configuration/validators/filenames.py b/monkey/common/agent_configuration/validators/filenames.py index 897fa2f13..2a8e4df01 100644 --- a/monkey/common/agent_configuration/validators/filenames.py +++ b/monkey/common/agent_configuration/validators/filenames.py @@ -13,12 +13,12 @@ def validate_linux_filename(linux_filename: str): def validate_windows_filename(windows_filename: str): - validate_windows_filename_not_reserved(windows_filename) + _validate_windows_filename_not_reserved(windows_filename) if not re.match(_valid_windows_filename_regex, windows_filename): raise ValidationError(f"Invalid Windows filename {windows_filename}: illegal characters") -def validate_windows_filename_not_reserved(windows_filename: str): +def _validate_windows_filename_not_reserved(windows_filename: str): # filename shouldn't start with any of these and be followed by a period if PureWindowsPath(windows_filename).is_reserved(): raise ValidationError(f"Invalid Windows filename {windows_filename}: reserved name used") diff --git a/monkey/common/agent_configuration/validators/ip_ranges.py b/monkey/common/agent_configuration/validators/ip_ranges.py index 5fca61b3d..6eabc9e61 100644 --- a/monkey/common/agent_configuration/validators/ip_ranges.py +++ b/monkey/common/agent_configuration/validators/ip_ranges.py @@ -52,15 +52,12 @@ def validate_ip_network(ip_network: str): def validate_ip_range(ip_range: str): - try: - ip_range = ip_range.replace(" ", "") - ips = ip_range.split("-") - if len(ips) != 2: - raise ValidationError(f"Invalid IP range {ip_range}") - validate_ip(ips[0]) - validate_ip(ips[1]) - except IndexError: + ip_range = ip_range.replace(" ", "") + ips = ip_range.split("-") + if len(ips) != 2: raise ValidationError(f"Invalid IP range {ip_range}") + validate_ip(ips[0]) + validate_ip(ips[1]) def validate_ip(ip: str):