Skip to content

Commit

Permalink
Merge pull request #1466 from wazuh/1465-macos-filestatus-IT-T0
Browse files Browse the repository at this point in the history
IT for macos file_status
  • Loading branch information
Rebits authored Oct 15, 2021
2 parents 580d59e + 3b1e165 commit e5825cc
Show file tree
Hide file tree
Showing 18 changed files with 629 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Python cache
__pycache__
.pytest_cache/
.pytest_cache

venv
wazuh_testing.egg-info
Expand Down
69 changes: 68 additions & 1 deletion deps/wazuh_testing/wazuh_testing/logcollector.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
GENERIC_CALLBACK_ERROR_ANALYZING_MACOS = "The expected analyzing macos log has not been produced"
GENERIC_CALLBACK_ERROR_TARGET_SOCKET = "The expected target socket log has not been produced"
GENERIC_CALLBACK_ERROR_TARGET_SOCKET_NOT_FOUND = "The expected target socket not found error has not been produced"
LOG_COLLECTOR_GLOBAL_TIMEOUT = 20
GENERIC_CALLBACK_ERROR_READING_FILE = "The expected invalid content error log has not been produced"
GENERIC_CALLBACK_ERROR = 'The expected error output has not been produced'

LOG_COLLECTOR_GLOBAL_TIMEOUT = 30

