mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-18 01:51:50 +00:00
79450d6977
Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com>
1450 lines
55 KiB
Python
1450 lines
55 KiB
Python
import re
|
|
from logging import WARNING
|
|
|
|
import botocore
|
|
import pytest
|
|
from boto3 import session
|
|
from botocore.client import ClientError
|
|
from mock import patch
|
|
|
|
from prowler.lib.outputs.asff.asff import ASFF
|
|
from prowler.providers.aws.lib.security_hub.exceptions.exceptions import (
|
|
SecurityHubInvalidRegionError,
|
|
SecurityHubNoEnabledRegionsError,
|
|
)
|
|
from prowler.providers.aws.lib.security_hub.security_hub import SecurityHub
|
|
from tests.lib.outputs.fixtures.fixtures import generate_finding_output
|
|
from tests.providers.aws.utils import (
|
|
AWS_ACCOUNT_NUMBER,
|
|
AWS_COMMERCIAL_PARTITION,
|
|
AWS_REGION_EU_WEST_1,
|
|
AWS_REGION_EU_WEST_2,
|
|
)
|
|
|
|
# Mocking Security Hub Get Findings
|
|
make_api_call = botocore.client.BaseClient._make_api_call
|
|
|
|
|
|
def mock_make_api_call(self, operation_name, kwarg):
|
|
if operation_name == "BatchImportFindings":
|
|
return {
|
|
"FailedCount": 0,
|
|
"SuccessCount": 1,
|
|
}
|
|
if operation_name == "DescribeHub":
|
|
return {
|
|
"HubArn": f"arn:aws:securityhub:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:hub/default",
|
|
"SubscribedAt": "2023-02-07T09:45:43.742Z",
|
|
"AutoEnableControls": True,
|
|
"ControlFindingGenerator": "STANDARD_CONTROL",
|
|
}
|
|
|
|
if operation_name == "ListEnabledProductsForImport":
|
|
return {
|
|
"ProductSubscriptions": [
|
|
f"arn:aws:securityhub:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:product-subscription/prowler/prowler",
|
|
f"arn:aws:securityhub:{AWS_REGION_EU_WEST_2}:{AWS_ACCOUNT_NUMBER}:product-subscription/prowler/prowler",
|
|
]
|
|
}
|
|
|
|
return make_api_call(self, operation_name, kwarg)
|
|
|
|
|
|
class TestSecurityHub:
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_verify_security_hub_integration_enabled_per_region(self):
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
assert security_hub._enabled_regions
|
|
assert len(security_hub._enabled_regions) == 1
|
|
assert AWS_REGION_EU_WEST_1 in security_hub._enabled_regions
|
|
|
|
def test_verify_security_hub_integration_enabled_per_region_security_hub_disabled(
|
|
self, caplog
|
|
):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.Session.client",
|
|
) as mock_security_hub:
|
|
error_message = f"Account {AWS_ACCOUNT_NUMBER} is not subscribed to AWS Security Hub in region {AWS_REGION_EU_WEST_1}"
|
|
error_code = "InvalidAccessException"
|
|
error_response = {
|
|
"Error": {
|
|
"Code": error_code,
|
|
"Message": error_message,
|
|
}
|
|
}
|
|
operation_name = "DescribeHub"
|
|
mock_security_hub.side_effect = ClientError(error_response, operation_name)
|
|
|
|
log_pattern = re.compile(
|
|
r"ClientError -- \[\d+\]: An error occurred \({error_code}\) when calling the {operation_name} operation: {error_message}".format(
|
|
error_code=re.escape(error_code),
|
|
operation_name=re.escape(operation_name),
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
|
|
assert security_hub._enabled_regions == {}
|
|
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|
|
|
|
def test_verify_security_hub_integration_enabled_per_region_prowler_not_subscribed(
|
|
self, caplog
|
|
):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.Session.client",
|
|
) as mock_security_hub:
|
|
mock_security_hub.describe_hub.return_value = None
|
|
mock_security_hub.list_enabled_products_for_import.return_value = []
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
|
|
assert security_hub._enabled_regions == {}
|
|
assert caplog.record_tuples == [
|
|
(
|
|
"root",
|
|
WARNING,
|
|
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.cloud/en/latest/tutorials/aws/securityhub/",
|
|
)
|
|
]
|
|
|
|
def test_verify_security_hub_integration_enabled_per_region_another_ClientError(
|
|
self, caplog
|
|
):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.Session.client",
|
|
) as mock_security_hub:
|
|
error_message = f"Another exception in region {AWS_REGION_EU_WEST_1}"
|
|
error_code = "AnotherException"
|
|
error_response = {
|
|
"Error": {
|
|
"Code": error_code,
|
|
"Message": error_message,
|
|
}
|
|
}
|
|
operation_name = "DescribeHub"
|
|
mock_security_hub.side_effect = ClientError(error_response, operation_name)
|
|
|
|
log_pattern = re.compile(
|
|
r"ClientError -- \[\d+\]: An error occurred \({error_code}\) when calling the {operation_name} operation: {error_message}".format(
|
|
error_code=re.escape(error_code),
|
|
operation_name=re.escape(operation_name),
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
|
|
assert security_hub._enabled_regions == {}
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|
|
|
|
def test_verify_security_hub_integration_enabled_per_region_another_Exception(
|
|
self, caplog
|
|
):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.Session.client",
|
|
) as mock_security_hub:
|
|
error_message = f"Another exception in region {AWS_REGION_EU_WEST_1}"
|
|
mock_security_hub.side_effect = Exception(error_message)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
|
|
log_pattern = re.compile(
|
|
r"Exception -- \[\d+\]: {error_message}".format(
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
|
|
assert security_hub._enabled_regions == {}
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_enabled_region_all_statuses(self):
|
|
findings = [generate_finding_output(status="PASS", region=AWS_REGION_EU_WEST_1)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {
|
|
AWS_REGION_EU_WEST_1: [asff.data[0]]
|
|
}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_all_statuses_MANUAL_finding(self):
|
|
findings = [
|
|
generate_finding_output(status="MANUAL", region=AWS_REGION_EU_WEST_1)
|
|
]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_disabled_region(self):
|
|
findings = [generate_finding_output(status="PASS", region=AWS_REGION_EU_WEST_2)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {AWS_REGION_EU_WEST_1: []}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_FAIL_and_FAIL_statuses(self):
|
|
findings = [generate_finding_output(status="FAIL", region=AWS_REGION_EU_WEST_1)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {
|
|
AWS_REGION_EU_WEST_1: [asff.data[0]]
|
|
}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_send_sh_only_fails_PASS(self):
|
|
findings = [generate_finding_output(status="PASS", region=AWS_REGION_EU_WEST_1)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
send_only_fails=True,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {AWS_REGION_EU_WEST_1: []}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_send_sh_only_fails_FAIL(self):
|
|
findings = [generate_finding_output(status="FAIL", region=AWS_REGION_EU_WEST_1)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
send_only_fails=True,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {
|
|
AWS_REGION_EU_WEST_1: [asff.data[0]]
|
|
}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_no_audited_regions(self):
|
|
findings = [generate_finding_output(status="PASS", region=AWS_REGION_EU_WEST_1)]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[],
|
|
findings=asff.data,
|
|
send_only_fails=True,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_muted_fail_with_send_sh_only_fails(
|
|
self,
|
|
):
|
|
findings = [
|
|
generate_finding_output(
|
|
status="FAIL", region=AWS_REGION_EU_WEST_1, muted=True
|
|
)
|
|
]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
send_only_fails=True,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {
|
|
AWS_REGION_EU_WEST_1: [],
|
|
}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_filter_security_hub_findings_per_region_muted_fail_with_status_FAIL(self):
|
|
findings = [
|
|
generate_finding_output(
|
|
status="FAIL", region=AWS_REGION_EU_WEST_1, muted=True
|
|
)
|
|
]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
findings=asff.data,
|
|
send_only_fails=True,
|
|
)
|
|
|
|
assert security_hub._findings_per_region == {
|
|
AWS_REGION_EU_WEST_1: [],
|
|
}
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_batch_send_to_security_hub_one_finding(self):
|
|
enabled_regions = [AWS_REGION_EU_WEST_1, AWS_REGION_EU_WEST_2]
|
|
findings = [
|
|
generate_finding_output(status="PASS", region=AWS_REGION_EU_WEST_1),
|
|
generate_finding_output(status="FAIL", region=AWS_REGION_EU_WEST_2),
|
|
]
|
|
asff = ASFF(findings=findings)
|
|
|
|
security_hub = SecurityHub(
|
|
aws_session=session.Session(
|
|
region_name=AWS_REGION_EU_WEST_1,
|
|
),
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=enabled_regions,
|
|
findings=asff.data,
|
|
)
|
|
|
|
assert security_hub.batch_send_to_security_hub() == 2
|
|
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_security_hub_test_connection_success(self):
|
|
|
|
# Test successful connection
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
regions={AWS_REGION_EU_WEST_1},
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
|
|
@patch("prowler.providers.aws.lib.security_hub.security_hub.Session.client")
|
|
def test_security_hub_test_connection_invalid_access_exception(
|
|
self, mock_security_hub_client
|
|
):
|
|
# Mock an InvalidAccessException
|
|
error_message = f"Account {AWS_ACCOUNT_NUMBER} is not subscribed to AWS Security Hub in region {AWS_REGION_EU_WEST_1}"
|
|
error_code = "InvalidAccessException"
|
|
error_response = {
|
|
"Error": {
|
|
"Code": error_code,
|
|
"Message": error_message,
|
|
}
|
|
}
|
|
operation_name = "DescribeHub"
|
|
mock_security_hub_client.side_effect = ClientError(
|
|
error_response, operation_name
|
|
)
|
|
|
|
# Test connection failure due to invalid access
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
regions={AWS_REGION_EU_WEST_1},
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, SecurityHubInvalidRegionError)
|
|
|
|
@patch("prowler.providers.aws.lib.security_hub.security_hub.Session.client")
|
|
def test_security_hub_test_connection_prowler_not_subscribed(
|
|
self, mock_security_hub_client
|
|
):
|
|
# Mock successful Security Hub but no Prowler subscription
|
|
mock_security_hub_client.describe_hub.return_value = {}
|
|
mock_security_hub_client.list_enabled_products_for_import.return_value = {
|
|
"ProductSubscriptions": []
|
|
}
|
|
|
|
# Test connection failure due to missing Prowler subscription
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
regions={AWS_REGION_EU_WEST_1},
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, SecurityHubInvalidRegionError)
|
|
|
|
@patch("prowler.providers.aws.lib.security_hub.security_hub.Session.client")
|
|
def test_security_hub_test_connection_unexpected_exception(
|
|
self, mock_security_hub_client
|
|
):
|
|
# Mock unexpected exception
|
|
mock_security_hub_client.side_effect = Exception("Unexpected error")
|
|
|
|
# Test connection failure due to an unexpected exception
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
regions={AWS_REGION_EU_WEST_1},
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, SecurityHubInvalidRegionError)
|
|
|
|
@patch("prowler.providers.aws.lib.security_hub.security_hub.Session.client")
|
|
def test_security_hub_test_connection_no_regions_enabled(
|
|
self, mock_security_hub_client
|
|
):
|
|
# Mock unexpected exception
|
|
mock_security_hub_client.side_effect = Exception("Unexpected error")
|
|
|
|
# Test connection failure due to an unexpected exception
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, SecurityHubNoEnabledRegionsError)
|
|
|
|
def test_init_without_session(self):
|
|
with pytest.raises(ValueError) as e:
|
|
SecurityHub(
|
|
aws_session=None,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
)
|
|
|
|
assert (
|
|
str(e.value)
|
|
== "If no role ARN is provided, a profile, an AWS access key ID, or an AWS secret access key is required."
|
|
)
|
|
|
|
def test_init_without_session_but_role_arn(self):
|
|
with pytest.raises(ValueError) as e:
|
|
SecurityHub(
|
|
aws_session=None,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
role_arn="arn:aws:iam::123456789012:role/my-role",
|
|
)
|
|
|
|
assert (
|
|
str(e.value)
|
|
== "If a role ARN is provided, a session duration, an external ID, and a role session name are required."
|
|
)
|
|
|
|
def test_init_without_session_and_role_arn_but_session_duration(self):
|
|
with pytest.raises(ValueError) as e:
|
|
SecurityHub(
|
|
aws_session=None,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_security_hub_available_regions=[AWS_REGION_EU_WEST_1],
|
|
session_duration=3600,
|
|
)
|
|
|
|
assert (
|
|
str(e.value)
|
|
== "If no role ARN is provided, a profile, an AWS access key ID, or an AWS secret access key is required."
|
|
)
|
|
|
|
# Tests for new test_connection functionality - AWS Credential Management
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_profile(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with profile
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
profile="test-profile",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_setup_session.assert_called_once_with(
|
|
mfa=False,
|
|
profile="test-profile",
|
|
aws_access_key_id=None,
|
|
aws_secret_access_key=None,
|
|
aws_session_token=None,
|
|
)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_access_keys(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with access keys
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_access_key_id="test-key",
|
|
aws_secret_access_key="test-secret",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_setup_session.assert_called_once_with(
|
|
mfa=False,
|
|
profile=None,
|
|
aws_access_key_id="test-key",
|
|
aws_secret_access_key="test-secret",
|
|
aws_session_token=None,
|
|
)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_session_token(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with session token
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_session_token="test-token",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_setup_session.assert_called_once_with(
|
|
mfa=False,
|
|
profile=None,
|
|
aws_access_key_id=None,
|
|
aws_secret_access_key=None,
|
|
aws_session_token="test-token",
|
|
)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_mfa(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with MFA enabled
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
mfa_enabled=True,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_setup_session.assert_called_once_with(
|
|
mfa=True,
|
|
profile=None,
|
|
aws_access_key_id=None,
|
|
aws_secret_access_key=None,
|
|
aws_session_token=None,
|
|
)
|
|
|
|
# Tests for Role Assumption functionality
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.assume_role")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_role_arn(
|
|
self,
|
|
mock_verify_enabled,
|
|
mock_get_regions,
|
|
mock_assume_role,
|
|
mock_setup_session,
|
|
):
|
|
# Mock initial session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock assumed role credentials
|
|
from datetime import datetime, timezone
|
|
|
|
from prowler.providers.aws.models import AWSCredentials
|
|
|
|
mock_credentials = AWSCredentials(
|
|
aws_access_key_id="assumed-key",
|
|
aws_secret_access_key="assumed-secret",
|
|
aws_session_token="assumed-token",
|
|
expiration=datetime.now(timezone.utc),
|
|
)
|
|
mock_assume_role.return_value = mock_credentials
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with role ARN
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
role_arn="arn:aws:iam::123456789012:role/test-role",
|
|
external_id="test-external-id",
|
|
session_duration=7200,
|
|
role_session_name="test-session",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_assume_role.assert_called_once()
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.assume_role")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_role_arn_default_values(
|
|
self,
|
|
mock_verify_enabled,
|
|
mock_get_regions,
|
|
mock_assume_role,
|
|
mock_setup_session,
|
|
):
|
|
# Mock initial session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock assumed role credentials
|
|
from datetime import datetime, timezone
|
|
|
|
from prowler.providers.aws.models import AWSCredentials
|
|
|
|
mock_credentials = AWSCredentials(
|
|
aws_access_key_id="assumed-key",
|
|
aws_secret_access_key="assumed-secret",
|
|
aws_session_token="assumed-token",
|
|
expiration=datetime.now(timezone.utc),
|
|
)
|
|
mock_assume_role.return_value = mock_credentials
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with role ARN using default values
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
role_arn="arn:aws:iam::123456789012:role/test-role",
|
|
external_id="test-external-id",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
mock_assume_role.assert_called_once()
|
|
|
|
# Tests for Error Handling - Session Setup Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_setup_session_error(self, mock_setup_session):
|
|
from prowler.providers.aws.exceptions.exceptions import AWSSetUpSessionError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSSetUpSessionError(
|
|
file="test_file.py", original_exception=Exception("Session setup failed")
|
|
)
|
|
|
|
# Test connection failure due to session setup error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSSetUpSessionError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_setup_session_error_raise(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import AWSSetUpSessionError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSSetUpSessionError(
|
|
file="test_file.py", original_exception=Exception("Session setup failed")
|
|
)
|
|
|
|
# Test that error is raised when raise_on_exception=True
|
|
with pytest.raises(AWSSetUpSessionError):
|
|
SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=True,
|
|
)
|
|
|
|
# Tests for Error Handling - Argument Validation Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_argument_validation_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSArgumentTypeValidationError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSArgumentTypeValidationError(
|
|
file="test_file.py", original_exception=ValueError("Invalid argument")
|
|
)
|
|
|
|
# Test connection failure due to argument validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSArgumentTypeValidationError)
|
|
|
|
# Tests for Error Handling - Role ARN Validation Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_region_not_empty_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNRegionNotEmtpyError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNRegionNotEmtpyError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN region validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNRegionNotEmtpyError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_partition_empty_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNPartitionEmptyError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNPartitionEmptyError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN partition validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNPartitionEmptyError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_service_not_iam_sts_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNServiceNotIAMnorSTSError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNServiceNotIAMnorSTSError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN service validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNServiceNotIAMnorSTSError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_invalid_account_id_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNInvalidAccountIDError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNInvalidAccountIDError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN account ID validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNInvalidAccountIDError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_invalid_resource_type_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNInvalidResourceTypeError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNInvalidResourceTypeError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN resource type validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNInvalidResourceTypeError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_role_arn_empty_resource_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSIAMRoleARNEmptyResourceError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSIAMRoleARNEmptyResourceError(
|
|
file="test_file.py"
|
|
)
|
|
|
|
# Test connection failure due to role ARN empty resource validation error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSIAMRoleARNEmptyResourceError)
|
|
|
|
# Tests for Error Handling - Role Assumption Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_assume_role_error(self, mock_setup_session):
|
|
from prowler.providers.aws.exceptions.exceptions import AWSAssumeRoleError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSAssumeRoleError(
|
|
file="test_file.py", original_exception=Exception("Role assumption failed")
|
|
)
|
|
|
|
# Test connection failure due to role assumption error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSAssumeRoleError)
|
|
|
|
# Tests for Error Handling - Profile and Credential Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_profile_not_found_error(
|
|
self, mock_setup_session
|
|
):
|
|
from botocore.exceptions import ProfileNotFound
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = ProfileNotFound(profile="test-profile")
|
|
|
|
# Test connection failure due to profile not found error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
profile="test-profile",
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, ProfileNotFound)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_profile_not_found_error_raise(
|
|
self, mock_setup_session
|
|
):
|
|
from botocore.exceptions import ProfileNotFound
|
|
|
|
from prowler.providers.aws.exceptions.exceptions import AWSProfileNotFoundError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = ProfileNotFound(profile="test-profile")
|
|
|
|
# Test that error is raised when raise_on_exception=True
|
|
with pytest.raises(AWSProfileNotFoundError):
|
|
SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
profile="test-profile",
|
|
raise_on_exception=True,
|
|
)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_no_credentials_error(
|
|
self, mock_setup_session
|
|
):
|
|
from botocore.exceptions import NoCredentialsError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = NoCredentialsError()
|
|
|
|
# Test connection failure due to no credentials error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, NoCredentialsError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_no_credentials_error_raise(
|
|
self, mock_setup_session
|
|
):
|
|
from botocore.exceptions import NoCredentialsError
|
|
|
|
from prowler.providers.aws.exceptions.exceptions import AWSNoCredentialsError
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = NoCredentialsError()
|
|
|
|
# Test that error is raised when raise_on_exception=True
|
|
with pytest.raises(AWSNoCredentialsError):
|
|
SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=True,
|
|
)
|
|
|
|
# Tests for Error Handling - Access Key and Secret Key Errors
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_access_key_id_invalid_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSAccessKeyIDInvalidError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSAccessKeyIDInvalidError(
|
|
file="test_file.py", original_exception=ValueError("Invalid access key ID")
|
|
)
|
|
|
|
# Test connection failure due to invalid access key ID error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSAccessKeyIDInvalidError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_secret_access_key_invalid_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSSecretAccessKeyInvalidError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSSecretAccessKeyInvalidError(
|
|
file="test_file.py",
|
|
original_exception=ValueError("Invalid secret access key"),
|
|
)
|
|
|
|
# Test connection failure due to invalid secret access key error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSSecretAccessKeyInvalidError)
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_session_token_expired_error(
|
|
self, mock_setup_session
|
|
):
|
|
from prowler.providers.aws.exceptions.exceptions import (
|
|
AWSSessionTokenExpiredError,
|
|
)
|
|
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = AWSSessionTokenExpiredError(
|
|
file="test_file.py", original_exception=ValueError("Session token expired")
|
|
)
|
|
|
|
# Test connection failure due to session token expired error
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, AWSSessionTokenExpiredError)
|
|
|
|
# Tests for Error Handling - Generic Exception
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_generic_exception(self, mock_setup_session):
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = Exception("Generic error")
|
|
|
|
# Test connection failure due to generic exception
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is False
|
|
assert isinstance(connection.error, Exception)
|
|
assert str(connection.error) == "Generic error"
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
def test_security_hub_test_connection_generic_exception_raise(
|
|
self, mock_setup_session
|
|
):
|
|
# Mock session setup error
|
|
mock_setup_session.side_effect = Exception("Generic error")
|
|
|
|
# Test that error is raised when raise_on_exception=True
|
|
with pytest.raises(Exception) as exc_info:
|
|
SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=True,
|
|
)
|
|
|
|
assert str(exc_info.value) == "Generic error"
|
|
|
|
# Tests for Edge Cases and Parameter Validation
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_with_aws_region(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1, AWS_REGION_EU_WEST_2]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection with specific AWS region
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
aws_region=AWS_REGION_EU_WEST_1,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
assert AWS_REGION_EU_WEST_1 in connection.enabled_regions
|
|
assert AWS_REGION_EU_WEST_2 in connection.disabled_regions
|
|
|
|
@patch("prowler.providers.aws.aws_provider.AwsProvider.setup_session")
|
|
@patch(
|
|
"prowler.providers.aws.aws_provider.AwsProvider.get_available_aws_service_regions"
|
|
)
|
|
@patch(
|
|
"prowler.providers.aws.lib.security_hub.security_hub.SecurityHub.verify_enabled_per_region"
|
|
)
|
|
def test_security_hub_test_connection_no_regions_specified(
|
|
self, mock_verify_enabled, mock_get_regions, mock_setup_session
|
|
):
|
|
# Mock session setup
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
mock_setup_session.return_value = mock_session
|
|
|
|
# Mock available regions
|
|
mock_get_regions.return_value = [AWS_REGION_EU_WEST_1, AWS_REGION_EU_WEST_2]
|
|
|
|
# Mock enabled regions
|
|
mock_verify_enabled.return_value = {AWS_REGION_EU_WEST_1: mock_session}
|
|
|
|
# Test connection without specifying regions
|
|
connection = SecurityHub.test_connection(
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
raise_on_exception=False,
|
|
)
|
|
|
|
assert connection.is_connected is True
|
|
assert connection.error is None
|
|
assert len(connection.enabled_regions) == 1
|
|
assert len(connection.disabled_regions) == 1
|
|
|
|
# Tests for _check_region_security_hub static method
|
|
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
|
|
def test_check_region_security_hub_success(self):
|
|
# Test successful security hub check
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
|
|
region, client = SecurityHub._check_region_security_hub(
|
|
region=AWS_REGION_EU_WEST_1,
|
|
session=mock_session,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
)
|
|
|
|
assert region == AWS_REGION_EU_WEST_1
|
|
assert client is not None
|
|
assert hasattr(client, "_make_api_call")
|
|
|
|
def test_check_region_security_hub_invalid_access_exception(self, caplog):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch("boto3.Session.client") as mock_client:
|
|
error_message = (
|
|
f"Account {AWS_ACCOUNT_NUMBER} is not subscribed to AWS Security Hub"
|
|
)
|
|
error_code = "InvalidAccessException"
|
|
error_response = {
|
|
"Error": {
|
|
"Code": error_code,
|
|
"Message": error_message,
|
|
}
|
|
}
|
|
operation_name = "DescribeHub"
|
|
mock_client.side_effect = ClientError(error_response, operation_name)
|
|
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
|
|
region, client = SecurityHub._check_region_security_hub(
|
|
region=AWS_REGION_EU_WEST_1,
|
|
session=mock_session,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
)
|
|
|
|
assert region == AWS_REGION_EU_WEST_1
|
|
assert client is None
|
|
|
|
# Check that warning was logged for InvalidAccessException
|
|
log_pattern = re.compile(
|
|
r"ClientError -- \[\d+\]: An error occurred \({error_code}\) when calling the {operation_name} operation: {error_message}".format(
|
|
error_code=re.escape(error_code),
|
|
operation_name=re.escape(operation_name),
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|
|
|
|
def test_check_region_security_hub_prowler_integration_not_enabled(self, caplog):
|
|
from logging import INFO
|
|
|
|
caplog.set_level(INFO)
|
|
|
|
with patch("boto3.Session.client") as mock_client:
|
|
mock_security_hub_client = mock_client.return_value
|
|
mock_security_hub_client.describe_hub.return_value = {}
|
|
mock_security_hub_client.list_enabled_products_for_import.return_value = {
|
|
"ProductSubscriptions": []
|
|
}
|
|
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
|
|
region, client = SecurityHub._check_region_security_hub(
|
|
region=AWS_REGION_EU_WEST_1,
|
|
session=mock_session,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
)
|
|
|
|
assert region == AWS_REGION_EU_WEST_1
|
|
assert client is None
|
|
|
|
# Check that warning was logged for missing Prowler integration
|
|
assert caplog.record_tuples == [
|
|
(
|
|
"root",
|
|
INFO,
|
|
f"Checking if the prowler/prowler is enabled in the {AWS_REGION_EU_WEST_1} region.",
|
|
),
|
|
(
|
|
"root",
|
|
WARNING,
|
|
f"Security Hub is enabled in {AWS_REGION_EU_WEST_1} but Prowler integration does not accept findings. More info: https://docs.prowler.cloud/en/latest/tutorials/aws/securityhub/",
|
|
),
|
|
]
|
|
|
|
def test_check_region_security_hub_other_client_error(self, caplog):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch("boto3.Session.client") as mock_client:
|
|
error_message = "Some other error"
|
|
error_code = "SomeOtherException"
|
|
error_response = {
|
|
"Error": {
|
|
"Code": error_code,
|
|
"Message": error_message,
|
|
}
|
|
}
|
|
operation_name = "DescribeHub"
|
|
mock_client.side_effect = ClientError(error_response, operation_name)
|
|
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
|
|
region, client = SecurityHub._check_region_security_hub(
|
|
region=AWS_REGION_EU_WEST_1,
|
|
session=mock_session,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
)
|
|
|
|
assert region == AWS_REGION_EU_WEST_1
|
|
assert client is None
|
|
|
|
# Check that error was logged for other ClientError
|
|
log_pattern = re.compile(
|
|
r"ClientError -- \[\d+\]: An error occurred \({error_code}\) when calling the {operation_name} operation: {error_message}".format(
|
|
error_code=re.escape(error_code),
|
|
operation_name=re.escape(operation_name),
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|
|
|
|
def test_check_region_security_hub_generic_exception(self, caplog):
|
|
caplog.set_level(WARNING)
|
|
|
|
with patch("boto3.Session.client") as mock_client:
|
|
error_message = "Generic exception occurred"
|
|
mock_client.side_effect = Exception(error_message)
|
|
|
|
mock_session = session.Session(region_name=AWS_REGION_EU_WEST_1)
|
|
|
|
region, client = SecurityHub._check_region_security_hub(
|
|
region=AWS_REGION_EU_WEST_1,
|
|
session=mock_session,
|
|
aws_account_id=AWS_ACCOUNT_NUMBER,
|
|
aws_partition=AWS_COMMERCIAL_PARTITION,
|
|
)
|
|
|
|
assert region == AWS_REGION_EU_WEST_1
|
|
assert client is None
|
|
|
|
# Check that error was logged for generic exception
|
|
log_pattern = re.compile(
|
|
r"Exception -- \[\d+\]: {error_message}".format(
|
|
error_message=re.escape(error_message),
|
|
)
|
|
)
|
|
assert any(
|
|
log_pattern.match(record.message) for record in caplog.records
|
|
), "Expected log message not found"
|