def test_disk_quota_disabled(tags_to_apply, filename, folder, size, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check that the disk_quota option is disabled correctly. Creates a file that, when compressed, is larger than the configured disk_quota limit and checks that the message about reaching the limit does not appear in the log. Parameters ---------- filename : str Name of the file to be created. folder : str Directory where the files are being created. size : int Size of each file in bytes. tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' to_write = generate_string(size, '0') create_file(REGULAR, folder, filename, content=to_write) check_time_travel(scheduled) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_disk_quota_limit_reached)
def test_max_files_per_second(get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Check that FIM sleeps for one second when the option max_files_per_second is enabled Args: tags_to_apply (set): Run test if matches with a configuration identifier, skip otherwise. get_configuration (fixture): Gets the current configuration of the test. configure_environment (fixture): Configure the environment for the execution of the test. restart_syscheckd (fixture): Restarts syscheck. wait_for_fim_start (fixture): Waits until the first FIM scan is completed. Raises: TimeoutError: If an expected event couldn't be captured. """ # Create the files in an empty folder to check realtime and whodata. for i in range(n_files_to_create): fim.create_file(fim.REGULAR, test_directories[0], f'test_{i}', content='') extra_timeout = n_files_to_create / max_files_per_second scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' fim.check_time_travel(scheduled) try: wazuh_log_monitor.start( timeout=global_parameters.default_timeout + extra_timeout, callback=fim.callback_detect_max_files_per_second) except TimeoutError as e: if get_configuration['metadata']['max_files_per_sec'] == 0: pass else: raise e
def test_max_eps(get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check that max_eps is respected when a big quantity of syscheck events are generated. During the test, a big quantity of files are created and the max number of event occurrences per second is measured to ensure it never exceeds max_eps """ check_apply_test({'max_eps'}, get_configuration['tags']) max_eps = int(get_configuration['metadata']['max_eps']) mode = get_configuration['metadata']['fim_mode'] # Create files to read max_eps files with added events for i in range(int(max_eps) * 5): create_file(REGULAR, testdir1, f'test{i}_{mode}_{max_eps}', content='') check_time_travel(mode == "scheduled") n_results = max_eps * 4 result = wazuh_log_monitor.start( timeout=(n_results / max_eps) * 6, accum_results=n_results, callback=callback_event_message, error_message=f'Received less results than expected ({n_results})' ).result() counter = Counter([date_time for date_time, _ in result]) error_margin = (max_eps * 0.1) for date_time, n_occurrences in counter.items(): assert n_occurrences <= round( max_eps + error_margin ), f'Sent {n_occurrences} but a maximum of {max_eps} was set'
def test_vulnerabilities_report(get_configuration, configure_environment, restart_modulesd, check_cve_db, mock_vulnerability_scan): """ Check if inserted vulnerable packages are reported by vulnerability detector """ vulnerabilities_number = mock_vulnerability_scan["vulnerabilities_number"] if mock_vulnerability_scan['format'] == 'pkg' and mock_vulnerability_scan['version'] == 'Wazuh v4.0': version = mock_vulnerability_scan['version'] wazuh_log_monitor.start( timeout=SCAN_TIMEOUT, update_position=False, callback=vd.make_vuln_callback(fr"Agent .* has an unsupported Wazuh version: '{version}'"), error_message="The expected event 'Agent .* has an unsupported Wazuh version' not found" ) return # Check the vulnerabilities of inserted packages try: for item in nvd_vulnerabilities['vulnerabilities_nvd']: vd.check_vulnerability_scan_event(wazuh_log_monitor, item['package']['name'], item['cve']['cveid']) except TimeoutError: check_time_travel(time_travel=True, interval=timedelta(seconds=300)) for item in nvd_vulnerabilities['vulnerabilities_nvd']: vd.check_vulnerability_scan_event(wazuh_log_monitor, item['package']['name'], item['cve']['cveid']) # Check that the number of NVD vulnerabilities is the expected if mock_vulnerability_scan["format"] != "win": vd.check_detected_vulnerabilities_number(wazuh_log_monitor=wazuh_log_monitor, expected_vulnerabilities_number=vulnerabilities_number, feed_source='NVD', timeout=vd.VULN_DETECTOR_SCAN_TIMEOUT) vd.check_if_modulesd_is_running()
def test_tag_ignore(directory, event_generated, get_configuration, configure_environment, put_env_variables, restart_syscheckd, wait_for_fim_start): """ Test environment variables are ignored """ # Create text files filename = "test" create_file(REGULAR, directory, filename, content="") # Go ahead in time to let syscheck perform a new scan scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' check_time_travel(scheduled, monitor=wazuh_log_monitor) if event_generated: event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert event['data']['type'] == 'added', 'Event type not equal' assert event['data']['path'] == os.path.join( directory, filename), 'Event path not equal' else: while True: ignored_file = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_ignore).result() if ignored_file == os.path.join(directory, filename): break
def test_events_from_existing_files(filename, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Checks if syscheck generates modified alerts for files that exists when starting the agent """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Modify file modify_file_content(testdir1, filename, new_content='Sample content') # Expect modified event check_time_travel(scheduled) modified_event = wazuh_log_monitor.start(timeout=timeout, callback=callback_detect_event).result() assert 'modified' in modified_event['data']['type'] and \ os.path.join(testdir1, filename) in modified_event['data']['path'] # Delete file delete_file(testdir1, filename) # Expect deleted event check_time_travel(scheduled) deleted_event = wazuh_log_monitor.start(timeout=timeout, callback=callback_detect_event).result() assert 'deleted' in deleted_event['data']['type'] and \ os.path.join(testdir1, filename) in deleted_event['data']['path']
def test_new_directory(tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check that a new monitored directory generates events after the next scheduled scan. This test performs the following steps: - Monitor a directory that does not exist. - Create the directory with files inside. Check that this does not produce events in ossec.log. - Move time forward to the next scheduled scan. - Check that now creating files within the directory do generate events. Parameters ---------- tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise """ check_apply_test(tags_to_apply, get_configuration['tags']) if sys.platform != 'win32': # Create the monitored directory with files and check that events are not raised regular_file_cud(directory_str, wazuh_log_monitor, file_list=['file1', 'file2', 'file3'], min_timeout=global_parameters.default_timeout, triggers_event=False) # Travel to the future to start next scheduled scan check_time_travel(True) detect_initial_scan(wazuh_log_monitor) else: os.makedirs(directory_str, exist_ok=True, mode=0o777) time.sleep(1) # Assert that events of new CUD actions are raised after next scheduled scan regular_file_cud(directory_str, wazuh_log_monitor, file_list=['file4', 'file5', 'file6'], min_timeout=global_parameters.default_timeout, triggers_event=True)
def test_events_from_existing_files(filename, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Check if syscheck generates modified alerts for files that exists when starting the agent""" check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' mode = get_configuration['metadata']['fim_mode'] # Modify file modify_file_content(testdir1, filename, new_content='Sample content') # Expect modified event check_time_travel(scheduled, monitor=wazuh_log_monitor) modified_event = wazuh_log_monitor.start( timeout=timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert 'modified' in modified_event['data']['type'] and \ os.path.join(testdir1, filename) in modified_event['data']['path'] validate_event(modified_event, mode=mode) # Delete file delete_file(testdir1, filename) # Expect deleted event check_time_travel(scheduled, monitor=wazuh_log_monitor) deleted_event = wazuh_log_monitor.start( timeout=timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert 'deleted' in deleted_event['data']['type'] and \ os.path.join(testdir1, filename) in deleted_event['data']['path'] validate_event(deleted_event, mode=mode)
def test_report_changes_after_restart(get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if diff directories are removed after disabling report_changes and Wazuh is restarted. """ check_apply_test({'test_delete_after_restart'}, get_configuration['tags']) value_name = 'random_value' folder_path_key1, diff_file_key_1 = calculate_registry_diff_paths( key, sub_key_1, KEY_WOW64_64KEY, value_name) folder_path_key2, diff_file_key_2 = calculate_registry_diff_paths( key, sub_key_1, KEY_WOW64_64KEY, value_name) # Open key key1_h = create_registry(registry_parser[key], sub_key_1, KEY_WOW64_64KEY) key2_h = create_registry(registry_parser[key], sub_key_2, KEY_WOW64_64KEY) # Modify the registry modify_registry_value(key1_h, value_name, REG_SZ, "some_content") modify_registry_value(key2_h, value_name, REG_SZ, "some_content") # Travel to future check_time_travel(True, monitor=wazuh_log_monitor) assert os.path.exists( diff_file_key_1), f'{diff_file_key_1} does not exists' assert os.path.exists( diff_file_key_2), f'{diff_file_key_2} does not exists' reload_new_conf('no', test_regs[0], test_regs[1]) assert not os.path.exists( folder_path_key1), f'{folder_path_key1} does exists' assert not os.path.exists( folder_path_key2), f'{folder_path_key2} does exists'
def test_update_interval(get_configuration, configure_environment, restart_modulesd): """ Check if the provider database update is triggered after the set interval time has passed """ check_apply_test({'test_providers_update_interval'}, get_configuration['tags']) provider = get_configuration['metadata']['provider_name'] interval_update_time = get_configuration['metadata']['interval'] seconds_to_travel = time_to_seconds(interval_update_time) + 5 # Check that the update has not started prematurely with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=8, callback=callback_provider_database_updating) raise AttributeError(f'Unexpected event {provider} database updating') # Travels the time set in the update interval parameter check_time_travel(time_travel=True, interval=timedelta(seconds=seconds_to_travel)) # Check that the feed is downloaded after the set update interval # (10s for vuln thread refresh + 5s to wait callback) wazuh_log_monitor.start( timeout=50, callback=callback_provider_database_updating, error_message= f"Could not find the provider {provider} updating feed log after \ the interval update")
def perform_and_validate_events(func, kwargs): for directory in dir_list: args = [REGULAR, directory, file ] if func.__name__ == 'create_file' else [directory, file] func(*args, **kwargs) time.sleep( 0.01) # This sleep is to let whodata fetching all events check_time_travel(time_travel=scheduled) try: events = log_monitor.start( timeout=timeout, callback=callback_detect_event, accum_results=len(dir_list), error_message= 'Did not receive expected "Sending FIM event: ..." ' 'event').result() time.sleep(1) for ev in events: validate_event(ev) except TimeoutError as e: if len(log_monitor.result()) == 63: pytest.xfail( reason= 'Xfailed due to issue: https://github.com/wazuh/wazuh/issues/4719' ) else: raise e
def test_scan_time(tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if there is a scan at a certain time scan_time option makes sure there is only one scan every 24 hours, at a certain time. """ check_apply_test(tags_to_apply, get_configuration['tags']) # Reformat given time to a readable format since it can be writen in several ways in ossec.conf scan_time = reformat_time(get_configuration['metadata']['scan_time']) current_time = datetime.now() # Calculate how much time we need to travel in time to make sure there hasn't been any scan until it is the given # time time_difference = (scan_time - current_time) if (scan_time - current_time).days == 0 else \ ((scan_time - current_time) + timedelta(days=2)) check_time_travel(time_travel=True, interval=time_difference + timedelta(minutes=-30)) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_end_scan) raise AttributeError(f'Unexpected event {event}') check_time_travel(time_travel=True, interval=timedelta(minutes=31)) wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_end_scan, error_message='Did not receive expected ' '"File integrity monitoring scan ended" event')
def modify_and_assert(file): modify_file_content(testdir1, file, new_content='Sample modification') check_time_travel(scheduled, monitor=wazuh_log_monitor) ev = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event).result() assert 'modified' in ev['data']['type'] and os.path.join(testdir1, file) in ev['data']['path'], \ f"'modified' event not matching for {testdir1} {file}"
def test_duplicate_entries(get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """Check if syscheckd ignores duplicate entries. For instance: - The second entry should prevail over the first one. <directories realtime="yes">/home/user</directories> (IGNORED) <directories whodata="yes">/home/user</directories> OR - Just generate one event. <directories realtime="yes">/home/user,/home/user</directories> """ logger.info('Applying the test configuration') check_apply_test({'ossec_conf_duplicate_simple'}, get_configuration['tags']) file = 'hello' mode2 = get_configuration['metadata']['fim_mode2'] scheduled = mode2 == 'scheduled' mode2 = "real-time" if mode2 == "realtime" else mode2 logger.info(f'Adding file {os.path.join(testdir1, file)}, content: " "') create_file(REGULAR, testdir1, file, content=' ') logger.info(f'Time travel: {scheduled}') check_time_travel(scheduled) logger.info('Checking the event...') event1 = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message=f'Did not receive expected event for file ' f'{os.path.join(testdir1, file)}').result() # Check for a second event event2 = check_event(previous_mode=mode2, previous_event=event1, file=file) assert event2 is None, "Multiple events created"
def test_file_size_default(tags_to_apply, filename, folder, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check that the file_size option with a default value for report_changes is working correctly. Create a file smaller than the default limit and check that the compressed file has been created. If the first part is successful, increase the size of the file and expect the message for file_size limit reached and no compressed file in the queue/diff/local folder. Parameters ---------- filename : str Name of the file to be created. folder : str Directory where the files are being created. tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' size_limit = translate_size('50MB') diff_file_path = make_diff_file_path(folder=folder, filename=filename) # Create file with a smaller size than the configured value to_write = generate_string(int(size_limit / 10), '0') create_file(REGULAR, folder, filename, content=to_write) check_time_travel(scheduled) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected "Sending FIM event: ..." event.' ) if not os.path.exists(diff_file_path): pytest.raises( FileNotFoundError( f"{diff_file_path} not found. It should exist before increasing the size." )) # Increase the size of the file over the configured value to_write = generate_string(size_limit, '0') modify_file_content(folder, filename, new_content=to_write * 3) check_time_travel(scheduled) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_file_size_limit_reached, error_message='Did not receive expected ' '"File ... is too big for configured maximum size to perform diff operation" event.' ) if os.path.exists(diff_file_path): pytest.raises( FileExistsError( f"{diff_file_path} found. It should not exist after incresing the size." ))
def test_disk_quota_values(key, subkey, arch, value_name, tags_to_apply, size, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check that no events are sent when the disk_quota exceeded Parameters ---------- key : str Root key (HKEY_*) subkey : str path of the registry. arch : str Architecture of the registry. value_name : str Name of the value that will be created tags_to_apply : set Run test if match with a configuration identifier, skip otherwise. size : int Size of the content to write in value """ check_apply_test(tags_to_apply, get_configuration['tags']) value_content = generate_string(size, '0') values = {value_name: value_content} _, diff_file = calculate_registry_diff_paths(key, subkey, arch, value_name) def report_changes_validator_no_diff(event): """Validate content_changes attribute exists in the event""" assert event['data'].get( 'content_changes') is None, 'content_changes isn\'t empty' def report_changes_validator_diff(event): """Validate content_changes attribute exists in the event""" assert os.path.exists(diff_file), '{diff_file} does not exist' assert event['data'].get( 'content_changes') is not None, 'content_changes is empty' if size > size_limit_configured: callback_test = report_changes_validator_no_diff else: callback_test = report_changes_validator_diff create_registry(registry_parser[key], subkey, arch) registry_value_cud( key, subkey, wazuh_log_monitor, arch=arch, value_list=values, time_travel=get_configuration['metadata']['fim_mode'] == 'scheduled', min_timeout=global_parameters.default_timeout, triggers_event=True, validators_after_update=[callback_test]) delete_registry(registry_parser[key], subkey, arch) check_time_travel(True, monitor=wazuh_log_monitor)
def test_skip_proc(get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """Check if syscheckd skips /proc when setting 'skip_proc="yes"'.""" check_apply_test({'skip_proc'}, get_configuration['tags']) trigger = get_configuration['metadata']['skip'] == 'no' if trigger: proc = subprocess.Popen([ "python3", f"{os.path.dirname(os.path.abspath(__file__))}/data/proc.py" ]) # Change configuration, monitoring the PID path in /proc # Monitor only /proc/PID to expect only these events. Otherwise, it will fail due to Timeouts since # integrity scans will take too long new_conf = change_conf(f'/proc/{proc.pid}') new_ossec_conf = [] # Get new skip_proc configuration for conf in new_conf: if conf['metadata']['skip'] == 'no' and conf['tags'] == [ 'skip_proc' ]: new_ossec_conf = set_section_wazuh_conf(conf.get('sections')) restart_wazuh_with_new_conf(new_ossec_conf) truncate_file(LOG_FILE_PATH) proc_monitor = FileMonitor(LOG_FILE_PATH) detect_initial_scan(proc_monitor) # Do not expect any 'Sending event' with pytest.raises(TimeoutError): proc_monitor.start( timeout=3, callback=callback_detect_event, error_message= 'Did not receive expected "Sending FIM event: ..." event') check_time_travel(time_travel=True, monitor=wazuh_log_monitor) found_event = False while not found_event: event = proc_monitor.start( timeout=5, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() if f'/proc/{proc.pid}/' in event['data'].get('path'): found_event = True # Kill the process subprocess.Popen(["kill", "-9", str(proc.pid)]) else: with pytest.raises(TimeoutError): event = wazuh_log_monitor.start( timeout=3, callback=callback_detect_integrity_state) raise AttributeError(f'Unexpected event {event}')
def test_delete_folder(folder, file_list, filetype, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if syscheckd detects 'deleted' events from the files contained in a folder that is being deleted. If we are monitoring /testdir and we have r1, r2, r3 withing /testdir, if we delete /testdir, we must see 3 events of the type 'deleted'. One for each one of the regular files. Parameters ---------- folder : str Directory where the files will be created. file_list : list Names of the files. filetype : str Type of the files that will be created. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' mode = get_configuration['metadata']['fim_mode'] # Create files inside subdir folder for file in file_list: create_file(filetype, folder, file, content='') check_time_travel(scheduled, monitor=wazuh_log_monitor) events = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=len(file_list), error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() for ev in events: validate_event(ev, mode=mode) # Remove folder shutil.rmtree(folder, ignore_errors=True) check_time_travel(scheduled, monitor=wazuh_log_monitor) # Expect deleted events event_list = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event', accum_results=len(file_list)).result() path_list = set([event['data']['path'] for event in event_list]) counter_type = Counter([event['data']['type'] for event in event_list]) for ev in events: validate_event(ev, mode=mode) assert counter_type['deleted'] == len(file_list), f'Number of "deleted" events should be {len(file_list)}' for file in file_list: assert os.path.join(folder, file) in path_list, f'File {file} not found within the events'
def modify_and_check_events(f1, f2, text): """ Modify the content of 2 given files. We assume the first one is being monitored and the other one is not. We expect a 'modified' event for the first one and a timeout for the second one. """ modify_file_content(f1, file1, text) modify_file_content(f2, file1, text) check_time_travel(scheduled) modify = wazuh_log_monitor.start( timeout=3, callback=callback_detect_event).result() assert 'modified' in modify['data']['type'] and f1 in modify['data']['path'], \ f"'modified' event not matching for {file1}" with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=3, callback=callback_detect_event)
def wait_for_event(fim_mode): """Wait for the event to be scanned. Parameters ---------- fim_mode : str FIM mode (scheduled, realtime, whodata) """ check_time_travel(time_travel=fim_mode == 'scheduled', monitor=wazuh_log_monitor) # Wait until event is detected wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected "Sending FIM event: ..." event')
def test_symbolic_revert_symlink(tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check if syscheck detects new targets properly CHECK: Having a symbolic link pointing to a file/folder, change its target to a folder. Check that the old file is not being monitored anymore and the new folder is. Revert the target change and ensure the file is being monitored and the folder is not. """ def modify_and_assert(file): modify_file_content(testdir1, file, new_content='Sample modification') check_time_travel(scheduled, monitor=wazuh_log_monitor) ev = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event).result() assert 'modified' in ev['data']['type'] and os.path.join(testdir1, file) in ev['data']['path'], \ f"'modified' event not matching for {testdir1} {file}" check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' whodata = get_configuration['metadata']['fim_mode'] == 'whodata' file1 = 'regular1' file2 = 'regular2' # Don't expect an event since it is not being monitored yet modify_file_content(testdir1, file2, new_content='Sample modification') check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}') # Change the target to the folder and now expect an event modify_symlink(testdir1, os.path.join(testdir_link, 'symlink')) wait_for_symlink_check(wazuh_log_monitor) wait_for_audit(whodata, wazuh_log_monitor) modify_and_assert(file2) # Modify symlink target, wait for sym_check to update it modify_symlink(os.path.join(testdir1, file1), os.path.join(testdir_link, 'symlink')) wait_for_symlink_check(wazuh_log_monitor) modify_file_content(testdir1, file2, new_content='Sample modification2') check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}') modify_and_assert(file1)
def check_restrict(directory, trigger, check_list, file_list, timeout, scheduled): """Standard restrict attribute test""" create_file(REGULAR, directory, file_list[0], content='') check_time_travel(scheduled, monitor=wazuh_log_monitor) while True: ignored_file = wazuh_log_monitor.start( timeout=timeout, callback=callback_restricted, error_message=f'TimeoutError was raised because a single ' f'"ignoring file {file_list[0]} due to restriction ..." ' f'was expected for {file_list[0]} but was not detected.').result() if ignored_file == os.path.join(directory, file_list[0]): break
def test_check_all_no(path, checkers, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Test the functionality of `check_all` option when set to no. When setting `check_all` to no, only 'type' and 'checksum' attributes should appear in every event. This will avoid any modification event. Parameters ---------- path : str Directory where the file is being created and monitored. checkers : dict Check options to be used. """ check_apply_test({'test_check_all_no'}, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Create regular file and dont expect any check file = 'regular' create_file(REGULAR, path, file) check_time_travel(scheduled, monitor=wazuh_log_monitor) create_event = wazuh_log_monitor.start( callback=callback_detect_event, timeout=15, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert create_event['data']['type'] == 'added' assert list( create_event['data']['attributes'].keys()) == ['type', 'checksum'] # Delete regular file and dont expect any check. Since it is not using any check, modification events will not # be triggered modify_file(path, file, 'Sample modification') with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(callback=callback_detect_event, timeout=5) raise AttributeError(f'Unexpected event {event}') delete_file(path, file) check_time_travel(scheduled, monitor=wazuh_log_monitor) delete_event = wazuh_log_monitor.start( callback=callback_detect_event, timeout=15, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert delete_event['data'][ 'type'] == 'deleted', f'Current value is {delete_event["data"]["type"]}' assert list(delete_event['data']['attributes'].keys()) == ['type', 'checksum'], \ f'Current value is {list(delete_event["data"]["attributes"].keys())}'
def test_ignore_works_over_restrict(folder, filename, triggers_event, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """Check if the ignore tag prevails over the restrict one when using both in the same directory. This test is intended to be used with valid configurations files. Each execution of this test will configure the environment properly, restart the service and wait for the initial scan. Parameters ---------- folder : str Directory where the file is being created filename : str Name of the file to be created triggers_event : bool True if an event must be generated, False otherwise tags_to_apply : set Run test if it matches with a configuration identifier, skip otherwise """ logger.info('Applying the test configuration') check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Create file that must be ignored logger.info(f'Adding file {os.path.join(testdir1, filename)}, content: ""') create_file(REGULAR, folder, filename, content='') # Go ahead in time to let syscheck perform a new scan if mode is scheduled logger.info(f'Time travel: {scheduled}') check_time_travel(scheduled, monitor=wazuh_log_monitor) if triggers_event: logger.info('Checking the event...') event = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message=f'Did not receive expected "Sending FIM event" ' f'event for file {os.path.join(testdir1, filename)}').result() assert event['data']['type'] == 'added', 'Event type not equal' assert event['data']['path'] == os.path.join(folder, filename), 'Event path not equal' else: while True: ignored_file = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_ignore, error_message=f'Did not receive expected ' f'"Ignoring ... due to ..." event for file ' f'{os.path.join(testdir1, filename)}').result() if ignored_file == os.path.join(folder, filename): break
def test_file_size_default(key, subkey, arch, value_name, tags_to_apply, get_configuration, configure_environment, restart_syscheckd_each_time): """ Check that no events are sent when the disk_quota exceeded Parameters ---------- key : str Root key (HKEY_*) subkey : str path of the registry. arch : str Architecture of the registry. value_name : str Name of the value that will be created tags_to_apply : set Run test if match with a configuration identifier, skip otherwise. size : int Size of the content to write in value """ check_apply_test(tags_to_apply, get_configuration['tags']) mode = get_configuration['metadata']['fim_mode'] file_size_values = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_diff_size_limit_value, accum_results=3, error_message='Did not receive expected ' '"Maximum file size limit to generate diff information ' 'configured to \'... KB\'..." event').result() for value in file_size_values: if value: assert value == str(DEFAULT_SIZE), 'Wrong value for file_size' else: raise AssertionError('Wrong value for file_size') key_h = create_registry(registry_parser[key], subkey, arch) modify_registry_value(key_h, "some_value", REG_SZ, "some content") check_time_travel(True, monitor=wazuh_log_monitor) events = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=2, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() for ev in events: validate_registry_value_event(ev, mode=mode)
def test_create_file_scheduled(folder, name, filetype, content, checkers, tags_to_apply, encoding, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check if a special or regular file creation is detected by syscheck using scheduled monitoring Regular files must be monitored. Special files must not. Parameters ---------- folder : str Name of the monitored folder. name : str Name of the file. filetype : str Type of the file. content : str Content of the file. checkers : set Checks that will compared to the ones from the event. """ check_apply_test(tags_to_apply, get_configuration['tags']) # Create files if encoding is not None: name = name.encode(encoding) folder = folder.encode(encoding) create_file(filetype, folder, name, content=content) # Go ahead in time to let syscheck perform a new scan check_time_travel(True, monitor=wazuh_log_monitor) if filetype == REGULAR: # Wait until event is detected event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, encoding=encoding, error_message= 'Did not receive expected "Sending FIM event: ..." event').result( ) validate_event(event, checkers) else: with pytest.raises(TimeoutError): event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event) raise AttributeError(f'Unexpected event {event}')
def test_scan_day_and_time(tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if there is a scan in a certain day and time This test must check both scan params. """ check_apply_test(tags_to_apply, get_configuration['tags']) day_of_week = {'monday': 0, 'tuesday': 1, 'wednesday': 2, 'thursday': 3, 'friday': 4, 'saturday': 5, 'sunday': 6 } current_day = datetime.now() scan_day = day_of_week[get_configuration['metadata']['scan_day']] scan_time = reformat_time(get_configuration['metadata']['scan_time']) day_diff = scan_day - current_day.weekday() scan_today = False if day_diff < 0: day_diff %= 7 elif day_diff == 0: scan_today = True scan_time = replace_date(scan_time, day_diff) if scan_today: if (scan_time - current_day).days == 0: check_time_travel(time_travel=True, interval=scan_time - current_day + timedelta(minutes=1)) wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_end_scan, error_message='Did not receive expected ' '"File integrity monitoring scan ended" event') return else: day_diff = 7 if day_diff > 1: check_time_travel(time_travel=True, interval=timedelta(days=day_diff - 1)) current_day = datetime.now() with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_end_scan) raise AttributeError(f'Unexpected event {event}') check_time_travel(time_travel=True, interval=scan_time - current_day - timedelta(minutes=5)) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_end_scan) raise AttributeError(f'Unexpected event {event}') check_time_travel(time_travel=True, interval=timedelta(minutes=6)) wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_end_scan, error_message='Did not receive expected ' '"File integrity monitoring scan ended" event')
def test_symbolic_delete_target(tags_to_apply, main_folder, aux_folder, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check if syscheck detects events properly when removing a target, have the symlink updated and then recreating the target CHECK: Having a symbolic link pointing to a file/folder, remove that file/folder and check that deleted event is detected. Once symlink_checker runs create the same file. No events should be raised. Wait again for symlink_checker run and modify the file. Modification event must be detected this time. :param main_folder: Directory that is being pointed at or contains the pointed file :param aux_folder: Directory that will be pointed at or will contain the future pointed file * This test is intended to be used with valid configurations files. Each execution of this test will configure the environment properly, restart the service and wait for the initial scan. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' whodata = get_configuration['metadata']['fim_mode'] == 'whodata' file1 = 'regular1' # If symlink is pointing to a directory, we need to add files and expect their 'added' event (only if the file # is being created withing the pointed directory. Then, delete the pointed file or directory if tags_to_apply == {'monitored_dir'}: create_file(REGULAR, main_folder, file1, content='') check_time_travel(scheduled) wazuh_log_monitor.start(timeout=3, callback=callback_detect_event) delete_f(main_folder) else: delete_f(main_folder, file1) check_time_travel(scheduled) delete = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event).result() assert 'deleted' in delete['data']['type'] and file1 in delete['data']['path'], \ f"'deleted' event not matching for {file1}" # If syscheck is monitoring with whodata, wait for audit to reload rules wait_for_audit(whodata, wazuh_log_monitor) wait_for_symlink_check(wazuh_log_monitor) # Restore the target and don't expect any event since symlink hasn't updated the link information create_file(REGULAR, main_folder, file1, content='') check_time_travel(scheduled) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=3, callback=callback_detect_event) wait_for_symlink_check(wazuh_log_monitor) wait_for_audit(whodata, wazuh_log_monitor) # Modify the files and expect events since symcheck has updated now modify_file_content(main_folder, file1, 'Sample modification') check_time_travel(scheduled) modify = wazuh_log_monitor.start(timeout=3, callback=callback_detect_event).result() assert 'modified' in modify['data']['type'] and file1 in modify['data']['path'], \ f"'modified' event not matching for {file1}"
def test_entries_match_key_count(get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if FIM entries match the entries count """ entries = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_registry_count_entries, error_message='Did not receive expected ' '"Fim inode entries: ..., path count: ..." event').result() check_time_travel(True, monitor=wazuh_log_monitor) if entries: assert entries == '4', 'Wrong number of entries' else: raise AssertionError('Wrong number of entries')
def perform_and_validate_events(func, kwargs): for directory in dir_list: args = [REGULAR, directory, file] if func.__name__ == 'create_file' else [directory, file] func(*args, **kwargs) if whodata: time.sleep(0.05) # This sleep is to let whodata fetching all events check_time_travel(time_travel=scheduled) events = log_monitor.start(timeout=timeout, callback=callback_detect_event, accum_results=n_results, error_message='Did not receive expected "Sending FIM event: ..." event').result() time.sleep(1) for ev in events: validate_event(ev)