def test_skip_nfs(modify_inode_mock, directory, tags_to_apply, configure_nfs, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Check if syscheckd skips nfs directories when setting 'skip_nfs="yes"'. This test assumes you have a nfs directory mounted on '/nfs-mount-point'. If you do not have one, use the fixture `configure_nfs`. Parameters ---------- directory : str Directory that will be monitored. """ def custom_callback(filename): def callback(line): match = callback_detect_event(line) if match and filename in match['data']['path']: return match return callback file = str(uuid.uuid1()) check_apply_test(tags_to_apply, get_configuration['tags']) trigger = get_configuration['metadata']['skip'] == 'no' try: regular_file_cud(directory, wazuh_log_monitor, file_list=[file], time_travel=True, min_timeout=3, triggers_event=trigger, callback=custom_callback(file)) finally: delete_file(directory, file)
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_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_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 multiple_dirs_test(dir_list=None, file=None, scheduled=None, log_monitor=None, timeout=1): """Perform a given action for every directory and validate all the events. Parameters ---------- dir_list : list, optional List of created/monitored directories. Default `None` file : str, optional Name of the file to be created. Default `None` scheduled : str, optional Monitoring mode. Default `None` log_monitor : FileMonitor, optional File monitor. Default `None` timeout : int, optional Maximum timeout to raise a TimeoutError. Default `1` """ 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 try: perform_and_validate_events(create_file, {'content': ''}) perform_and_validate_events(modify_file, {'new_content': 'New content'}) perform_and_validate_events(delete_file, {}) finally: for directory in dir_list: delete_file(directory, file)
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 multiple_dirs_test(mode=None, dir_list=None, file=None, scheduled=None, whodata=None, log_monitor=None, timeout=1): """Perform a given action for every directory and validate all the events. Parameters ---------- dir_list : list, optional List of created/monitored directories. Default `None` file : str, optional Name of the file to be created. Default `None` scheduled : str, optional Monitoring mode. Default `None` log_monitor : FileMonitor, optional File monitor. Default `None` timeout : int, optional Maximum timeout to raise a TimeoutError. Default `1` """ if mode == "entries": n_results = len(dir_list) elif mode == "dirs": n_results = 64 # Maximum number of directories monitored in one line 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) try: perform_and_validate_events(create_file, {'content': ''}) perform_and_validate_events(modify_file, {'new_content': 'New content'}) perform_and_validate_events(delete_file, {}) finally: for directory in dir_list: delete_file(directory, file)
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_file_limit_delete_full(folder, file_name, tags_to_apply, get_configuration, configure_environment, restart_syscheckd): """ This test checks a specific case: If in a file (for example test_1) is not inserted in the database and a file ended in 0 (for example test_10) is inserted in the DB, after deleting test_1, the delete alert was raised for test_10. Parameters ---------- folder: path Path to the folder where the test is going to be executed. file_name: base name of the file (in the example above it will be test_) tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) database_state = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_file_limit_capacity, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB 100% full alert." event').result() if database_state: assert database_state == '100', 'Wrong value for full database alert' create_file(REGULAR, testdir1, file_name) sleep(2) delete_file(folder, file_name) with pytest.raises(TimeoutError): event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event).result() assert event is None, 'No events should be detected.' delete_file(folder, f'{file_name}{0}') event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected deleted event').result() assert event['data']['path'] == os.path.join(folder, f'{file_name}{0}')
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, 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 test_hard_link(path_file, path_link, num_links, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """Test the check_inode option when used with Hard links by creating a hard link file inside and outside the monitored 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. :param path_file: The path to the regular file to be created :param path_link: The path to the Hard links to be created :param num_links: Number of hard links to create. All of them will be pointing to the same regular file. :param checkers: Dict with all the check options to be used """ truncate_file(LOG_FILE_PATH) wazuh_log_monitor = FileMonitor(LOG_FILE_PATH) is_scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' regular_file_name = "testregularfile" file_list = [regular_file_name] hardlinks_list = [] try: event_checker = EventChecker(wazuh_log_monitor, path_file, file_list) # Create the regular file create_file(REGULAR, path_file, regular_file_name, content='test content') check_time_travel(is_scheduled) event_checker.fetch_and_check('added', min_timeout=DEFAULT_TIMEOUT) # Create as many links pointing to the regular file as num_links for link in range(0, num_links): hardlinks_list.append("HardLink" + str(link)) create_file(HARDLINK, path_link, "HardLink" + str(link), target=os.path.join(path_file, regular_file_name)) # Try to detect the creation events for all the created links if path_file == path_link: check_time_travel(is_scheduled) event_checker.file_list = hardlinks_list event_checker.fetch_and_check('added', min_timeout=DEFAULT_TIMEOUT) # Update file_list with the links if these were created in the monitored folder event_checker.file_list = file_list + hardlinks_list if path_file == path_link else file_list # Modify the original file and detect the events for the entire file_list modify_file_content(path_file, regular_file_name, new_content="modified testregularfile") check_time_travel(is_scheduled) event_checker.fetch_and_check('modified', min_timeout=DEFAULT_TIMEOUT) # Modify one of the hard links modify_file_content(path_link, "HardLink0", new_content="modified HardLink0") # If the hard link is inside the monitored dir alerts should be triggered for the entire file_list # Scheduled run should ALWAYS detect the modification of the file, even if we are using Real-time or Whodata. check_time_travel(path_file != path_link or is_scheduled) event_checker.fetch_and_check('modified', min_timeout=DEFAULT_TIMEOUT) finally: # Clean up delete_file(path_file, regular_file_name) for link in hardlinks_list: delete_file(path_link, link) check_time_travel(True)
def test_file_limit_capacity_alert(percentage, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Checks that the corresponding alerts appear in schedule mode for different capacity thresholds. Parameters ---------- tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) NUM_FILES = percentage + 1 if percentage == 0: NUM_FILES = 0 if percentage >= 80: # Percentages 80 and 90 for i in range(NUM_FILES): create_file(REGULAR, testdir1, f'test{i}') else: # Database back to normal for i in range(91): delete_file(testdir1, f'test{i}') check_time_travel(True, monitor=wazuh_log_monitor) if percentage >= 80: # Percentages 80 and 90 file_limit_capacity = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_file_limit_capacity, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB ...% full alert." event' ).result() if file_limit_capacity: assert file_limit_capacity == str(percentage), 'Wrong capacity log for DB file_limit' else: raise AssertionError('Wrong capacity log for DB file_limit') else: # Database back to normal event_found = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_file_limit_back_to_normal, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB back to normal alert." event' ).result() assert event_found, 'Event "Sending DB back to normal alert." not found' entries, path_count = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_entries_path_count, error_message='Did not receive expected ' '"Fim inode entries: ..." event' ).result() check_time_travel(True, monitor=wazuh_log_monitor) if sys.platform != 'win32': if entries and path_count: assert entries == str(NUM_FILES) and path_count == str(NUM_FILES), 'Wrong number of inodes and path count' else: raise AssertionError('Wrong number of inodes and path count') else: if entries: assert entries == str(NUM_FILES), 'Wrong number of entries count' else: raise AssertionError('Wrong number of entries count')
def test_move_file(file, file_content, tags_to_apply, source_folder, target_folder, triggers_delete_event, triggers_add_event, get_configuration, configure_environment, restart_syscheckd, wait_for_initial_scan): """ Checks if syscheckd detects 'added' or 'deleted' events when moving a file. :param file str Name of the file to be created :param file_content str Content of the file to be created :param source_folder str Folder to move the file from :param target_folder str Destination folder to move the file to :param triggers_delete_event boolean Expects a 'deleted' event in the source folder :param triggers_add_event boolean Expects a 'added' event in the target folder """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Create file inside folder create_file(REGULAR, source_folder, file, content=file_content) if source_folder in test_directories: check_time_travel(scheduled) wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT, callback=callback_detect_event) # Move file to target directory os.rename(os.path.join(source_folder, file), os.path.join(target_folder, file)) check_time_travel(scheduled) # Monitor expected events events = wazuh_log_monitor.start( timeout=DEFAULT_TIMEOUT, callback=callback_detect_event, accum_results=(triggers_add_event + triggers_delete_event)).result() # Expect deleted events if isinstance(events, list): events_data = [ (event['data']['type'], event['data']['path'], os.path.join(source_folder, file) if event['data']['type'] == 'deleted' else os.path.join(target_folder, file)) for event in events ] assert set([event[0] for event in events_data]) == {'deleted', 'added'} for _, path, expected_path in events_data: assert path == expected_path else: if triggers_delete_event: assert 'deleted' in events['data']['type'] and os.path.join( source_folder, file) in events['data']['path'] else: assert 'added' in events['data']['type'] and os.path.join( target_folder, file) in events['data']['path'] # Remove file delete_file(target_folder, file) if target_folder in test_directories: check_time_travel(scheduled) wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT, callback=callback_detect_event)
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
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 extra_configuration_after_yield(): # Delete file created in the test. delete_file(rules_directory, test_file)
def extra_configuration_before_yield(): # Delete file before running the test if it exists. delete_file(rules_directory, test_file)
def test_move_file(file, file_content, tags_to_apply, source_folder, target_folder, triggers_delete_event, triggers_add_event, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if syscheckd detects 'added' or 'deleted' events when moving a file. Parameters ---------- file : str Name of the file to be created. file_content : str Content of the file to be created. source_folder : str Folder to move the file from. target_folder : str Destination folder to move the file to. triggers_delete_event : bool Expects a 'deleted' event in the `source_folder`. triggers_add_event : bool Expects a 'added' event in the `target_folder`. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' mode = get_configuration['metadata']['fim_mode'] # Create file inside folder create_file(REGULAR, source_folder, file, content=file_content) if source_folder in test_directories: check_time_travel(scheduled, monitor=wazuh_log_monitor) 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() validate_event(event, mode=mode) # Move file to target directory os.rename(os.path.join(source_folder, file), os.path.join(target_folder, file)) check_time_travel(scheduled, monitor=wazuh_log_monitor) # Monitor expected events events = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=(triggers_add_event + triggers_delete_event), error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() # Expect deleted events if isinstance(events, list): events_data = [(event['data']['type'], event['data']['path'], os.path.join(source_folder, file) if event['data']['type'] == 'deleted' else os.path.join( target_folder, file)) for event in events] assert set([event[0] for event in events_data]) == {'deleted', 'added'} for _, path, expected_path in events_data: assert path == expected_path else: if triggers_delete_event: assert 'deleted' in events['data']['type'] and os.path.join(source_folder, file) in events['data']['path'] else: assert 'added' in events['data']['type'] and os.path.join(target_folder, file) in events['data']['path'] events = [events] if not isinstance(events, list) else events for ev in events: validate_event(ev, mode=mode) # Remove file delete_file(target_folder, file) if target_folder in test_directories: check_time_travel(scheduled, monitor=wazuh_log_monitor) 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() validate_event(event, mode=mode)
def extra_configuration_after_yield(): delete_file(testdir1, 'regular') set_local_timezone()