def test_regular_file_changes(sleep, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check if syscheckd detects regular file changes (add, modify, delete) with a very specific delay between every action. Parameters ---------- sleep : float Delay in seconds between every action. """ threshold = 1.5 if sys.platform == 'win32' else 1.25 if sleep < threshold and get_configuration['metadata']['fim_mode'] == 'whodata': pytest.xfail('Xfailing due to whodata threshold.') check_apply_test(tags_to_apply, get_configuration['tags']) file = 'regular' create_file(REGULAR, path=testdir1, name=file, content='') time.sleep(sleep) modify_file(path=testdir1, name=file, new_content='Sample') time.sleep(sleep) delete_file(path=testdir1, name=file) events = wazuh_log_monitor.start(timeout=max(sleep * 3, global_parameters.default_timeout), callback=callback_detect_event, accum_results=3, error_message='Did not receive expected "Sending FIM event: ..." event').result() for ev in events: validate_event(ev)
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 configure_syscheck_environment(time_sleep): # Create every needed directory for n in range(n_directories): t_dir = os.path.join(PREFIX, f'{testdir}{n}') os.makedirs(t_dir, exist_ok=True, mode=0o777) directories_list.append(t_dir) wazuh_log_monitor = FileMonitor(LOG_FILE_PATH) control_service('restart') logger.debug('Waiting 15 seconds for syscheckd to start.') time.sleep(15) file = 'regular' logger.debug( f'Waiting {str(time_sleep)} seconds. Execute `generate_windows_yaml.py` now.' ) time.sleep(time_sleep) logger.debug('Creating files...') for directory in directories_list: create_file(REGULAR, directory, file, content='') time.sleep(0.01) try: while True: wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) except TimeoutError: pass logger.debug('Modifying files...') for directory in directories_list: modify_file(directory, file, new_content='Modified') time.sleep(0.01) try: while True: wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) except TimeoutError: pass logger.debug('Deleting files...') for directory in directories_list: delete_file(directory, file) time.sleep(0.01) try: while True: wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) except TimeoutError: pass
def test_follow_symbolic_disabled(path, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Check what happens when follow_symbolic_link option is set to "no". Ensure that the monitored symbolic link is considered a regular file and it will not follow its target path. It will only generate events if it changes somehow, not its target (file or directory) Args: path (str): Path of the target file or directory 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 a expected event wasn't triggered. AttributeError: If a unexpected event was captured. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' regular_file = 'regular1' error_msg = 'A "Sending FIM event: ..." event has been detected. No events should be detected at this time.' # If the symlink targets to a directory, create a file in it and ensure no event is raised. if tags_to_apply == {'monitored_dir'}: fim.create_file(fim.REGULAR, path, regular_file) fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=fim.callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg) # Modify the target file and don't expect any events fim.modify_file(path, regular_file, new_content='Modify sample') fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=fim.callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg) # Delete the target file and don't expect any events fim.delete_file(path, regular_file) fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=fim.callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg)
def test_follow_symbolic_disabled(path, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """Check what happens when follow_symbolic_link option is set to "no". Ensure that the monitored symbolic link is considered a regular file and it will not follow its target path. It will only generate events if it changes somehow, not its target (file or directory) Parameters ---------- path : str Path of the target file or directory """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' regular_file = 'regular1' error_msg = 'A "Sending FIM event: ..." event has been detected. No events should be detected at this time.' # If the symlink targets to a directory, create a file in it and ensure no event is raised. if tags_to_apply == {'monitored_dir'}: create_file(REGULAR, path, regular_file) check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg) # Modify the target file and don't expect any events modify_file(path, regular_file, new_content='Modify sample') check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg) # Delete the target file and don't expect any events delete_file(path, regular_file) check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) logger.error(error_msg) raise AttributeError(error_msg)
def test_regular_file_changes(sleep, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Check if syscheckd detects regular file changes (add, modify, delete) with a very specific delay between every action. Parameters ---------- sleep : float Delay in seconds between every action. """ check_apply_test(tags_to_apply, get_configuration['tags']) file = 'regular' create_file(REGULAR, path=testdir1, name=file, content='') time.sleep(sleep) modify_file(path=testdir1, name=file, new_content='Sample') time.sleep(sleep) delete_file(path=testdir1, name=file) try: events = wazuh_log_monitor.start( timeout=max(sleep * 3, global_parameters.default_timeout), callback=callback_detect_event, accum_results=3, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() for ev in events: validate_event(ev) except TimeoutError as e: if get_configuration['metadata']['fim_mode'] == 'whodata': pytest.xfail( reason= 'Xfailing due to issue: https://github.com/wazuh/wazuh/issues/4710' ) else: raise e
def test_symbolic_monitor_directory_with_symlink( monitored_dir, non_monitored_dir1, non_monitored_dir2, sym_target, tags_to_apply, get_configuration, configure_environment, clean_directories, restart_syscheckd, wait_for_initial_scan): """ Check what happens with a symlink and its target when syscheck monitors a directory with a symlink and not the symlink itself. When this happens, the symbolic link is considered a regular file and it will not follow its target path. It will only generate events if it changes somehow, not its target (file or directory) Parameters ---------- monitored_dir : str Monitored directory. non_monitored_dir1 : str Non-monitored directory. non_monitored_dir2 : str Non-monitored directory. """ check_apply_test(tags_to_apply, get_configuration['tags']) name1 = 'regular1' name2 = 'regular2' sl_name = 'symlink' a_path = os.path.join(non_monitored_dir1, name1) b_path = os.path.join( non_monitored_dir1, name2) if sym_target == 'file' else non_monitored_dir2 sl_path = os.path.join(monitored_dir, sl_name) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Create regular files out of the monitored directory and don't expect its event create_file(REGULAR, non_monitored_dir1, name1, content='') create_file(REGULAR, non_monitored_dir1, name2, content='') target = a_path if sym_target == 'file' else non_monitored_dir1 create_file(SYMLINK, monitored_dir, sl_name, target=target) # Create the syslink and expect its event, since it's withing the monitored directory check_time_travel(scheduled, monitor=wazuh_log_monitor) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected "Sending FIM event: ..." event' ) # Modify the target file and don't expect any event modify_file(non_monitored_dir1, name1, new_content='Modify sample') check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}') # Modify the target of the symlink and expect the modify event modify_symlink(target=b_path, path=sl_path) check_time_travel(scheduled, monitor=wazuh_log_monitor) result = 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 'modified' in result['data'][ 'type'], f"No 'modified' event when modifying symlink" # Remove and restore the target file. Don't expect any events delete_file(b_path, name2) create_file(REGULAR, non_monitored_dir1, name2, content='') check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=5, callback=callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}')
def test_symbolic_monitor_directory_with_symlink( monitored_dir, non_monitored_dir1, non_monitored_dir2, sym_target, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Check what happens with a symlink and its target when syscheck monitors a directory with a symlink and not the symlink itself. When this happens, the symbolic link is considered a regular file and it will not follow its target path. It will only generate events if it changes somehow, not its target (file or directory) Args: monitored_dir (str): Monitored directory. non_monitored_dir1 (str): Non-monitored directory. non_monitored_dir2 (str): Non-monitored directory. 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 a expected event wasn't triggered. AttributeError: If a unexpected event was captured. ValueError: If the event's type and path are not the expected. """ check_apply_test(tags_to_apply, get_configuration['tags']) name1 = f'{sym_target}regular1' name2 = f'{sym_target}regular2' sl_name = f'{sym_target}symlink' a_path = os.path.join(non_monitored_dir1, name1) b_path = os.path.join( non_monitored_dir1, name2) if sym_target == 'file' else non_monitored_dir2 sl_path = os.path.join(monitored_dir, sl_name) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Create regular files out of the monitored directory and don't expect its event fim.create_file(fim.REGULAR, non_monitored_dir1, name1, content='') fim.create_file(fim.REGULAR, non_monitored_dir1, name2, content='') target = a_path if sym_target == 'file' else non_monitored_dir1 fim.create_file(fim.SYMLINK, monitored_dir, sl_name, target=target) # Create the syslink and expect its event, since it's withing the monitored directory fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=fim.callback_detect_event, error_message='Did not receive expected "Sending FIM event: ..." event' ) # Modify the target file and don't expect any event fim.modify_file(non_monitored_dir1, name1, new_content='Modify sample') fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=5, callback=fim.callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}') # Modify the target of the symlink and expect the modify event modify_symlink(target=b_path, path=sl_path) fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) result = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=fim.callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() if 'modified' in result['data']['type']: logger.info( "Received modified event. No more events will be expected.") elif 'deleted' in result['data']['type']: logger.info( "Received deleted event. Now an added event will be expected.") result = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=fim.callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() assert 'added' in result['data'][ 'type'], f"The event {result} should be of type 'added'" else: assert False, f"Detected event {result} should be of type 'modified' or 'deleted'" # Remove and restore the target file. Don't expect any events fim.delete_file(b_path, name2) fim.create_file(fim.REGULAR, non_monitored_dir1, name2, content='') fim.check_time_travel(scheduled, monitor=wazuh_log_monitor) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start(timeout=5, callback=fim.callback_detect_event) logger.error(f'Unexpected event {event.result()}') raise AttributeError(f'Unexpected event {event.result()}')
def generate_analysisd_yaml(n_events, modify_events): def parse_events_into_yaml(requests, yaml_file): yaml_result = [] with open(yaml_file, 'a') as y_f: id_ev = 0 for req, event in requests: type_ev = event['data']['type'] stage_ev = type_ev.title() mode = None agent_id = callback_analysisd_agent_id(req) or '000' del event['data']['mode'] del event['data']['type'] if 'tags' in event['data']: del event['data']['tags'] if type_ev == 'added': mode = 'save2' output_ev = json.dumps(event['data']) elif type_ev == 'deleted': mode = 'delete' output_ev = json.dumps(event['data']['path']).replace( '"', '') elif type_ev == 'modified': mode = 'save2' for field in [ 'old_attributes', 'changed_attributes', 'content_changes' ]: if field in event['data']: del event['data'][field] output_ev = json.dumps(event['data']) yaml_result.append({ 'name': f"{stage_ev}{id_ev}", 'test_case': [{ 'input': f"{req}", 'output': f"agent {agent_id} syscheck {mode} {output_ev}", 'stage': f"{stage_ev}" }] }) id_ev += 1 y_f.write(yaml.safe_dump(yaml_result)) def remove_logs(): for root, dirs, files in os.walk(WAZUH_LOGS_PATH): for file in files: os.remove(os.path.join(root, file)) file = 'regular' # Restart syscheckd with the new configuration truncate_file(LOG_FILE_PATH) file_monitor = FileMonitor(LOG_FILE_PATH) control_service('stop') check_daemon_status(running=False) remove_logs() control_service('start', daemon='wazuh-db', debug_mode=True) check_daemon_status(running=True, daemon='wazuh-db') control_service('start', daemon='wazuh-analysisd', debug_mode=True) check_daemon_status(running=True, daemon='wazuh-analysisd') mitm_analysisd = ManInTheMiddle(address=analysis_path, family='AF_UNIX', connection_protocol='UDP') analysis_queue = mitm_analysisd.queue mitm_analysisd.start() control_service('start', daemon='wazuh-syscheckd', debug_mode=True) check_daemon_status(running=True, daemon='wazuh-syscheckd') # Wait for initial scan detect_initial_scan(file_monitor) analysis_monitor = QueueMonitor(analysis_queue) for directory in directories_list: create_file(REGULAR, directory, file, content='') time.sleep(0.01) added = analysis_monitor.start( timeout=max(0.01 * n_events, 10), callback=callback_analysisd_event, accum_results=len(directories_list)).result() logger.debug('"added" alerts collected.') for directory in directories_list: modify_file(directory, file, new_content='Modified') time.sleep(0.01) modified = analysis_monitor.start(timeout=max(0.01 * n_events, 10), callback=callback_analysisd_event, accum_results=modify_events).result() logger.debug('"modified" alerts collected.') for directory in directories_list: delete_file(directory, file) time.sleep(0.01) deleted = analysis_monitor.start( timeout=max(0.01 * len(directories_list), 10), callback=callback_analysisd_event, accum_results=len(directories_list)).result() logger.debug('"deleted" alerts collected.') # Truncate file with open(yaml_file, 'w') as y_f: y_f.write(f'---\n') for ev_list in [added, modified, deleted]: parse_events_into_yaml(ev_list, yaml_file) logger.debug(f'YAML done: "{yaml_file}"') return mitm_analysisd