From 4459e74662a15d3e69ba8ece3ee41319d3007598 Mon Sep 17 00:00:00 2001 From: mauromalara Date: Tue, 25 Jul 2023 00:19:24 +0000 Subject: [PATCH 1/3] fix(#4336): fix flaky test. --- .../test_execd/conftest.py | 11 ++++++ .../test_execd/test_execd_firewall_drop.py | 39 ++++--------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/tests/integration/test_active_response/test_execd/conftest.py b/tests/integration/test_active_response/test_execd/conftest.py index a3f86a5404..3708fe1fc6 100644 --- a/tests/integration/test_active_response/test_execd/conftest.py +++ b/tests/integration/test_active_response/test_execd/conftest.py @@ -2,6 +2,7 @@ import platform import pytest +import wazuh_testing.execd as execd from wazuh_testing.tools import WAZUH_PATH, get_version @@ -41,3 +42,13 @@ def test_version(): """Validate Wazuh version.""" if get_version() < "v4.2.0": raise AssertionError("The version of the agent is < 4.2.0") + + +@pytest.fixture +def truncate_ar_log(): + """Truncate the logs related with Active Response.""" + execd.clean_logs() + + yield + + execd.clean_logs() diff --git a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py index f8ef5458ee..e03cf271ee 100644 --- a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py +++ b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py @@ -174,20 +174,6 @@ def get_configuration(request): yield request.param -def validate_ar_message(message, command_id): - """Verify that Active Response JSON messages have a "command" field and that it is valid. - - Args: - message (str): Serialized JSON message. - command_id (int): Integer with command ID. - """ - command = 'add' if command_id == 0 else 'delete' - - json_alert = json.loads(message) # Alert in JSON - assert json_alert['command'], 'Missing command in JSON message' - assert json_alert['command'] == command, 'Invalid command in JSON message' - - def wait_message_line(line): """Callback function to wait for Active Response JSON message. @@ -228,7 +214,7 @@ def build_message(metadata, expected): def test_execd_firewall_drop(set_debug_mode, get_configuration, test_version, configure_environment, - remove_ip_from_iptables, start_agent, set_ar_conf_mode): + remove_ip_from_iptables, truncate_ar_log, start_agent, set_ar_conf_mode): ''' description: Check if 'firewall-drop' command of 'active response' is executed correctly. For this purpose, a simulated agent is used and the 'active response' @@ -288,28 +274,17 @@ def test_execd_firewall_drop(set_debug_mode, get_configuration, test_version, co ossec_log_monitor.start(timeout=60, callback=execd.wait_received_message_line) # Checking AR in active-response logs - ar_log_monitor.start(timeout=60, callback=execd.wait_start_message_line) + ar_log_monitor.start(timeout=10, callback=execd.wait_start_message_line) if expected['success']: for command_id in range(2): - ar_log_monitor.start(timeout=60, callback=wait_message_line) + ar_log_monitor.start(timeout=10, callback=wait_message_line) last_log = ar_log_monitor.result() - validate_ar_message(last_log, command_id) - - ar_log_monitor.start(timeout=60, callback=execd.wait_ended_message_line) - - # Checking if the IP was added/removed in iptables - iptables_file = os.popen('iptables -L') - flag = False - for iptables_line in iptables_file: - if metadata['ip'] in iptables_line: - flag = True - - if not flag and command_id == 0: - raise AssertionError("IP was not added to iptable") - elif flag and command_id == 1: - raise AssertionError("IP was not deleted from iptable") + ar_log_monitor.start(timeout=10, callback=execd.wait_ended_message_line) + # Restart file monitoring + ar_log_monitor = FileMonitor(execd.AR_LOG_FILE_PATH) + # Default timeout of AR after command time.sleep(5) else: ar_log_monitor.start(timeout=60, callback=wait_invalid_input_message_line) From 8835735e65d7bc5e38c989164309cc5845acc433 Mon Sep 17 00:00:00 2001 From: mauromalara Date: Tue, 25 Jul 2023 01:10:43 +0000 Subject: [PATCH 2/3] style(#4336): add missing line --- .../test_active_response/test_execd/test_execd_firewall_drop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py index e03cf271ee..1696b6e8e7 100644 --- a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py +++ b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py @@ -140,6 +140,7 @@ def start_agent(request, get_configuration): remoted_simulator.stop() authd_simulator.shutdown() + @pytest.fixture(scope="function") def remove_ip_from_iptables(request, get_configuration): """Remove the testing IP address from `iptables` if it exists. From cc0f2b2ddd05e6d898b23e3e5436e04a2c5d2927 Mon Sep 17 00:00:00 2001 From: mauromalara Date: Wed, 2 Aug 2023 13:41:01 +0000 Subject: [PATCH 3/3] fix(#4336): fix test logic --- deps/wazuh_testing/wazuh_testing/execd.py | 31 ++++++++++++-- .../test_execd/test_execd_firewall_drop.py | 40 +++++++++---------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/deps/wazuh_testing/wazuh_testing/execd.py b/deps/wazuh_testing/wazuh_testing/execd.py index 9ce8293dc1..aead4315a7 100644 --- a/deps/wazuh_testing/wazuh_testing/execd.py +++ b/deps/wazuh_testing/wazuh_testing/execd.py @@ -1,5 +1,6 @@ import os import platform +import re from wazuh_testing.tools import LOG_FILE_PATH, WAZUH_PATH from wazuh_testing.tools.file import truncate_file @@ -16,14 +17,38 @@ def clean_logs(): def wait_ended_message_line(line): """Callback function to wait for the Ended Active Response message.""" - return True if "Ended" in line else None + regex = r'.*active-response\/bin\/\S+: Ended$' + match = re.match(regex, line) + + return None if not match else line def wait_received_message_line(line): """Callback function to wait for the Received Active Response message.""" - return True if "DEBUG: Received message: " in line else None + regex = r'.*wazuh-execd.+ExecdStart\(\): DEBUG: Received message: \S+' + match = re.match(regex, line) + + return None if not match else line def wait_start_message_line(line): """Callback function to wait for the Starting Active Response message.""" - return True if "Starting" in line else None + regex = r'.*active-response\/bin\/\S+: Starting$' + match = re.match(regex, line) + + return None if not match else line + + +def wait_firewall_drop_msg(line): + """Callback function to wait for a JSON message with the AR command. + + Args: + line (str): String containing message. + + Returns: + match.group(1): First capturing group which is the JSON message. + """ + regex = r'.*active-response\/bin\/firewall-drop: (.+)' + match = re.match(regex, line) + + return None if not match else match.group(1) diff --git a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py index 1696b6e8e7..92540f0265 100644 --- a/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py +++ b/tests/integration/test_active_response/test_execd/test_execd_firewall_drop.py @@ -175,17 +175,6 @@ def get_configuration(request): yield request.param -def wait_message_line(line): - """Callback function to wait for Active Response JSON message. - - Args: - line (str): String containing message. - """ - if "{\"version\"" in line: - return line.split("active-response/bin/firewall-drop: ", 1)[1] - return None - - def wait_invalid_input_message_line(line): """Callback function to wait for error message. @@ -268,23 +257,34 @@ def test_execd_firewall_drop(set_debug_mode, get_configuration, test_version, co ''' metadata = get_configuration['metadata'] expected = metadata['results'] + expected_commands = ('add', 'delete') ossec_log_monitor = FileMonitor(LOG_FILE_PATH) ar_log_monitor = FileMonitor(execd.AR_LOG_FILE_PATH) # Checking AR in ossec logs - ossec_log_monitor.start(timeout=60, callback=execd.wait_received_message_line) - - # Checking AR in active-response logs - ar_log_monitor.start(timeout=10, callback=execd.wait_start_message_line) + monitor_result = ossec_log_monitor.start(timeout=5, callback=execd.wait_received_message_line).result() + assert monitor_result is not None, 'The expected message was not found in the logs. ' \ + 'The AR command was not received' if expected['success']: - for command_id in range(2): - ar_log_monitor.start(timeout=10, callback=wait_message_line) - last_log = ar_log_monitor.result() + for expected_command in expected_commands: + monitor_result = ar_log_monitor.start(timeout=10, callback=execd.wait_start_message_line).result() + assert monitor_result is not None, 'The expected message was not found in the logs. ' \ + 'The AR was not triggered.' + + monitor_result = ar_log_monitor.start(timeout=10, callback=execd.wait_firewall_drop_msg).result() + assert monitor_result is not None, 'The expected message was not found in the logs. ' \ + 'The AR command was not run.' + + ar_log_msg = json.loads(monitor_result) + + assert ar_log_msg['command'], 'Missing command in active response log.' + assert ar_log_msg['command'] == expected_command, 'Invalid command in active response log.\n' \ + f"Expected: {expected_command}\n" \ + f"Current: {ar_log_msg}" + ar_log_monitor.start(timeout=10, callback=execd.wait_ended_message_line) - # Restart file monitoring - ar_log_monitor = FileMonitor(execd.AR_LOG_FILE_PATH) # Default timeout of AR after command time.sleep(5) else: