diff --git a/prowler/lib/scan/exceptions/exceptions.py b/prowler/lib/scan/exceptions/exceptions.py index 96b11acd43..07f89e2b3e 100644 --- a/prowler/lib/scan/exceptions/exceptions.py +++ b/prowler/lib/scan/exceptions/exceptions.py @@ -25,6 +25,10 @@ class ScanBaseException(ProwlerException): "message": "Invalid category provided.", "remediation": "Please provide a valid category name.", }, + (2005, "ScanInvalidStatusError"): { + "message": "Invalid status provided.", + "remediation": "Please provide a valid status: FAIL, PASS, MANUAL.", + }, } def __init__(self, code, file=None, original_exception=None, message=None): @@ -74,3 +78,10 @@ class ScanInvalidCategoryError(ScanBaseException): super().__init__( 2004, file=file, original_exception=original_exception, message=message ) + + +class ScanInvalidStatusError(ScanBaseException): + def __init__(self, file=None, original_exception=None, message=None): + super().__init__( + 2005, file=file, original_exception=original_exception, message=message + ) diff --git a/prowler/lib/scan/scan.py b/prowler/lib/scan/scan.py index 96df1af1d8..acbee45871 100644 --- a/prowler/lib/scan/scan.py +++ b/prowler/lib/scan/scan.py @@ -12,13 +12,14 @@ from prowler.lib.check.compliance import update_checks_metadata_with_compliance from prowler.lib.check.compliance_models import Compliance from prowler.lib.check.models import CheckMetadata from prowler.lib.logger import logger -from prowler.lib.outputs.finding import Finding, Severity +from prowler.lib.outputs.finding import Finding, Severity, Status from prowler.lib.scan.exceptions.exceptions import ( ScanInvalidCategoryError, ScanInvalidCheckError, ScanInvalidComplianceFrameworkError, ScanInvalidServiceError, ScanInvalidSeverityError, + ScanInvalidStatusError, ) from prowler.providers.common.models import Audit_Metadata from prowler.providers.common.provider import Provider @@ -36,6 +37,7 @@ class Scan: _progress: float = 0.0 _findings: list = [] _duration: int = 0 + _status: list[str] = None def __init__( self, @@ -47,6 +49,7 @@ class Scan: severities: list[str] = None, excluded_checks: list[str] = None, excluded_services: list[str] = None, + status: list[str] = None, ): """ Scan is the class that executes the checks and yields the progress and the findings. @@ -60,6 +63,7 @@ class Scan: severities: list[str] -> The severities of the checks excluded_checks: list[str] -> The checks to exclude excluded_services: list[str] -> The services to exclude + status: list[str] -> The status of the checks Raises: ScanInvalidCheckError: If the check does not exist in the provider or is from another provider. @@ -67,9 +71,22 @@ class Scan: ScanInvalidComplianceFrameworkError: If the compliance framework does not exist in the provider. ScanInvalidCategoryError: If the category does not exist in the provider. ScanInvalidSeverityError: If the severity does not exist in the provider. + ScanInvalidStatusError: If the status does not exist in the provider. """ self._provider = provider + # Validate the status + if status: + try: + for s in status: + Status(s) + if not self._status: + self._status = [] + if s not in self._status: + self._status.append(s) + except ValueError: + raise ScanInvalidStatusError(f"Invalid status provided: {s}.") + # Load bulk compliance frameworks bulk_compliance_frameworks = Compliance.get_bulk(provider.type) @@ -258,6 +275,12 @@ class Scan: output_options=None, ) + # Filter the findings by the status + if self._status: + for finding in check_findings: + if finding.status not in self._status: + check_findings.remove(finding) + # Store findings self._findings.extend(check_findings) diff --git a/tests/lib/scan/scan_test.py b/tests/lib/scan/scan_test.py index 079ac0bb13..e33a4eb7f9 100644 --- a/tests/lib/scan/scan_test.py +++ b/tests/lib/scan/scan_test.py @@ -11,6 +11,7 @@ from prowler.lib.scan.exceptions.exceptions import ( ScanInvalidComplianceFrameworkError, ScanInvalidServiceError, ScanInvalidSeverityError, + ScanInvalidStatusError, ) from prowler.lib.scan.scan import Scan, get_service_checks_to_execute from tests.lib.outputs.fixtures.fixtures import generate_finding_output @@ -376,3 +377,41 @@ class TestScan: Scan( mock_provider, checks=checks_to_execute, categories=["invalid_category"] ) + + def test_init_invalid_status( + mock_provider, + ): + checks_to_execute = set() + mock_provider.type = "aws" + + with pytest.raises(ScanInvalidStatusError): + Scan(mock_provider, checks=checks_to_execute, status=["invalid_status"]) + + @patch("importlib.import_module") + def test_scan_filter_status( + mock_import_module, + mock_global_provider, + mock_recover_checks_from_provider, + mock_load_check_metadata, + ): + mock_check_class = MagicMock() + mock_check_instance = mock_check_class.return_value + mock_check_instance.Provider = "aws" + mock_check_instance.CheckID = "accessanalyzer_enabled" + mock_check_instance.CheckTitle = "Check if IAM Access Analyzer is enabled" + mock_check_instance.Categories = [] + + mock_import_module.return_value = MagicMock( + accessanalyzer_enabled=mock_check_class + ) + + checks_to_execute = {"accessanalyzer_enabled"} + custom_checks_metadata = {} + mock_global_provider.type = "aws" + + scan = Scan(mock_global_provider, checks=checks_to_execute, status=["FAIL"]) + mock_load_check_metadata.assert_called_once() + mock_recover_checks_from_provider.assert_called_once_with("aws") + results = list(scan.scan(custom_checks_metadata)) + + assert results[0] == (100.0, [])