Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional Vulnerability Detection E2E tests #5287

Merged
merged 138 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
0fb9ef3
feat: include paralellism to control_environment method
Rebits Mar 11, 2024
bf6247d
feat: include debug and evidence gathering parameters
Rebits Mar 11, 2024
40a896f
feat: merge init syscollector scan and index test
Rebits Mar 11, 2024
3633aee
feat: include change manager test
Rebits Mar 11, 2024
12f32f8
Merge branch '4.8.0' into feat/4914-additional-e2e-vd-tests
Rebits Mar 11, 2024
86ffd73
feat: Evidence and Check classes
Rebits Mar 22, 2024
f0c27ea
feat: migrate load_vd conf to moduel
Rebits Mar 22, 2024
aa95bf0
feat: include List to indexer api
Rebits Mar 22, 2024
d320633
feat: create check_error function
Rebits Mar 22, 2024
b094ff8
feat: migrate syscollector message monitoring to logs module
Rebits Mar 22, 2024
d1f307b
feat: migrate index state gathering function
Rebits Mar 22, 2024
31d182b
fix: update conf and callout
Rebits Mar 22, 2024
799a92f
feat: include verbose evidence parameter
Rebits Mar 22, 2024
d7c584b
feat: create additional E2E tests
Rebits Mar 22, 2024
cf6bf15
feat: improve check validation logging
Rebits Mar 26, 2024
893110d
fix: first second initial scan consistency test
Rebits Mar 26, 2024
daf0b66
fix: update VD start callback
Rebits Mar 26, 2024
e07dc4c
fix: html report generation
Rebits Mar 26, 2024
4201746
feat: create additional tests structure
Rebits Mar 26, 2024
f715faf
feat: refactor remote operator module
Rebits Mar 27, 2024
4d73320
refac: vuln detector e2e modules
Rebits Mar 27, 2024
7133717
fix: test agent down
Rebits Mar 27, 2024
9d1f2ac
fix: include install package checks
Rebits Mar 27, 2024
a13f06b
feat: allow custom certs path
Rebits Apr 2, 2024
5137795
feat: replace vulnerable packages
Rebits Apr 2, 2024
4636e35
style: waiters style errors
Rebits Apr 2, 2024
9852408
fix: change tests case vulenrabilities
Rebits Apr 2, 2024
745e7c3
fix: include logging message
Rebits Apr 2, 2024
7c4aed3
feat: allow custom api password and user
Rebits Apr 2, 2024
b7c5d9a
feat: include custom indexer server to vd conf
Rebits Apr 2, 2024
3db9585
feat: include custom indexer ip conf
Rebits Apr 3, 2024
48a6ce2
feat: allow custom indexer pasword in libraries VD
Rebits Apr 3, 2024
142c0c5
refac: check vulnerabilities methods
Rebits Apr 3, 2024
c1f8959
fix: avoid consider dependencies vulnerabilities
Rebits Apr 3, 2024
61a12d7
fix: error in filter method
Rebits Apr 3, 2024
48c4019
fix: rename variable
Rebits Apr 3, 2024
a6de71b
fix: include CVE-2023-3128
Rebits Apr 3, 2024
aa26f73
fix: error in return value of filter vulnerabilities method
Rebits Apr 3, 2024
53dac02
feat: include alerts to agent down test
Rebits Apr 3, 2024
2261359
fix: remove type from Vulnerability tuple
Rebits Apr 3, 2024
3142b46
fix: remote_handler_operations
Rebits Apr 3, 2024
6041dea
fix: include single vuln package test case
Rebits Apr 3, 2024
c0ddfe4
fix: typo in remote operation dicts
Rebits Apr 3, 2024
a5d8da9
fix: include fix operation handler expected alerts
Rebits Apr 3, 2024
3108e9a
fix: improve date handling
Rebits Apr 3, 2024
f048c19
fix: change time format for remote operation handler func
Rebits Apr 4, 2024
2b1f9ba
fix: replace timeformat
Rebits Apr 4, 2024
f5a104f
fix: setup teardown fixtures
Rebits Apr 4, 2024
357d5e0
fix: validate_operation_results check
Rebits Apr 4, 2024
d98ac4b
fix: typo in test params
Rebits Apr 4, 2024
11080b5
fix: include remove package validation
Rebits Apr 4, 2024
86cd9b9
fix: remove package function
Rebits Apr 4, 2024
d21155d
feat: include vuln validator
Rebits Apr 4, 2024
7863de7
fix: dict bad key
Rebits Apr 4, 2024
6bdf781
fix: typo in remote handler function
Rebits Apr 4, 2024
9e397fc
fix: get only host expected vuln
Rebits Apr 4, 2024
176c2c3
fix: avoid check vuln in setup and teardown tasks
Rebits Apr 4, 2024
e8622fd
fix: check option
Rebits Apr 4, 2024
398ac9a
fix: typo in check method
Rebits Apr 4, 2024
7493ed3
fix: error in remote handler
Rebits Apr 4, 2024
ea3585c
fix: get check value bug
Rebits Apr 4, 2024
e90ad4b
fix: validate vulns function
Rebits Apr 4, 2024
513723e
fix: vulnerability detection vulnerability check function
Rebits Apr 4, 2024
df8868f
fix: typo in expected vulns
Rebits Apr 4, 2024
45afd44
fix: remote handler check vuln and validator function
Rebits Apr 5, 2024
a67d436
refac: tests case structure
Rebits Apr 5, 2024
fa5f446
feat: include remove package validation
Rebits Apr 5, 2024
0632ba5
feat: remove abstraction on tests cases
Rebits Apr 5, 2024
514f372
feat: comment scan tests for debugging
Rebits Apr 9, 2024
dfa068b
Wait until the feed is completed
santipadilla Apr 16, 2024
5b96cbc
Update changelog
santipadilla Apr 16, 2024
f720465
Update CHANGELOG.md
santipadilla Apr 17, 2024
d36391e
Merge remote-tracking branch 'origin/5173-wait-until-vd-is-updated' i…
Rebits Apr 17, 2024
0eac6bd
feat: uncomment scan syscollector cases
Rebits Apr 17, 2024
55f7f78
refac: expected errors and checks library
Rebits Apr 19, 2024
a866b32
Merge branch 'fix/5239-vd-timestamp-filter' into feat/4914-additional…
Rebits Apr 19, 2024
49b54fb
fix: errors in manager configuration
Rebits Apr 22, 2024
5acb9b1
refac: TestScanSyscollectorCases tests
Rebits Apr 23, 2024
d118809
style: pep8 errors in remote_operations_handler
Rebits Apr 24, 2024
59e1089
refac: Evidence class
Rebits Apr 24, 2024
840f91a
style: pep8 to init module e2e
Rebits Apr 24, 2024
3f608d4
refac: removed unnused checks module
Rebits Apr 24, 2024
4072c27
style: pep8 to init configuration module e2e
Rebits Apr 24, 2024
d325f40
refac: remove env utils integrating methods to HostManager
Rebits Apr 24, 2024
6e3a013
refac: wazuh states index variable
Rebits Apr 24, 2024
3806bfe
style: pep8 to logs module
Rebits Apr 24, 2024
9ca60a2
style: pep8 monitoring module
Rebits Apr 24, 2024
bd940a5
refac: removed not used modules
Rebits Apr 24, 2024
a49e6d8
fix: typo in check report
Rebits Apr 24, 2024
5452124
fix: improve checks validations
Rebits Apr 24, 2024
511f930
fix: windows node package removal
Rebits Apr 24, 2024
b8a621a
style: remove extra whitespace
Rebits Apr 24, 2024
1fcaf74
fix: setup target_os handling
Rebits Apr 24, 2024
7b826c8
fix: refactor manager change cases
Rebits Apr 24, 2024
34a8599
fix: remote operation handler upgrade cases
Rebits Apr 24, 2024
77c158e
refac: vulnerability tests cases
Rebits Apr 24, 2024
c514403
refac: single vuln test case
Rebits Apr 24, 2024
7509791
fix: remove pdb trace
Rebits Apr 24, 2024
5667e9f
feat: include comment for waiter
Rebits Apr 24, 2024
55fdc33
fix: remove failing | from methods typping
Rebits Apr 24, 2024
164dd27
Merge branch '4.8.0' into feat/4914-additional-e2e-vd-tests
Rebits Apr 25, 2024
afe37f6
feat: remove macos host from E2E test to allow testing
Rebits Apr 25, 2024
f79b96f
style: fix wrong indentation
Rebits Apr 25, 2024
f5c4090
fix: upgrade non vulnerable package case
Rebits Apr 25, 2024
f20b379
fix: upgrade non vulnerable package case
Rebits Apr 25, 2024
4671ff2
feat: improve unexpected vulenrabilities check
Rebits Apr 26, 2024
2435d46
fix: missing grafana 8.5.5-1 vulnerability
Rebits Apr 26, 2024
6daa456
feat: include duplicate vulenrabilities checks
Rebits Apr 26, 2024
d4d5725
fix: rename duplicated test case
Rebits Apr 26, 2024
da5ea84
fix: remove verbose logging
Rebits Apr 26, 2024
baf7c8d
fix: sort vuln error
Rebits Apr 26, 2024
2d21a01
fix: duplicate vuln information report
Rebits Apr 26, 2024
325e017
refac: get pacakge npm gathering
Rebits Apr 26, 2024
efe97cc
fix: duplicated vuln check
Rebits Apr 26, 2024
634e6f2
fix: upgrade package versions VD e2e
Rebits Apr 28, 2024
958e828
fix: duplicated vuln check
Rebits Apr 28, 2024
7b14d0a
fix: duplicate elements validtor
Rebits Apr 28, 2024
7c4f052
refac: remote handler operation
Rebits Apr 28, 2024
179fdb1
docs: fix typo in changelog
Rebits Apr 29, 2024
60298fe
docs: update copyright line
Rebits Apr 29, 2024
f86b435
docs: update get_alert_indexer_api docstring
Rebits Apr 29, 2024
0e6efc3
style: sort e2e imports
Rebits Apr 29, 2024
8b5f771
refac: remove unnused duplicated elements function
Rebits Apr 29, 2024
e895863
style: sort imports
Rebits Apr 29, 2024
833e820
style: remote operations handler func to inline
Rebits Apr 29, 2024
e970ae5
fix: uncomment macOS agnent provision
Rebits Apr 29, 2024
806d579
style: rename indice_vuln to index_vuln
Rebits Apr 29, 2024
eb29000
fix: restore node 18.20.0 non vuln package
Rebits Apr 29, 2024
88bd944
fix: uncomment environment verification
Rebits Apr 29, 2024
dfb9045
fix: replace deprecated utcnow
Rebits Apr 29, 2024
7633465
fix: hardcoded wait for VD to appear
Rebits Apr 29, 2024
c55a056
fix: move save indexer cred to conftest
Rebits Apr 29, 2024
5f5cff5
fix: replace hardocded timeout for waiter
Rebits Apr 29, 2024
8a8f365
fix: replace deprecated utcnow func
Rebits Apr 29, 2024
ce1cf6a
Merge branch '5173-wait-until-vd-is-updated' into feat/4914-additiona…
Rebits Apr 29, 2024
95ead8f
fix: grafana remove duplicated vuln packages
Rebits Apr 29, 2024
fc5c385
fix: replace windows node package vd
Rebits Apr 30, 2024
74cff8b
Merge branch '4.8.0' into feat/4914-additional-e2e-vd-tests
Rebits Apr 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file.