DEFAULT_AUTHD_REMOTED_SIMULATOR_CONFIGURATION = {
'ip_address': 'localhost',
'client_keys': os.path.join(WAZUH_PATH, 'etc', 'client.keys'),
Expand Down Expand Up @@ -714,6 +715,37 @@ def callback_invalid_state_interval(interval):
return monitoring.make_callback(pattern=msg, prefix=prefix, escape=True)


def callback_logcollector_started():
"""Check if logcollector started."""
return monitoring.make_callback(pattern='Started', prefix=prefix)


def callback_log_bad_predicate():
"""Check for the macOS ULS bad predicate message."""
return monitoring.make_callback(pattern="Execution error 'log:", prefix=prefix)


def callback_macos_uls_log(expected_message):
"""Callback function to wait for a macOS' ULS log, collected by logcollector."""
return monitoring.make_callback(pattern=expected_message, prefix=prefix, escape=False)


def callback_logcollector_log_stream_log():
"""Check for logcollector's macOS ULS module start message."""
return monitoring.make_callback(pattern='Monitoring macOS logs with:(.+?)log stream',
prefix=prefix, escape=False)


def callback_file_status_macos_key():
"""Check for 'macos' key."""
return monitoring.make_callback(pattern='"macos"', prefix='.*', escape=False)


def callback_log_macos_stream_exit():
"""Check for the macOS ULS log stream exit message."""
return monitoring.make_callback(pattern="macOS 'log stream' process exited, pid:", prefix=prefix)


def wait_statistics_file(timeout=LOG_COLLECTOR_GLOBAL_TIMEOUT):
"""Wait until statistics file is available.
Expand Down Expand Up @@ -789,3 +821,38 @@ def format_macos_message_pattern(process_name, message, type='log', subsystem=No
assert macos_message is not None, 'Wrong type or process name selected for macos message pattern format.'

return macos_message


def compose_macos_log_command(type='', level='', predicate='', is_sierra=False):
"""
This function replicates how the command 'log' will be called from the Wazuh agent given the query parameters
Args:
type (str): < activity | log | trace > Limit streaming to a given event type.
level (str): < default | info | debug > Include events at, and below, the given level.
predicate (str): Filter events using the given predicate.
is_sierra (boolean): True if running on macOS Sierra, False otherwise.
Returns:
string: Full log command composed with the given parameters.
"""

settings_str = ''

if (is_sierra):
settings_str = '/usr/bin/script -q /dev/null '

settings_str += '/usr/bin/log stream --style syslog '

if (type):
for t in type.split(','):
settings_str += '--type ' + t + ' '

if (level):
level = level.replace(' ', '')
settings_str += '--level ' + level + ' '

if(predicate):
settings_str += '--predicate ' + predicate

return settings_str
1 change: 1 addition & 0 deletions deps/wazuh_testing/wazuh_testing/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
ARCHIVES_LOG_FILE_PATH = os.path.join(WAZUH_PATH, 'logs', 'archives', 'archives.log')
AGENT_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-agentd.state')
LOGCOLLECTOR_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-logcollector.state')
LOGCOLLECTOR_FILE_STATUS_PATH = os.path.join(WAZUH_PATH, 'queue', 'logcollector', 'file_status.json')
REMOTE_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-remoted.state')
ANALYSIS_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-analysisd.state')

Expand Down
12 changes: 8 additions & 4 deletions deps/wazuh_testing/wazuh_testing/tools/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import yaml
from wazuh_testing import global_parameters
from wazuh_testing.tools import WAZUH_PATH, GEN_OSSEC, WAZUH_CONF, PREFIX, WAZUH_LOCAL_INTERNAL_OPTIONS
from wazuh_testing import global_parameters, logger


# customize _serialize_xml to avoid lexicographical order in XML attributes
Expand Down Expand Up @@ -620,10 +621,13 @@ def get_local_internal_options_dict():
with open(WAZUH_LOCAL_INTERNAL_OPTIONS, 'r') as local_internal_option_file:
configuration_options = local_internal_option_file.readlines()
for configuration_option in configuration_options:
if not configuration_option.startswith('#') and not configuration_option=='\n':
option_name, option_value = configuration_option.split('=')
local_internal_option_dict[option_name] = option_value

if not configuration_option.startswith('#') and not configuration_option == '\n':
try:
option_name, option_value = configuration_option.split('=')
local_internal_option_dict[option_name] = option_value
except ValueError:
logger.error(f"Invalid local_internal_options value: {configuration_option}")
raise ValueError('Invalid local_internal_option')
return local_internal_option_dict


Expand Down
23 changes: 23 additions & 0 deletions deps/wazuh_testing/wazuh_testing/tools/monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
AGENT_DETECTOR_PREFIX = r'.*wazuh-agent.*'
AUTHD_DETECTOR_PREFIX = r'.*wazuh-authd.*'

DEFAULT_POLL_FILE_TIME = 1
DEFAULT_WAIT_FILE_TIMEOUT = 30

def wazuh_unpack(data, format_: str = "<I"):
"""Unpack data with a given header. Using Wazuh header by default.
Expand Down Expand Up @@ -1016,3 +1019,23 @@ def wait_mtime(path, time_step=5, timeout=-1):
if last_mtime - tic >= timeout:
logger.error(f"{len(open(path, 'r').readlines())} lines within the file.")
raise TimeoutError("Reached timeout.")


def wait_file(path, timeout=DEFAULT_WAIT_FILE_TIMEOUT):
"""Wait until a file, defined by its path, is available.
Args:
path (str): Absolute path to a file.
timeout (int): Maximum time to wait for a file to be available, in seconds.
Raises:
FileNotFoundError: If the file is not available within the timeout defined interval of time.
"""
for _ in range(timeout):
if os.path.isfile(path):
break
else:
time.sleep(DEFAULT_POLL_FILE_TIME)

if not os.path.isfile(path):
raise FileNotFoundError
9 changes: 9 additions & 0 deletions docs/tests/integration/test_logcollector/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ produced while the agent was stopped.

For each location and exclude option specified in the configuration file, check if `logcollector` is analyzing or excluding the required files.

#### Test macos

For the macos log format, test the basic operation:
- Collect logs from `log stream`
- Collect logs from `log show` (only future events)
- Correctly filters/generates the predicate of `log`.
- Updates and correctly generates `file_status.json` file
- Detects invalid configurations in the localfile

### Tier 1
#### Test location custom sockets

Expand Down
16 changes: 12 additions & 4 deletions docs/tests/integration/test_logcollector/test_macos/index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Test macOS

## Overview
## Overview

Wazuh macOS agent allows gathering unified logging system events. These tests ensure logcollector works correctly with
Wazuh macOS agent allows gathering unified logging system events. These tests ensure logcollector works correctly with
this kind of log format. Also, these tests check that every option available for this log format work as expected.

## Objective
Expand All @@ -13,11 +13,19 @@ Confirm that logcollector works correctly for unified logging system events in m

|Tier | Total | Time spent |
| :--:| :--: | :--: |
| 0 | 4 | 3m40s |

| 0 | 4 | 5m20s |

## List of tests

- **[Test macOS file status basic](test_macos_file_status_basic.md)**: Checks if `wazuh-logcollector` correctly
generates the `file_status.json` file used by `only future events`.

- **[Test macOS file status predicate](test_macos_file_status_predicate.md)**: Checks that `wazuh-logcollector` does not
store "macos"-formatted localfile data in `file_status.json`, since its predicate is erroneous.

- **[Test macOS file status when no macos](test_macos_file_status_when_no_macos.md)**: Checks that `wazuh-logcollector`
does not store and removes, if exists, previous "macos"-formatted localfile data in the file_status.json

- **[Test macOS format basic](test_macos_format_basic.md)**: Check if `wazuh-logcollector` correctly gather generated
unified logging system events.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Test macOS - File status basic

## Overview

Checks if `wazuh-logcollector` correctly generates the `file_status.json` file used by `only future events`.

## Objective

- To confirm that the Wazuh macOS agent generates a valid status file (`file_status.json`)
that can be used at the next startup of Wazuh-Logcollector

## General info

|Tier | Total | Time spent |
| :--:| :--: | :--: |
| 0 | 2 | 105s |

## Expected behavior

- Fail if `wazuh-logcollector` does not gather generated unified logging system event or does not send it to the manager
- Fail if `wazuh-logcollector` does not create the status file `status_file.json`
- Fail if `wazuh-logcollector` saves incorrectly formatted, or invalid data in the status file `status_file.json`.

## Code documentation

::: tests.integration.test_logcollector.test_macos.test_macos_file_status_basic
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Test macOS - File status predicate

## Overview

Checks that `wazuh-logcollector` does not store "macos"-formatted localfile data in `file_status.json`,
since its predicate is erroneous. Respective errors should be logged in the `ossec.log` file.

## Objective

- Confirm that, even when the Wazuh macOS agent generates a valid status file (`file_status.json`), there is no stored
data related to the "macos"-formatted localfile, even when a configuration block (with an erroneous predicate) is set.

## General info

|Tier | Total | Time spent |
| :--:| :--: | :--: |
| 0 | 2 | 30s |

## Expected behavior

- Fail if `wazuh-logcollector` does not create the status file `status_file.json`
- Fail if `wazuh-logcollector` stores "macos"-formatted localfile data in the status file `status_file.json`.
- Fail if `wazuh-logcollector` does not log the errors related to `log stream` in the log file `ossec.log`.

## Code documentation

::: tests.integration.test_logcollector.test_macos.test_macos_file_status_predicate
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Test macOS - File status when no macos
## Overview

Checks that `wazuh-logcollector` does not store and removes, if exists, previous
"macos"-formatted localfile data in the file_status.json

## Objective

- Confirm that, given a file_status.json that contains a valid combination of
"settings" and "timestamp" of "macos", when starting an agent that has no
"macos" localfile configured on its ossec.conf file, it should happen that, when
file_status.json is updated after a certain time, no "macos" status should
remain stored on the status file.

## General info

|Tier | Total | Time spent |
| :--:| :--: | :--: |
| 0 | 1 | 50s |


## Expected behavior

- Fail if `wazuh-logcollector` stores "macos"-formatted localfile data in the status file `status_file.json`.


## Code documentation

::: tests.integration.test_logcollector.test_macos.test_macos_file_status_when_no_macos
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@ nav:
- Test location custom sockets: tests/integration/test_logcollector/test_location_custom_sockets/test_location_custom_sockets.md
- Test macos:
- Overview: tests/integration/test_logcollector/test_macos/index.md
- Test macos file status basic: tests/integration/test_logcollector/test_macos/test_macos_file_status_basic.md
- Test macos file status predicate: tests/integration/test_logcollector/test_macos/test_macos_file_status_predicate.md
- Test macos file when no macos: tests/integration/test_logcollector/test_macos/test_macos_file_status_when_no_macos.md
- Test macos format basic: tests/integration/test_logcollector/test_macos/test_macos_format_basic.md
- Test only future events macos format: tests/integration/test_logcollector/test_macos/test_macos_format_only_future_events.md
- Test query macos format: tests/integration/test_logcollector/test_macos/test_macos_format_query.md
Expand Down
40 changes: 39 additions & 1 deletion tests/integration/test_logcollector/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
import pytest
import wazuh_testing.tools.configuration as conf
from wazuh_testing.logcollector import LOGCOLLECTOR_DEFAULT_LOCAL_INTERNAL_OPTIONS
from wazuh_testing.tools import LOG_FILE_PATH
from wazuh_testing.tools import LOG_FILE_PATH, LOGCOLLECTOR_FILE_STATUS_PATH, WAZUH_LOCAL_INTERNAL_OPTIONS
from wazuh_testing.tools.file import truncate_file
from wazuh_testing.tools.monitoring import FileMonitor
from wazuh_testing.tools.services import control_service
from wazuh_testing.tools.remoted_sim import RemotedSimulator
from wazuh_testing.tools.authd_sim import AuthdSimulator
from wazuh_testing.tools import CLIENT_CUSTOM_KEYS_PATH, CLIENT_CUSTOM_CERT_PATH
from os.path import exists
from os import remove

DAEMON_NAME = "wazuh-logcollector"

Expand Down Expand Up @@ -66,3 +68,39 @@ def init_authd_remote_simulator(get_connection_configuration, request):

remoted_simulator.stop()
authd_simulator.shutdown()


@pytest.fixture(scope="package", autouse=True)
def configure_local_internal_options_logcollector():
"""Configure Wazuh with local internal options required for logcollector tests."""
backup_options_lines = conf.get_wazuh_local_internal_options()
backup_options_dict = conf.local_internal_options_to_dict(backup_options_lines)

if backup_options_dict != LOGCOLLECTOR_DEFAULT_LOCAL_INTERNAL_OPTIONS:
conf.add_wazuh_local_internal_options(LOGCOLLECTOR_DEFAULT_LOCAL_INTERNAL_OPTIONS)

control_service('restart')

yield

conf.set_wazuh_local_internal_options(backup_options_lines)

control_service('restart')
else:
yield


@pytest.fixture(scope='function')
def delete_file_status_json():
"""Delete file_status.json from logcollector"""
remove(LOGCOLLECTOR_FILE_STATUS_PATH) if exists(LOGCOLLECTOR_FILE_STATUS_PATH) else None

yield


@pytest.fixture(scope='function')
def truncate_log_file():
"""Truncate the log file (ossec.log)"""
truncate_file(LOG_FILE_PATH)

yield
Loading

0 comments on commit e5825cc

Please sign in to comment.