Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix flaky test in AR suite (excecd) #4360

Merged
merged 3 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions deps/wazuh_testing/wazuh_testing/execd.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
11 changes: 11 additions & 0 deletions tests/integration/test_active_response/test_execd/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import platform
import pytest

import wazuh_testing.execd as execd
from wazuh_testing.tools import WAZUH_PATH, get_version


Expand Down Expand Up @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -174,31 +175,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'


damarisg marked this conversation as resolved.
Show resolved Hide resolved
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.

Expand Down Expand Up @@ -228,7 +204,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'
Expand Down Expand Up @@ -281,35 +257,35 @@ 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=60, 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=60, callback=wait_message_line)
last_log = ar_log_monitor.result()
validate_ar_message(last_log, command_id)
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_monitor.start(timeout=60, callback=execd.wait_ended_message_line)
ar_log_msg = json.loads(monitor_result)

# 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
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}"

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)

# Default timeout of AR after command
time.sleep(5)
else:
ar_log_monitor.start(timeout=60, callback=wait_invalid_input_message_line)
Loading