### Changed

- Include additional Vulnerability Detector E2E tests ([#5287](https://github.com/wazuh/wazuh-qa/pull/5287)) \- (Framework + Tests)
- Change Vulnerability Detection feed updated waiter ([#5227](https://github.com/wazuh/wazuh-qa/pull/5227)) \- (Tests)
- Replace timestamp filter with vulnerabilities detected_at field.([#5266](https://github.com/wazuh/wazuh-qa/pull/5266)) \- (Framework + Tests)
- Changes macOS packages with new ones that generate vulnerabilities ([#5174](https://github.com/wazuh/wazuh-qa/pull/5174)) \- (Tests)
Expand Down
332 changes: 306 additions & 26 deletions deps/wazuh_testing/wazuh_testing/end_to_end/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# Copyright (C) 2015-2022, Wazuh Inc.
# Copyright (C) 2015, Wazuh Inc
# Created by Wazuh, Inc. <info@wazuh.com>.
# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2
import json
import logging
import os
import requests

from dataclasses import dataclass
from http import HTTPStatus
from tempfile import gettempdir
from typing import Any, Callable, List

from wazuh_testing.tools.utils import retry


fetched_alerts_json_path = os.path.join(gettempdir(), 'alerts.json')
VD_E2E_TIMEOUT_SYSCOLLECTOR_SCAN = 130

base_path = {
'linux': '/var/ossec',
Expand All @@ -32,22 +38,24 @@
def get_alert_indexer_api(query, credentials, ip_address, index='wazuh-alerts-4.x-*'):
"""Get an alert from the wazuh-indexer API

Make a request to the wazuh-indexer API to get the last indexed alert that matches the values passed in
must_match.
Make a request to the wazuh-indexer API to get the last indexed alert that matches the values passed in
query.

Args:
ip_address (str): wazuh-indexer IP address.
index (str): Index in which to search for the alert.
query (dict): Query to send to the API.
credentials(dict): wazuh-indexer credentials.
Args:
ip_address (str): wazuh-indexer IP address.
index (str): Index in which to search for the alert.
query (dict): Query to send to the API.
credentials(dict): wazuh-indexer credentials.

Returns:
`obj`(map): Search results
Returns:
`obj`(map): Search results
"""
url = f"https://{ip_address}:9200/{index}/_search?"

response = requests.get(url=url, params={'pretty': 'true'}, json=query, verify=False,
auth=requests.auth.HTTPBasicAuth(credentials['user'], credentials['password']))
response = requests.get(url=url, params={'pretty': 'true'}, json=query,
verify=False,
auth=requests.auth.HTTPBasicAuth(credentials['user'],
credentials['password']))

if '"hits" : [ ]' in response.text:
raise Exception('Alert not indexed')
Expand All @@ -60,19 +68,20 @@ def get_alert_indexer_api(query, credentials, ip_address, index='wazuh-alerts-4.
def delete_index_api(credentials, ip_address, index='wazuh-alerts-4.x-*'):
"""Delete indices from wazuh-indexer using its API.

Make a request to the wazuh-indexer API to delete indices that match a given name.
Make a request to the wazuh-indexer API to delete indices that match a given name.

Args:
ip_address (str): wazuh-indexer IP address.
index (str): Name of the index to be deleted.
credentials(dict): wazuh-indexer credentials.
Args:
ip_address (str): wazuh-indexer IP address.
index (str): Name of the index to be deleted.
credentials(dict): wazuh-indexer credentials.

Returns:
obj(class): `Response <Response>` object
obj(class): `NoneType` object
Returns:
obj(class): `Response <Response>` object
obj(class): `NoneType` object
"""
url = f"https://{ip_address}:9200/"
authorization = requests.auth.HTTPBasicAuth(credentials['user'], credentials['password'])
authorization = requests.auth.HTTPBasicAuth(credentials['user'],
credentials['password'])

response = requests.delete(url=url+index, params={'pretty': 'true'}, verify=False, auth=authorization)

Expand All @@ -85,12 +94,12 @@ def delete_index_api(credentials, ip_address, index='wazuh-alerts-4.x-*'):
def make_query(must_match):
"""Create a query according to the values passed in must_match.

Args:
must_match (list): Values to be matched with the indexed alert.
Args:
must_match (list): Values to be matched with the indexed alert.

Returns:
dict: Fully formed query.
"""
Returns:
dict: Fully formed query.
"""
query = {
"query": {
"bool": {
Expand All @@ -108,3 +117,274 @@ def make_query(must_match):
}

return query


@dataclass
class Evidence:
"""A data class representing evidence.

Attributes:
name (str): The name of the evidence.
value (Any): The value of the evidence.
debug (bool, optional): Indicates whether the evidence is for debugging, for verbose evidences.
Defaults to False.
"""
name: str
value: Any
debug: bool = False

def collect_evidence(self, evidences_directory: str):
"""Collects evidence and stores it in the specified directory.

Args:
evidences_directory (str): The directory where evidence files will be stored.
"""
try:
with open(os.path.join(evidences_directory, self.name), 'w') as evidence_file:
self._write_to_file(evidence_file)
except Exception as e:
self._log_error(e)

def _write_to_file(self, evidence_file):
"""Writes evidence to a file.

Args:
evidence_file: File object to write evidence to.
"""
if isinstance(self.value, (dict, list)):
json.dump(self.value, evidence_file, indent=4)
else:
evidence_file.write(str(self.value))

def _log_error(self, e):
"""Logs error occurred while writing evidence.

Args:
e: The exception that occurred.
"""
logging.error(f"Error while writing evidence {self.name}: {e}")

def dict(self):
"""Returns the evidence as a dictionary.

Returns:
dict: A dictionary representation of the evidence.
"""
return {self.name: self.value}

juliamagan marked this conversation as resolved.
Show resolved Hide resolved

class Check:
"""A class representing a check to be performed, including validation and reporting.

Attributes:
name (str): The name of the check.
assert_function (Callable): The function used for assertion.
expected_evidences (List[str] | None): List of expected evidence names to perform the validation.
Default is None.
result: The result of the check.
evidences: List of collected evidence objects.
"""
def __init__(self, name: str, assert_function: Callable,
expected_evidences: List[str] = None):
"""Initializes a check with the given name, assertion function, and expected evidences.

Args:
name (str): The name of the check.
assert_function (Callable): The function used for assertion.
expected_evidences (List[str] | None, optional): List of expected evidence names. Defaults to None.
"""
self.name = name
self.result = None
self.assert_function = assert_function
self.expected_evidences = expected_evidences if expected_evidences else []
self.evidences = []

def __str__(self) -> str:
"""Returns a string representation of the check.

Returns:
str: A string containing the check's name and result.
"""
return self.report_check()

def validate(self, evidences: List[Evidence] = None) -> bool:
"""Validates the check using the provided evidences.

Args:
evidences (List[Evidence] | None, optional): List of evidence objects. Defaults to None.

Returns:
bool: True if validation succeeds, False otherwise.

Raises:
ValueError: If provided evidences do not contains the expected ones.
"""

evidences = [] if not evidences else evidences

provided_evidences_names = [evidence.name for evidence in evidences]
provided_evidences_expected = [evidence for evidence in evidences
if evidence.name in self.expected_evidences]

if len(self.expected_evidences) != len(provided_evidences_expected):
raise ValueError('Evidences should match the expected ones.\n' +
f"Expected evidences: {self.expected_evidences}."
f"Evidences found: {provided_evidences_names}")

self.result = self.assert_function(*[evidence.value for evidence in provided_evidences_expected])
self.evidences = evidences

logging.error(f"Marked check {self.name} result to {self.result} with evidences {provided_evidences_names}")

return self.result

def get_result(self):
"""Gets the result of the check.

Returns:
Any: The result of the check.

Raises:
ValueError: If the check has not been executed yet.
"""
if self.result is None:
raise ValueError(f"Check {self.name} has not been executed yet")

return self.result

def report_check(self):
"""Generates a report message for the check.

Returns:
str: A report message indicating whether the check succeeded or failed.
"""
message = f"Check {self.name} "
message += f"failed. Evidences ({self.expected_evidences}) " + \
"can be found in the report." if not self.get_result() else "succeeded"
message += '\n'

return message

def collect_evidences(self, evidences_directory: str, collect_debug_evidences: bool = False):
"""Collects evidences for the check.

Args:
evidences_directory (str): The directory where evidence files will be stored.
collect_debug_evidences (bool, optional): If True, collects debug evidence. Defaults to False.
"""
for evidence in self.evidences:
if evidence.debug and not collect_debug_evidences:
continue

evidence.collect_evidence(evidences_directory)


class TestResult:
"""A data class representing a test result.

Attributes:
test_name (str): The name of the test.
checks (List[Check]): List of checks of the test, default is an empty list.
"""
def __init__(self, test_name: str, checks: List[Check] = None):
"""Initializes a test suite with the given name and checks.

Args:
test_name (str): The name of the test suite.
checks (List[Check] | None, optional): List of checks. Defaults to None.
"""
self.test_name = test_name
self.checks = checks if checks else []

def __str__(self) -> str:
"""Returns a string representation of the test suite.

Returns:
str: A string containing the test suite's name and report.
"""
return self.report()

def add_check(self, check: Check) -> None:
"""Adds a check to the test suite.

Args:
check (Check): The check to be added.
"""
self.checks.append(check)

def get_test_result(self) -> bool:
"""Gets the result of the test suite.

Returns:
bool: True if all checks passed, False otherwise.
"""
return all([check.result for check in self.checks])

def collect_evidences(self, evidences_directory: str,
collect_verbose_evidences: bool = False,
collect_evidences_for_passed_checks: bool = False) -> None:
"""Collects evidences for the checks in the test suite.

Args:
evidences_directory (str): The directory where evidence files will be stored.
collect_verbose_evidences (bool, optional): If True, collects verbose evidences. Defaults to False.
collect_evidences_for_passed_checks (bool, optional): If True, collects evidences for passed checks as well.
Defaults to False.
"""
for check in self.checks:
if check.get_result() and not collect_evidences_for_passed_checks:
continue
check.collect_evidences(evidences_directory, collect_verbose_evidences)

def report(self) -> str:
"""Generates a report message for the test suite.

Returns:
str: A report message indicating whether the test suite succeeded or failed,
along with individual check reports.
"""
message = f"\nTest {self.test_name} "
message += "failed\n\n" if not self.get_test_result() else "succeeded:\n\n-----\n"

if not self.get_test_result():
for check in self.checks:
message += check.report_check()

message += "-----\n"

return message

def validate_check(self, check_name: str, evidences: List[Evidence]) -> bool:
"""Validates a specific check in the test suite.

Args:
check_name (str): The name of the check to validate.
evidences (List[Evidence]): List of evidence objects.

Returns:
bool: True if validation succeeds, False otherwise.

Raises:
ValueError: If the check with the given name is not found in the test suite.
"""
check = self.get_check(check_name)

return check.validate(evidences)

def get_check(self, check_name: str) -> Check:
""" Retrieves a specific check from the test suite.

Args:
check_name (str): The name of the check to retrieve.

Returns:
Check: The check object.

Raises:
ValueError: If the check with the given name is not found in the test suite.
"""
for check in self.checks:
if check.name == check_name:
return check
else:
raise ValueError(f"Check {check_name} not found in test {self.test_name}")