def test_providers_no_os(clean_vuln_tables, get_configuration, configure_environment): """ Check if modulesd downloads the feeds without specifing the os version. """ check_apply_test({'test_providers_no_os'}, get_configuration['tags']) provider_name = get_configuration['metadata']['provider_name'] # Those providers that aren't expected to work without the <os> tag. try: control_service('restart') except ValueError: assert 'error' in get_configuration['metadata'] if 'error' in get_configuration['metadata']: wazuh_log_monitor.start(timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback(r".* \(\d+\): Configuration error at.*", prefix='.*wazuh-modulesd.*'), error_message=f"Error log 'Configuration error at '/var/ossec/etc/ossec.conf'.' " f"not found") else: for os in get_configuration['metadata']['os']: if os != '': os_name = f"{provider_name} {os}" else: os_name = f"JSON {provider_name}" if 'Red Hat' in provider_name else f"{provider_name}" wazuh_log_monitor.start( timeout=80 if 'Red Hat' in os_name else VULN_DETECTOR_GLOBAL_TIMEOUT, callback=make_vuln_callback(f"Starting '{os_name}' database update"), error_message=f"Could not find {os_name} update starting log", )
def test_vulnerabilities_report(get_configuration, configure_environment, restart_modulesd, check_cve_db, mock_agent, mock_vulnerability_scan): """ Check if a missing patch triggers a vulnerability(only windows). """ hotfixes = mock_vulnerability_scan['hotfixes'] dep = vulnerabilities['dependencies'] for cve, item in vulnerabilities['vulnerabilities'].items(): installed, hotfix = is_hotfix_installed(item[0]['patch'], dep, hotfixes) if installed: wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_SCAN_TIMEOUT, update_position=False, callback=vd.make_vuln_callback( f"Agent '{mock_agent}' has installed '{hotfix}' that corrects the vulnerability '{cve}'" ), error_message= f"Could not find the report which says that the patch {hotfix} solves {cve}" ) else: wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_SCAN_TIMEOUT, update_position=False, callback=vd.make_vuln_callback( f"Agent '{mock_agent}' is vulnerable to '{cve}'. Condition: 'KB{hotfix} patch is not installed'" ), error_message= f"Could not find the report which says that the system" + f" is vulnerable to {cve} due to missing {hotfix}") vd.check_if_modulesd_is_running()
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_enabled(get_configuration, configure_environment, restart_modulesd): """ Check if modulesd downloads the feeds from different providers when enabled is set to yes. """ provider_name = get_configuration['metadata']['provider_name'] if get_configuration['metadata']['enabled'] == 'no': with pytest.raises(TimeoutError): wazuh_log_monitor.start( timeout=VULN_DETECTOR_GLOBAL_TIMEOUT, callback=make_vuln_callback("Starting.+database update")) raise AttributeError( f"Unexpected event {provider_name} database updating") else: wazuh_log_monitor.start( timeout=VULN_DETECTOR_GLOBAL_TIMEOUT, callback=make_vuln_callback( f"Starting '{provider_name}' database update"), error_message=f"Could not find {provider_name} update starting log", )
def test_window_version_indexing(get_configuration, configure_environment, restart_modulesd, check_cve_db, mock_system): wazuh_log_monitor.start( timeout=50, update_position=False, callback=vd.make_vuln_callback( rf"The CPE 'o:microsoft:{mock_system['index_name']}:(-|r2|{mock_system['os_release']}):" + rf"({mock_system['os_release']})?:::::{mock_system['architecture']}:' from the agent '.*' was indexed." ), error_message=f"{mock_system['os_name']} was not indexed")
def test_providers(get_configuration, configure_environment, restart_modulesd): """ Check if modulesd downloads the feeds for each os """ check_apply_test({'test_providers_os'}, get_configuration['tags']) provider_name = get_configuration['metadata']['provider_name'] if 'os_warning' in get_configuration['metadata']: wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback( rf".*Invalid option 'os' for .* provider.*", prefix='.*wazuh-modulesd.*'), error_message= f"Warning log 'Invalid option 'os' for '{provider_name}' " f"provider not found") wazuh_log_monitor.start( timeout=VULN_DETECTOR_GLOBAL_TIMEOUT, callback=make_vuln_callback( f"Starting '{provider_name}' database update"), error_message=f"Could not find {provider_name} update starting log", )
def test_ignore_time(get_configuration, configure_environment, restart_modulesd, prepare_agent, custom_callback_vulnerability=vd.make_vuln_callback( callback_string_vulnerability)): """ Check if an alert is not fired during the ignore time interval """ control_service('stop', daemon='wazuh-modulesd') control_service('stop', daemon='wazuh-db') vd.update_last_scan(agent=prepare_agent) control_service('start', daemon='wazuh-db') control_service('start', daemon='wazuh-modulesd') ignore_time = get_configuration['metadata']['ignore_time'] jumps = get_configuration['metadata']['jumps'] seconds_to_travel = time_to_seconds(ignore_time) / jumps # Check for initial alert wazuh_log_monitor.start( timeout=get_configuration['metadata']['timeout'], callback=custom_callback_vulnerability, error_message='Alert did not appear at the start of the test') # Check if alert does not appear during ignore time for _ in range(1, jumps): check_time_travel(time_travel=True, interval=timedelta(seconds=seconds_to_travel)) with pytest.raises(TimeoutError): wazuh_log_monitor.start( timeout=get_configuration['metadata']['timeout'], callback=custom_callback_vulnerability) raise AttributeError( 'Alert appeared before ignore_time was finished') # Travel to the time set in ignore time check_time_travel(time_travel=True, interval=timedelta(seconds=seconds_to_travel)) # Check for final alert wazuh_log_monitor.start( timeout=get_configuration['metadata']['timeout'], callback=custom_callback_vulnerability, error_message='Alert did not appear at the end of the test')
def test_download_feeds(clean_vuln_tables, get_configuration, configure_environment, restart_modulesd): """ Check if modulesd downloads successfully the feeds from different providers and os. Additionaly, check that the updates are applied only when required. Ex: Outdated local database. """ check_apply_test({'test_download_feeds'}, get_configuration['tags']) provider = get_configuration['metadata']['provider_name'] os = get_configuration['metadata']['os'].capitalize() system_log = get_configuration['metadata']['system_log'] download_timeout = get_configuration['metadata']['download_timeout'] callback_system_log = system_log # If provider configuration has <os> tag if os and system_log == provider_info[provider]['system_log']: callback_system_log = f"{system_log} {os}" try: # Check that it starts the feed download wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback( f"Starting '{callback_system_log}' database update"), error_message= f"Could not find the provider {callback_system_log} feed download") if get_configuration['metadata']['provider_name'] == "debian": # Check Debian Security Tracker feed wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback( f"Indexing vulnerabilities from the Debian " f"Security Tracker."), error_message= f"Could not find the Debian Security Tracker feed download") # Check that the feed is downloaded successfully wazuh_log_monitor.start( timeout=download_timeout, callback=vd.make_vuln_callback( f"The update of the '{callback_system_log}' feed \ finished successfully" ), error_message= f"The provider {callback_system_log} download has taken more than \ {download_timeout / 60} minutes" ) # Travels the time set in the update interval parameter check_time_travel(time_travel=True, interval=timedelta(seconds=10)) # Download again the feed. # No updates should be applied since the local copy is up-to-date. if get_configuration['metadata']['provider_name'] == "nvd": # There's no "already up-to-date" message for the nvd. year = get_configuration['metadata']['update_from_year'] with pytest.raises(TimeoutError): vd.check_log_event( wazuh_log_monitor=wazuh_log_monitor, log_event= fr"The feed '{callback_system_log} ({year})' is outdated. Fetching the " fr"last version.", update_position=False, timeout=15, prefix=MODULESD_PREFIX) raise AttributeError( f'Unexpected outdated feed message for the provider {callback_system_log}' ) else: if get_configuration['metadata']['provider_name'] == "arch": pytest.xfail( "The expected message doesn't exist for Arch Linux. Issue: " "https://github.com/wazuh/wazuh/issues/8194") else: wazuh_log_monitor.start( timeout=download_timeout, callback=vd.make_vuln_callback( f"The feed '{callback_system_log}' is in its latest version." ), error_message= f"Could not find the provider {callback_system_log} updated feed " f"log after the interval update") finally: control_service('stop', daemon='wazuh-db') # Clean NVD tables when the download has finished vd.clean_vuln_and_sys_programs_tables() # Wait for cleaning NVD tables sleep(5) control_service('start', daemon='wazuh-db')
'interval': item }]) ids.extend([ f"Redhat_{item}", f"Canonical_{item}", f"Debian_{item}", f"NVD_{item}", f"MSU_{item}", f"Arch_{item}" ]) # Configuration data configurations = load_wazuh_configurations(configurations_path, __name__, params=parameters, metadata=metadata) # Callbacks callback_provider_database_updating = make_vuln_callback( 'Starting .* database update') # Fixtures @pytest.fixture(scope='module', params=configurations, ids=ids) def get_configuration(request): """Get configurations from the module.""" return request.param 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'},
def test_multiple_providers(clean_vuln_tables, get_configuration, configure_environment, restart_modulesd): """ From now on, Redhat and Debian providers use two feeds to fetch vulnerabilities and CVEs' metadata. \<os path=...\> option indicates the local path where the OVAL feed is whereas \<path\> indicates the local path where the another feed is. The same goes for \<os url=...\> and \<url\> options. This test verifies the path/url and multipath/url options work properly according to the configuration and check there are no conflicts when downloading or reading the feeds. """ provider = get_configuration['metadata']['provider'] if provider == 'redhat': os_feed = 'https://www.redhat.com/security/data/oval/v2/RHEL8/rhel-8-including-unpatched.oval.xml.bz2' feed = 'https://access.redhat.com/labs/securitydataapi/cve.json?after=1999-01-01&per_page=1000&page=1' else: os_feed = 'https://www.debian.org/security/oval/oval-definitions-buster.xml' feed = 'https://security-tracker.debian.org/tracker/data/json' try: if get_configuration['sections'][0]['elements'][1]['provider'][ 'elements'][1]['os']['attributes']: if 'path' in get_configuration['tags'][0]: os_feed = get_configuration['metadata']['os_path'] else: os_feed = get_configuration['metadata']['os_url'] except (KeyError, IndexError): pass try: if get_configuration['sections'][0]['elements'][1]['provider'][ 'elements'][2]: path_enable = True if 'path' in get_configuration['tags'][0]: feed = get_configuration['metadata']['path'] else: feed = get_configuration['metadata']['url'] except (KeyError, IndexError): path_enable = False wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback(rf"(Path|Url): ('{os_feed}'|'none').*", prefix='.*wazuh-modulesd.*'), error_message= f"OVAL feed {os_feed} from provider {provider} not correctly assigned") if path_enable and provider == 'redhat': wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback(rf"Multi (path|url): '{feed}'.*", prefix='.*wazuh-modulesd.*'), error_message= f"Feed {feed} from provider {provider} was not correctly assigned") wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback( rf"(Fetching feed from|Downloading) '{os_feed}'.*", prefix='.*wazuh-modulesd.*'), error_message= f"Event 'Fetching or Downloading {os_feed} was not received") wazuh_log_monitor.start( timeout=vd.VULN_DETECTOR_GLOBAL_TIMEOUT, callback=vd.make_vuln_callback( f"((Fetching .* from|Downloading) '{feed}'|.*Trying to " f"download).*", prefix='.*wazuh-modulesd.*'), error_message=f"Event 'Trying to download {feed}' was not received") vd.clean_vuln_and_sys_programs_tables()
custom_nvd_feed_path = os.path.join(test_data_path, 'feeds', vd.CUSTOM_NVD_FEED) wazuh_log_monitor = FileMonitor(LOG_FILE_PATH) scan_timeout = 60 config_wildcards_values = [{'RUN_ON_START': 'yes', 'NVD_JSON_PATH': custom_nvd_feed_path}, {'RUN_ON_START': 'no', 'NVD_JSON_PATH': custom_nvd_feed_path}] config_wildcards_metadata = [{'run_on_start': 'yes'}, {'run_on_start': 'no'}] # Configuration data configurations = load_wazuh_configurations(configurations_path, __name__, params=config_wildcards_values, metadata=config_wildcards_metadata) # Callbacks callback_detect_vulnerability_scan_started = make_vuln_callback('Starting vulnerability scan') callback_detect_vulnerability_scan_finished = make_vuln_callback('Vulnerability scan finished') # Fixtures @pytest.fixture(scope='module', params=configurations, ids=["run_on_start_yes", "run_on_start_no"]) def get_configuration(request): """Get configurations from the module.""" return request.param def test_run_on_start(get_configuration, configure_environment, restart_modulesd): """ Check if modulesd detects the vulnerability detector scan after starting. If we have set the parameter run_on_start to 'yes', modulesd will have to report the
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 """ provider_vulnerabilities_number = mock_vulnerability_scan[ "provider_vulnerabilities_number"] nvd_vulnerabilities_number = mock_vulnerability_scan[ "nvd_vulnerabilities_number"] # Check the vulnerabilities of packages inserted try: for item in vulnerabilities['vulnerabilities_provider']: 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 vulnerabilities['vulnerabilities_provider']: vd.check_vulnerability_scan_event(wazuh_log_monitor, item['package']['name'], item['cve']['cveid']) if mock_vulnerability_scan["format"] != "rpm": try: for item in 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 vulnerabilities['vulnerabilities_nvd']: vd.check_vulnerability_scan_event(wazuh_log_monitor, item['package']['name'], item['cve']['cveid']) # Check that the number of provider vulnerabilities is the expected wazuh_log_monitor.start( timeout=SCAN_TIMEOUT, update_position=False, callback=vd.make_vuln_callback( f"A total of '{provider_vulnerabilities_number}' vulnerabilities have been reported for agent '.*' " + "thanks to the 'vendor' feed."), error_message= f"The expected number of vulnerabilities for vendor have not been found", ) # Check that the number of NVD vulnerabilities is the expected wazuh_log_monitor.start( timeout=SCAN_TIMEOUT, update_position=False, callback=vd.make_vuln_callback( f"A total of '{nvd_vulnerabilities_number}' vulnerabilities have been reported for agent '.*' " + "thanks to the 'NVD' feed."), error_message= f"The expected number of vulnerabilities for NVD have not been found", ) vd.check_if_modulesd_is_running()