Files
prowler/tests/providers/aws/aws_provider_test.py
Pedro Martín 8d1d041092 chore(aws): support new eusc partition (#9649)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2025-12-23 12:28:10 +01:00

2080 lines
76 KiB
Python

import json
import os
import re
import tempfile
from datetime import datetime, timedelta
from json import dumps
from re import search
from unittest import mock
import botocore
import botocore.exceptions
import pytest
from boto3 import client, resource, session
from mock import patch
from moto import mock_aws
from pytest import raises
from tzlocal import get_localzone
from prowler.providers.aws.aws_provider import AwsProvider, get_aws_region_for_sts
from prowler.providers.aws.config import (
AWS_STS_GLOBAL_ENDPOINT_REGION,
BOTO3_USER_AGENT_EXTRA,
ROLE_SESSION_NAME,
)
from prowler.providers.aws.exceptions.exceptions import (
AWSArgumentTypeValidationError,
AWSIAMRoleARNInvalidResourceTypeError,
AWSInvalidPartitionError,
AWSInvalidProviderIdError,
AWSNoCredentialsError,
)
from prowler.providers.aws.lib.arn.models import ARN
from prowler.providers.aws.lib.mutelist.mutelist import AWSMutelist
from prowler.providers.aws.models import (
AWSAssumeRoleInfo,
AWSCallerIdentity,
AWSCredentials,
AWSMFAInfo,
AWSOrganizationsInfo,
)
from prowler.providers.common.models import Connection
from prowler.providers.common.provider import Provider
from tests.providers.aws.utils import (
AWS_ACCOUNT_ARN,
AWS_ACCOUNT_NUMBER,
AWS_CHINA_PARTITION,
AWS_COMMERCIAL_PARTITION,
AWS_EUSC_PARTITION,
AWS_GOV_CLOUD_ACCOUNT_ARN,
AWS_GOV_CLOUD_PARTITION,
AWS_ISO_PARTITION,
AWS_REGION_CN_NORTH_1,
AWS_REGION_CN_NORTHWEST_1,
AWS_REGION_EU_CENTRAL_1,
AWS_REGION_EU_WEST_1,
AWS_REGION_EUSC_DE_EAST_1,
AWS_REGION_GOV_CLOUD_US_EAST_1,
AWS_REGION_ISO_GLOBAL,
AWS_REGION_US_EAST_1,
AWS_REGION_US_EAST_2,
EXAMPLE_AMI_ID,
create_role,
set_mocked_aws_provider,
)
make_api_call = botocore.client.BaseClient._make_api_call
def mock_get_caller_identity_china(self, operation_name, kwarg):
if operation_name == "GetCallerIdentity":
return {
"UserId": "XXXXXXXXXXXXXXXXXXXXX",
"Account": AWS_ACCOUNT_NUMBER,
"Arn": f"arn:{AWS_CHINA_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:user/test-user",
}
return make_api_call(self, operation_name, kwarg)
def mock_get_caller_identity_gov_cloud(self, operation_name, kwarg):
if operation_name == "GetCallerIdentity":
return {
"UserId": "XXXXXXXXXXXXXXXXXXXXX",
"Account": AWS_ACCOUNT_NUMBER,
"Arn": f"arn:{AWS_GOV_CLOUD_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:user/test-user",
}
return make_api_call(self, operation_name, kwarg)
def mock_recover_checks_from_aws_provider(*_):
return [
(
"accessanalyzer_enabled_without_findings",
"/root_dir/fake_path/accessanalyzer/accessanalyzer_enabled_without_findings",
),
(
"awslambda_function_url_cors_policy",
"/root_dir/fake_path/awslambda/awslambda_function_url_cors_policy",
),
(
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"/root_dir/fake_path/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port",
),
]
def mock_recover_checks_from_aws_provider_lambda_service(*_):
return [
(
"awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
"/root_dir/fake_path/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
),
(
"awslambda_function_url_cors_policy",
"/root_dir/fake_path/awslambda/awslambda_function_url_cors_policy",
),
(
"awslambda_function_no_secrets_in_code",
"/root_dir/fake_path/awslambda/awslambda_function_no_secrets_in_code",
),
]
def mock_recover_checks_from_aws_provider_elb_service(*_):
return [
(
"elb_insecure_ssl_ciphers",
"/root_dir/fake_path/elb/elb_insecure_ssl_ciphers",
),
(
"elb_internet_facing",
"/root_dir/fake_path/elb/elb_internet_facing",
),
(
"elb_logging_enabled",
"/root_dir/fake_path/elb/elb_logging_enabled",
),
]
def mock_recover_checks_from_aws_provider_efs_service(*_):
return [
(
"efs_encryption_at_rest_enabled",
"/root_dir/fake_path/efs/efs_encryption_at_rest_enabled",
),
(
"efs_have_backup_enabled",
"/root_dir/fake_path/efs/efs_have_backup_enabled",
),
(
"efs_not_publicly_accessible",
"/root_dir/fake_path/efs/efs_not_publicly_accessible",
),
]
def mock_recover_checks_from_aws_provider_iam_service(*_):
return [
(
"iam_customer_attached_policy_no_administrative_privileges",
"/root_dir/fake_path/iam/iam_customer_attached_policy_no_administrative_privileges",
),
(
"iam_check_saml_providers_sts",
"/root_dir/fake_path/iam/iam_check_saml_providers_sts",
),
(
"iam_password_policy_minimum_length_14",
"/root_dir/fake_path/iam/iam_password_policy_minimum_length_14",
),
]
def mock_recover_checks_from_aws_provider_s3_service(*_):
return [
(
"s3_account_level_public_access_blocks",
"/root_dir/fake_path/s3/s3_account_level_public_access_blocks",
),
(
"s3_bucket_acl_prohibited",
"/root_dir/fake_path/s3/s3_bucket_acl_prohibited",
),
(
"s3_bucket_policy_public_write_access",
"/root_dir/fake_path/s3/s3_bucket_policy_public_write_access",
),
]
def mock_recover_checks_from_aws_provider_cloudwatch_service(*_):
return [
(
"cloudwatch_changes_to_network_acls_alarm_configured",
"/root_dir/fake_path/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured",
),
(
"cloudwatch_changes_to_network_gateways_alarm_configured",
"/root_dir/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured",
),
(
"cloudwatch_changes_to_network_route_tables_alarm_configured",
"/root_dir/fake_path/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured",
),
]
def mock_recover_checks_from_aws_provider_ec2_service(*_):
return [
(
"ec2_securitygroup_allow_ingress_from_internet_to_any_port",
"/root_dir/fake_path/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port",
),
(
"ec2_networkacl_allow_ingress_any_port",
"/root_dir/fake_path/ec2/ec2_networkacl_allow_ingress_any_port",
),
(
"ec2_ami_public",
"/root_dir/fake_path/ec2/ec2_ami_public",
),
]
def mock_recover_checks_from_aws_provider_rds_service(*_):
return [
(
"rds_instance_backup_enabled",
"/root_dir/fake_path/rds/rds_instance_backup_enabled",
),
(
"rds_instance_deletion_protection",
"/root_dir/fake_path/rds/rds_instance_deletion_protection",
),
(
"rds_snapshots_public_access",
"/root_dir/fake_path/rds/rds_snapshots_public_access",
),
]
def mock_recover_checks_from_aws_provider_cognito_service(*_):
return []
def mock_recover_checks_from_aws_provider_eks_service(*_):
return [
(
"eks_cluster_not_publicly_accessible",
"/root_dir/fake_path/eks/eks_cluster_not_publicly_accessible",
),
(
"eks_cluster_uses_a_supported_version",
"/root_dir/fake_path/eks/eks_cluster_uses_a_supported_version",
),
(
"eks_cluster_network_policy_enabled",
"/root_dir/fake_path/eks/eks_cluster_network_policy_enabled",
),
(
"eks_control_plane_logging_all_types_enabled",
"/root_dir/fake_path/eks/eks_control_plane_logging_all_types_enabled",
),
]
class TestAWSProvider:
@mock_aws
def test_aws_provider_default(self):
mfa = False
scan_unused_services = True
aws_provider = AwsProvider(
mfa=mfa,
scan_unused_services=scan_unused_services,
)
assert aws_provider.type == "aws"
assert aws_provider.scan_unused_services is True
assert aws_provider.audit_config
assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
@mock_aws
def test_aws_provider_with_static_credentials(self):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
username = "test-user"
iam_user = iam_client.create_user(UserName=username)["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
credentials = {
"aws_access_key_id": access_key["AccessKeyId"],
"aws_secret_access_key": access_key["SecretAccessKey"],
}
aws_provider = AwsProvider(**credentials)
assert aws_provider.type == "aws"
# Session
assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
assert aws_provider.session.current_session.profile_name == "default"
assert aws_provider.session.original_session.region_name == AWS_REGION_US_EAST_1
assert aws_provider.session.original_session.profile_name == "default"
# Identity
assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
assert (
aws_provider.identity.identity_arn
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/{username}"
)
assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
assert aws_provider.identity.profile is None
assert aws_provider.identity.profile_region == AWS_REGION_US_EAST_1
@mock_aws
def test_aws_provider_with_session_credentials(self):
sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
session_token = sts_client.get_session_token()
session_credentials = {
"aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
"aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
"aws_session_token": session_token["Credentials"]["SessionToken"],
}
aws_provider = AwsProvider(**session_credentials)
assert aws_provider.type == "aws"
# Session
assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
assert aws_provider.session.current_session.profile_name == "default"
assert aws_provider.session.original_session.region_name == AWS_REGION_US_EAST_1
assert aws_provider.session.original_session.profile_name == "default"
# Identity
assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
# moto is the default user created by moto
assert (
aws_provider.identity.identity_arn
== f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:user/moto"
)
assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
assert aws_provider.identity.profile is None
assert aws_provider.identity.profile_region == AWS_REGION_US_EAST_1
@mock_aws
def test_aws_provider_organizations_delegated_administrator(self):
organizations_client = client("organizations", region_name=AWS_REGION_EU_WEST_1)
organization = organizations_client.create_organization()["Organization"]
organizations_client.tag_resource(
ResourceId=AWS_ACCOUNT_NUMBER,
Tags=[
{"Key": "tagged", "Value": "true"},
],
)
aws_provider = AwsProvider()
assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
assert aws_provider.organizations_metadata.account_email == "master@example.com"
assert aws_provider.organizations_metadata.account_name == "master"
assert aws_provider.organizations_metadata.account_tags == {"tagged": "true"}
assert (
aws_provider.organizations_metadata.organization_account_arn
== f"arn:aws:organizations::{AWS_ACCOUNT_NUMBER}:account/{organization['Id']}/{AWS_ACCOUNT_NUMBER}"
)
assert aws_provider.organizations_metadata.organization_id == organization["Id"]
assert (
aws_provider.organizations_metadata.organization_arn == organization["Arn"]
)
@mock_aws
def test_aws_provider_organizations_none_organizations_metadata(self):
aws_provider = AwsProvider()
assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
assert aws_provider.organizations_metadata.account_email == ""
assert aws_provider.organizations_metadata.account_name == ""
assert aws_provider.organizations_metadata.account_tags == []
assert aws_provider.organizations_metadata.organization_account_arn == ""
assert aws_provider.organizations_metadata.organization_id == ""
assert aws_provider.organizations_metadata.organization_arn == ""
@mock_aws
def test_aws_provider_organizations_with_role(self):
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
policy_name = "describe_organizations_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"organizations:DescribeAccount",
"organizations:ListTagsForResource",
],
"Resource": "*",
},
],
}
policy = iam_client.create_policy(
PolicyName=policy_name,
PolicyDocument=dumps(policy_document),
)["Policy"]
assume_policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
"Action": "sts:AssumeRole",
}
],
}
role_name = "organizations_role"
organizations_role = iam_client.create_role(
RoleName=role_name, AssumeRolePolicyDocument=dumps(assume_policy_document)
)["Role"]
iam_client.attach_role_policy(
RoleName=role_name,
PolicyArn=policy["Arn"],
)
organizations_client = client("organizations", region_name=AWS_REGION_EU_WEST_1)
organization = organizations_client.create_organization()["Organization"]
organizations_client.tag_resource(
ResourceId=AWS_ACCOUNT_NUMBER,
Tags=[
{"Key": "tagged", "Value": "true"},
],
)
organizations_role = organizations_role["Arn"]
session_duration = 900
aws_provider = AwsProvider(
organizations_role_arn=organizations_role,
session_duration=session_duration,
)
assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
assert aws_provider.organizations_metadata.account_email == "master@example.com"
assert aws_provider.organizations_metadata.account_name == "master"
assert aws_provider.organizations_metadata.account_tags == {"tagged": "true"}
assert (
aws_provider.organizations_metadata.organization_account_arn
== f"arn:aws:organizations::{AWS_ACCOUNT_NUMBER}:account/{organization['Id']}/{AWS_ACCOUNT_NUMBER}"
)
assert aws_provider.organizations_metadata.organization_id == organization["Id"]
assert (
aws_provider.organizations_metadata.organization_arn == organization["Arn"]
)
@mock_aws
def test_aws_provider_session_with_mfa(self):
mfa = True
with patch(
"prowler.providers.aws.aws_provider.AwsProvider.input_role_mfa_token_and_code",
return_value=AWSMFAInfo(
arn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/test-role-mfa",
totp="111111",
),
):
aws_provider = AwsProvider(mfa=mfa)
assert aws_provider.type == "aws"
assert aws_provider.scan_unused_services is False
assert aws_provider.audit_config != {}
assert (
aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
)
assert (
aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
)
@mock_aws
def test_aws_provider_assume_role_with_mfa(self):
# Variables
mfa = True
role_name = "test-role"
role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
session_duration = 900
role_session_name = "ProwlerAssessmentSession"
external_id = "test-external-id"
with patch(
"prowler.providers.aws.aws_provider.AwsProvider.input_role_mfa_token_and_code",
return_value=AWSMFAInfo(
arn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/test-role-mfa",
totp="111111",
),
):
aws_provider = AwsProvider(
mfa=mfa,
role_arn=role_arn,
session_duration=session_duration,
role_session_name=role_session_name,
external_id=external_id,
)
assert (
aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
)
assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
assert isinstance(
aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
)
assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
role_arn=ARN(arn=role_arn),
session_duration=session_duration,
external_id=external_id,
mfa_enabled=True, # <- MFA configuration
role_session_name=role_session_name,
sts_region=AWS_REGION_US_EAST_1,
)
credentials = aws_provider._assumed_role_configuration.credentials
assert isinstance(credentials, AWSCredentials)
assert credentials.aws_access_key_id
assert len(credentials.aws_access_key_id) == 20
assert search(r"^ASIA.*$", credentials.aws_access_key_id)
assert credentials.aws_session_token
assert len(credentials.aws_session_token) == 356
assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)
assert credentials.aws_secret_access_key
assert len(credentials.aws_secret_access_key) == 40
assert credentials.expiration
# assert credentials.expiration == datetime.now(tzinfo=tzutc())
@mock_aws
def test_aws_provider_assume_role_without_mfa(self):
# Variables
mfa = False
role_name = "test-role"
role_arn = (
f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
session_duration = 900
role_session_name = "ProwlerAssessmentSession"
aws_provider = AwsProvider(
mfa=mfa,
role_arn=role_arn,
session_duration=session_duration,
role_session_name=role_session_name,
)
assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
assert isinstance(
aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
)
assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
role_arn=ARN(arn=role_arn),
session_duration=session_duration,
external_id=None,
mfa_enabled=False, # <- MFA configuration
role_session_name=role_session_name,
sts_region=AWS_REGION_US_EAST_1,
)
credentials = aws_provider._assumed_role_configuration.credentials
assert isinstance(credentials, AWSCredentials)
assert credentials.aws_access_key_id
assert len(credentials.aws_access_key_id) == 20
assert search(r"^ASIA.*$", credentials.aws_access_key_id)
assert credentials.aws_session_token
assert len(credentials.aws_session_token) == 356
assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)
assert credentials.aws_secret_access_key
assert len(credentials.aws_secret_access_key) == 40
assert credentials.expiration
# assert credentials.expiration == datetime.now(tzinfo=tzutc())
@mock_aws
def test_aws_provider_assume_role_without_mfa_gov_cloud(self, monkeypatch):
# Set AWS_DEFAULT_REGION = 'us-gov-east-1' since is set by default to 'us-east-1
monkeypatch.setenv("AWS_DEFAULT_REGION", AWS_REGION_GOV_CLOUD_US_EAST_1)
# Variables
mfa = False
role_name = "test-role"
role_arn = (
f"arn:{AWS_GOV_CLOUD_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
session_duration = 900
role_session_name = "ProwlerAssessmentSession"
aws_provider = AwsProvider(
mfa=mfa,
role_arn=role_arn,
session_duration=session_duration,
role_session_name=role_session_name,
)
assert (
aws_provider.session.current_session.region_name
== AWS_REGION_GOV_CLOUD_US_EAST_1
)
assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
assert aws_provider.identity.account_arn == AWS_GOV_CLOUD_ACCOUNT_ARN
assert aws_provider.identity.partition == AWS_GOV_CLOUD_PARTITION
assert isinstance(
aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
)
assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
role_arn=ARN(arn=role_arn),
session_duration=session_duration,
external_id=None,
mfa_enabled=False, # <- MFA configuration
role_session_name=role_session_name,
sts_region=AWS_REGION_GOV_CLOUD_US_EAST_1,
)
credentials = aws_provider._assumed_role_configuration.credentials
assert isinstance(credentials, AWSCredentials)
assert credentials.aws_access_key_id
assert len(credentials.aws_access_key_id) == 20
assert search(r"^ASIA.*$", credentials.aws_access_key_id)
assert credentials.aws_session_token
assert len(credentials.aws_session_token) == 356
assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)
assert credentials.aws_secret_access_key
assert len(credentials.aws_secret_access_key) == 40
assert credentials.expiration
# assert credentials.expiration == datetime.now(tzinfo=tzutc())
@mock_aws
def test_aws_provider_config(self):
config = """
aws:
test_key: value"""
config_file_input = tempfile.NamedTemporaryFile(delete=False)
config_file_input.write(bytes(config, encoding="raw_unicode_escape"))
config_file_input.close()
config_file_input = config_file_input.name
aws_provider = AwsProvider(
config_path=config_file_input,
)
os.remove(config_file_input)
assert aws_provider.audit_config == {"test_key": "value"}
@mock_aws
def test_aws_provider_mutelist(self):
mutelist = {
"Mutelist": {
"Accounts": {
AWS_ACCOUNT_NUMBER: {
"Checks": {
"test-check": {
"Regions": [],
"Resources": [],
"Tags": [],
"Exceptions": {
"Accounts": [],
"Regions": [],
"Resources": [],
"Tags": [],
},
}
}
}
}
}
}
mutelist_file = tempfile.NamedTemporaryFile(delete=False)
with open(mutelist_file.name, "w") as mutelist_file:
mutelist_file.write(json.dumps(mutelist, indent=4))
aws_provider = AwsProvider(mutelist_path=mutelist_file.name)
os.remove(mutelist_file.name)
assert isinstance(aws_provider.mutelist, AWSMutelist)
assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
assert aws_provider.mutelist.mutelist_file_path == mutelist_file.name
@mock_aws
def test_aws_provider_mutelist_none(self):
with patch(
"prowler.providers.aws.aws_provider.get_default_mute_file_path",
return_value=None,
):
aws_provider = AwsProvider(mutelist_path=None)
assert isinstance(aws_provider.mutelist, AWSMutelist)
assert aws_provider.mutelist.mutelist == {}
assert aws_provider.mutelist.mutelist_file_path is None
@mock_aws
def test_aws_provider_mutelist_s3(self):
# Create mutelist temp file
mutelist = {
"Mutelist": {
"Accounts": {
AWS_ACCOUNT_NUMBER: {
"Checks": {
"test-check": {
"Regions": [],
"Resources": [],
"Tags": [],
"Exceptions": {
"Accounts": [],
"Regions": [],
"Resources": [],
"Tags": [],
},
}
}
}
}
}
}
mutelist_file = tempfile.NamedTemporaryFile(delete=False)
with open(mutelist_file.name, "w") as mutelist_file:
mutelist_file.write(json.dumps(mutelist, indent=4))
# Create bucket and upload mutelist yaml
s3_resource = resource("s3", region_name=AWS_REGION_US_EAST_1)
bucket_name = "test-mutelist"
mutelist_file_name = "mutelist.yaml"
mutelist_bucket_object_uri = f"s3://{bucket_name}/{mutelist_file_name}"
s3_resource.create_bucket(Bucket=bucket_name)
s3_resource.Object(bucket_name, "mutelist.yaml").put(
Body=open(
mutelist_file.name,
"rb",
)
)
aws_provider = AwsProvider(mutelist_path=mutelist_bucket_object_uri)
os.remove(mutelist_file.name)
assert isinstance(aws_provider.mutelist, AWSMutelist)
assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
assert aws_provider.mutelist.mutelist_file_path == mutelist_bucket_object_uri
@mock_aws
def test_aws_provider_mutelist_lambda(self):
# Create mutelist temp file
mutelist = {
"Mutelist": {
"Accounts": {
AWS_ACCOUNT_NUMBER: {
"Checks": {
"test-check": {
"Regions": [],
"Resources": [],
"Tags": [],
"Exceptions": {
"Accounts": [],
"Regions": [],
"Resources": [],
"Tags": [],
},
}
}
}
}
}
}
lambda_mutelist_path = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function:lambda-mutelist"
aws_provider = AwsProvider()
with patch(
"prowler.providers.aws.lib.mutelist.mutelist.AWSMutelist.get_mutelist_file_from_lambda",
return_value=mutelist["Mutelist"],
):
aws_provider = AwsProvider(mutelist_path=lambda_mutelist_path)
assert isinstance(aws_provider.mutelist, AWSMutelist)
assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
assert aws_provider.mutelist.mutelist_file_path == lambda_mutelist_path
@mock_aws
def test_aws_provider_mutelist_dynamodb(self):
# Create mutelist temp file
mutelist = {
"Mutelist": {
"Accounts": {
AWS_ACCOUNT_NUMBER: {
"Checks": {
"test-check": {
"Regions": [],
"Resources": [],
"Tags": [],
"Exceptions": {
"Accounts": [],
"Regions": [],
"Resources": [],
"Tags": [],
},
}
}
}
}
}
}
dynamodb_mutelist_path = f"arn:aws:dynamodb:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:table/mutelist-dynamo"
aws_provider = AwsProvider()
with patch(
"prowler.providers.aws.lib.mutelist.mutelist.AWSMutelist.get_mutelist_file_from_dynamodb",
return_value=mutelist["Mutelist"],
):
aws_provider = AwsProvider(mutelist_path=dynamodb_mutelist_path)
assert isinstance(aws_provider.mutelist, AWSMutelist)
assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
assert aws_provider.mutelist.mutelist_file_path == dynamodb_mutelist_path
@mock_aws
def test_empty_input_regions_in_arguments(self):
aws_provider = AwsProvider(regions=None)
assert isinstance(aws_provider, AwsProvider)
@mock_aws
def test_generate_regional_clients_all_enabled_regions(self):
aws_provider = AwsProvider()
response = aws_provider.generate_regional_clients("ec2")
assert len(response.keys()) == 33
@mock_aws
def test_generate_regional_clients_with_enabled_regions(self):
aws_provider = AwsProvider()
enabled_regions = [AWS_REGION_EU_WEST_1]
aws_provider._enabled_regions = enabled_regions
response = aws_provider.generate_regional_clients("ec2")
assert list(response.keys()) == enabled_regions
@mock_aws
def test_generate_regional_clients_with_enabled_regions_and_input_regions(self):
region = [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
aws_provider = AwsProvider(
regions=region,
)
enabled_regions = [AWS_REGION_EU_WEST_1]
aws_provider._enabled_regions = enabled_regions
response = aws_provider.generate_regional_clients("ec2")
assert list(response.keys()) == enabled_regions
@mock_aws
def test_generate_regional_clients_cn_partition(self):
region = [AWS_REGION_CN_NORTH_1, AWS_REGION_CN_NORTHWEST_1]
aws_provider = AwsProvider(
regions=region,
)
response = aws_provider.generate_regional_clients("ec2")
assert AWS_REGION_CN_NORTH_1 in response.keys()
assert AWS_REGION_CN_NORTHWEST_1 in response.keys()
@mock_aws
def test_generate_regional_clients_cn_partition_not_present_service(self):
region = ["cn-northwest-1", "cn-north-1"]
aws_provider = AwsProvider(
regions=region,
)
response = aws_provider.generate_regional_clients("shield")
assert response == {}
@mock_aws
def test_get_default_region(self):
region = [AWS_REGION_EU_WEST_1]
aws_provider = AwsProvider(
regions=region,
)
aws_provider._identity.profile_region = AWS_REGION_EU_WEST_1
assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1
@mock_aws
def test_get_default_region_profile_region_not_audited(self):
region = [AWS_REGION_EU_WEST_1]
aws_provider = AwsProvider(
regions=region,
)
aws_provider._identity.profile_region = AWS_REGION_US_EAST_2
assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1
@mock_aws
def test_get_default_region_non_profile_region(self):
region = [AWS_REGION_EU_WEST_1]
aws_provider = AwsProvider(
regions=region,
)
aws_provider._identity.profile_region = None
assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1
@mock_aws
def test_get_default_region_non_profile_or_audited_region(self):
aws_provider = AwsProvider()
aws_provider._identity.profile_region = None
assert aws_provider.get_default_region("ec2") == AWS_REGION_US_EAST_1
@mock_aws
def test_get_default_region_profile_region_not_present_in_service(self):
region = [AWS_REGION_EU_WEST_1]
aws_provider = AwsProvider(
regions=region,
)
aws_provider._identity.profile_region = "non-existent-region"
assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1
@mock_aws
def test_aws_gov_get_global_region(self):
aws_provider = AwsProvider()
aws_provider._identity.partition = AWS_GOV_CLOUD_PARTITION
assert aws_provider.get_global_region() == AWS_REGION_GOV_CLOUD_US_EAST_1
@mock_aws
def test_aws_cn_get_global_region(self):
aws_provider = AwsProvider()
aws_provider._identity.partition = AWS_CHINA_PARTITION
assert aws_provider.get_global_region() == AWS_REGION_CN_NORTH_1
@mock_aws
def test_aws_iso_get_global_region(self):
aws_provider = AwsProvider()
aws_provider._identity.partition = AWS_ISO_PARTITION
assert aws_provider.get_global_region() == AWS_REGION_ISO_GLOBAL
@mock_aws
def test_aws_eusc_get_global_region(self):
aws_provider = AwsProvider()
aws_provider._identity.partition = AWS_EUSC_PARTITION
assert aws_provider.get_global_region() == AWS_REGION_EUSC_DE_EAST_1
@mock_aws
def test_get_available_aws_service_regions_with_us_east_1_audited(self):
region = [AWS_REGION_US_EAST_1]
aws_provider = AwsProvider(
regions=region,
)
with patch(
"prowler.providers.aws.aws_provider.parse_json_file",
return_value={
"services": {
"ec2": {
"regions": {
"aws": [
"af-south-1",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-south-2",
AWS_REGION_EU_WEST_1,
"eu-west-2",
"eu-west-3",
"me-central-1",
"me-south-1",
"sa-east-1",
AWS_REGION_US_EAST_1,
"us-east-2",
"us-west-1",
"us-west-2",
],
}
}
}
},
):
assert aws_provider.get_available_aws_service_regions(
"ec2", "aws", {AWS_REGION_US_EAST_1}
) == {AWS_REGION_US_EAST_1}
@mock_aws
def test_get_available_aws_service_regions_with_all_regions_audited(self):
aws_provider = AwsProvider()
with patch(
"prowler.providers.aws.aws_provider.parse_json_file",
return_value={
"services": {
"ec2": {
"regions": {
"aws": [
"af-south-1",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-south-2",
AWS_REGION_EU_WEST_1,
"eu-west-2",
"eu-west-3",
"me-central-1",
"me-south-1",
"sa-east-1",
AWS_REGION_US_EAST_1,
"us-east-2",
"us-west-1",
"us-west-2",
],
}
}
}
},
):
assert (
len(aws_provider.get_available_aws_service_regions("ec2", "aws")) == 17
)
@mock_aws
def test_get_tagged_resources(self):
ec2_client = client("ec2", region_name=AWS_REGION_EU_CENTRAL_1)
instances = ec2_client.run_instances(
ImageId=EXAMPLE_AMI_ID,
MinCount=1,
MaxCount=1,
InstanceType="t2.micro",
TagSpecifications=[
{
"ResourceType": "instance",
"Tags": [
{"Key": "MY_TAG1", "Value": "MY_VALUE1"},
{"Key": "MY_TAG2", "Value": "MY_VALUE2"},
],
},
{
"ResourceType": "instance",
"Tags": [{"Key": "ami", "Value": "test"}],
},
],
)
instance_id = instances["Instances"][0]["InstanceId"]
instance_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:instance/{instance_id}"
image_id = ec2_client.create_image(Name="testami", InstanceId=instance_id)[
"ImageId"
]
image_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:image/{image_id}"
ec2_client.create_tags(
Resources=[image_id], Tags=[{"Key": "ami", "Value": "test"}]
)
# Through the AWS provider
region = [AWS_REGION_EU_CENTRAL_1]
resource_tags = ["ami=test"]
aws_provider = AwsProvider(
regions=region,
resource_tags=resource_tags,
)
tagged_resources = aws_provider.audit_resources
assert len(tagged_resources) == 2
assert image_arn in tagged_resources
assert instance_arn in tagged_resources
# Calling directly the function
tagged_resources = aws_provider.get_tagged_resources(["MY_TAG1=MY_VALUE1"])
assert len(tagged_resources) == 1
assert instance_arn in tagged_resources
@mock_aws
def test_aws_provider_resource_tags(self):
resource_arn = [AWS_ACCOUNT_ARN]
aws_provider = AwsProvider(
resource_arn=resource_arn,
)
assert aws_provider.audit_resources == [AWS_ACCOUNT_ARN]
@mock_aws
def test_validate_credentials_commercial_partition_with_regions(self):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
iam_user = iam_client.create_user(UserName="test-user")["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
access_key_id = access_key["AccessKeyId"]
secret_access_key = access_key["SecretAccessKey"]
# Create AWS session to validate
current_session = session.Session(
aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name=AWS_REGION_EU_WEST_1,
)
get_caller_identity = AwsProvider.validate_credentials(
session=current_session, aws_region=AWS_REGION_EU_WEST_1
)
assert isinstance(get_caller_identity, AWSCallerIdentity)
assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
assert get_caller_identity.region == AWS_REGION_EU_WEST_1
assert isinstance(get_caller_identity.arn, ARN)
assert get_caller_identity.arn.partition == AWS_COMMERCIAL_PARTITION
assert get_caller_identity.arn.region is None
assert get_caller_identity.arn.resource == "test-user"
assert get_caller_identity.arn.resource_type == "user"
@mock_aws
@patch(
"botocore.client.BaseClient._make_api_call", new=mock_get_caller_identity_china
)
def test_validate_credentials_china_partition(self):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_CN_NORTH_1)
iam_user = iam_client.create_user(UserName="test-user")["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
access_key_id = access_key["AccessKeyId"]
secret_access_key = access_key["SecretAccessKey"]
# Create AWS session to validate
current_session = session.Session(
aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name=AWS_REGION_CN_NORTH_1,
)
# To use GovCloud or China it is either required:
# - Set the AWS profile region with a valid partition region
# - Use the -f/--region with a valid partition region
get_caller_identity = AwsProvider.validate_credentials(
session=current_session, aws_region=AWS_REGION_CN_NORTH_1
)
assert isinstance(get_caller_identity, AWSCallerIdentity)
assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
assert get_caller_identity.region == AWS_REGION_CN_NORTH_1
assert isinstance(get_caller_identity.arn, ARN)
assert get_caller_identity.arn.partition == AWS_CHINA_PARTITION
assert get_caller_identity.arn.region is None
assert get_caller_identity.arn.resource == "test-user"
assert get_caller_identity.arn.resource_type == "user"
@mock_aws
@patch(
"botocore.client.BaseClient._make_api_call",
new=mock_get_caller_identity_gov_cloud,
)
def test_validate_credentials_gov_cloud_partition(self):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_GOV_CLOUD_US_EAST_1)
iam_user = iam_client.create_user(UserName="test-user")["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
access_key_id = access_key["AccessKeyId"]
secret_access_key = access_key["SecretAccessKey"]
# Create AWS session to validate
current_session = session.Session(
aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name=AWS_REGION_GOV_CLOUD_US_EAST_1,
)
# To use GovCloud or China it is either required:
# - Set the AWS profile region with a valid partition region
# - Use the -f/--region with a valid partition region
get_caller_identity = AwsProvider.validate_credentials(
session=current_session, aws_region=AWS_REGION_GOV_CLOUD_US_EAST_1
)
assert isinstance(get_caller_identity, AWSCallerIdentity)
assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
assert get_caller_identity.region == AWS_REGION_GOV_CLOUD_US_EAST_1
assert isinstance(get_caller_identity.arn, ARN)
assert get_caller_identity.arn.partition == AWS_GOV_CLOUD_PARTITION
assert get_caller_identity.arn.region is None
assert get_caller_identity.arn.resource == "test-user"
assert get_caller_identity.arn.resource_type == "user"
@mock_aws
def test_test_connection_with_env_credentials(self, monkeypatch):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
iam_user = iam_client.create_user(UserName="test-user")["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
monkeypatch.delenv("AWS_ACCESS_KEY_ID")
monkeypatch.delenv("AWS_SECRET_ACCESS_KEY")
monkeypatch.setenv("AWS_ACCESS_KEY_ID", access_key["AccessKeyId"])
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", access_key["SecretAccessKey"])
connection = AwsProvider.test_connection()
assert isinstance(connection, Connection)
assert connection.is_connected
assert connection.error is None
def test_test_connection_without_credentials(self):
with (
mock.patch("boto3.Session.get_credentials", return_value=None),
mock.patch("botocore.session.Session.get_scoped_config", return_value={}),
mock.patch("botocore.credentials.EnvProvider.load", return_value=None),
mock.patch(
"botocore.credentials.SharedCredentialProvider.load", return_value=None
),
mock.patch(
"botocore.credentials.InstanceMetadataProvider.load", return_value=None
),
mock.patch.dict(
"os.environ",
{
"AWS_ACCESS_KEY_ID": "",
"AWS_SECRET_ACCESS_KEY": "",
"AWS_SESSION_TOKEN": "",
"AWS_PROFILE": "",
},
clear=True,
),
):
with raises(AWSNoCredentialsError) as exception:
AwsProvider.test_connection(
profile=None
) # No profile to avoid ProfileNotFound error
assert exception.type == AWSNoCredentialsError
assert "AWSNoCredentialsError[1002]: No AWS credentials found" in str(
exception.value
)
@mock_aws
def test_test_connection_with_role_from_env(self, monkeypatch):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
iam_user = iam_client.create_user(UserName="test-user")["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
monkeypatch.setenv("AWS_ACCESS_KEY_ID", access_key["AccessKeyId"])
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", access_key["SecretAccessKey"])
role_name = "test-role"
role_arn = (
f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
connection = AwsProvider.test_connection(
role_arn=role_arn, role_session_name=ROLE_SESSION_NAME
)
assert isinstance(connection, Connection)
assert connection.is_connected
assert connection.error is None
@mock_aws
def test_test_connection_with_role_from_env_invalid_session_duration(self):
role_name = "test-role"
role_arn = (
f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
with raises(AWSArgumentTypeValidationError) as exception:
AwsProvider.test_connection(role_arn=role_arn, session_duration=899)
assert exception.type == AWSArgumentTypeValidationError
assert (
exception.value.args[0]
== "[1003] Session Duration must be between 900 and 43200 seconds."
)
@mock_aws
def test_test_connection_with_role_from_env_invalid_session_duration_not_raise(
self,
):
role_name = "test-role"
role_arn = (
f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
connection = AwsProvider.test_connection(
role_arn=role_arn, session_duration=899, raise_on_exception=False
)
assert isinstance(connection, Connection)
assert not connection.is_connected
assert isinstance(connection.error, AWSArgumentTypeValidationError)
assert (
connection.error.args[0]
== "[1003] Session Duration must be between 900 and 43200 seconds."
)
@mock_aws
def test_test_connection_with_role_from_env_invalid_session_name(self):
role_name = "test-role"
role_arn = (
f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
)
with raises(AWSArgumentTypeValidationError) as exception:
AwsProvider.test_connection(role_arn=role_arn, role_session_name="???")
assert exception.type == AWSArgumentTypeValidationError
assert (
exception.value.args[0]
== "[1003] Role Session Name must be between 2 and 64 characters and may contain alphanumeric characters, periods, hyphens, and underscores."
)
@mock_aws
def test_test_connection_with_role_from_env_invalid_role_arn(self):
role_name = "test-role"
role_arn = f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:not-role/{role_name}"
with raises(AWSIAMRoleARNInvalidResourceTypeError) as exception:
AwsProvider.test_connection(role_arn=role_arn)
assert exception.type == AWSIAMRoleARNInvalidResourceTypeError
assert (
exception.value.args[0]
== "[1010] AWS IAM Role ARN resource type is invalid"
)
@mock_aws
def test_test_connection_with_static_credentials(self):
# Create a mock IAM user
iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
username = "test-user"
iam_user = iam_client.create_user(UserName=username)["User"]
# Create a mock IAM access keys
access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
"AccessKey"
]
credentials = {
"aws_access_key_id": access_key["AccessKeyId"],
"aws_secret_access_key": access_key["SecretAccessKey"],
}
connection = AwsProvider.test_connection(**credentials)
assert isinstance(connection, Connection)
assert connection.is_connected
assert connection.error is None
@mock_aws
def test_test_connection_with_session_credentials(self):
sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
session_token = sts_client.get_session_token()
session_credentials = {
"aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
"aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
"aws_session_token": session_token["Credentials"]["SessionToken"],
}
connection = AwsProvider.test_connection(**session_credentials)
assert isinstance(connection, Connection)
assert connection.is_connected
assert connection.error is None
@mock_aws
def test_test_connection_with_own_account(self):
sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
session_token = sts_client.get_session_token()
session_credentials = {
"aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
"aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
"aws_session_token": session_token["Credentials"]["SessionToken"],
"provider_id": AWS_ACCOUNT_NUMBER,
}
connection = AwsProvider.test_connection(**session_credentials)
assert isinstance(connection, Connection)
assert connection.is_connected
assert connection.error is None
@mock_aws
def test_test_connection_with_different_account(self):
sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
session_token = sts_client.get_session_token()
session_credentials = {
"aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
"aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
"aws_session_token": session_token["Credentials"]["SessionToken"],
"provider_id": "111122223333",
}
with raises(AWSInvalidProviderIdError) as exception:
AwsProvider.test_connection(**session_credentials)
assert exception.type == AWSInvalidProviderIdError
assert (
exception.value.args[0]
== "[1015] The provided AWS credentials belong to a different account"
)
@mock_aws
def test_test_connection_with_different_account_dont_raise(self):
sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
session_token = sts_client.get_session_token()
session_credentials = {
"aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
"aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
"aws_session_token": session_token["Credentials"]["SessionToken"],
"provider_id": "111122223333",
}
connection = AwsProvider.test_connection(
**session_credentials, raise_on_exception=False
)
assert isinstance(connection, Connection)
assert not connection.is_connected
assert isinstance(connection.error, AWSInvalidProviderIdError)
assert (
connection.error.message
== "The provided AWS credentials belong to a different account"
)
assert connection.error.code == 1015
@mock_aws
def test_test_connection_generic_exception(self):
with patch(
"prowler.providers.aws.aws_provider.AwsProvider.setup_session",
side_effect=Exception(),
):
connection = AwsProvider.test_connection(raise_on_exception=False)
assert isinstance(connection, Connection)
assert not connection.is_connected
assert isinstance(connection.error, Exception)
@mock_aws
def test_create_sts_session(self):
current_session = session.Session()
aws_region = AWS_REGION_US_EAST_1
sts_session = AwsProvider.create_sts_session(current_session, aws_region)
assert sts_session._service_model.service_name == "sts"
assert sts_session._client_config.region_name == aws_region
assert sts_session._endpoint._endpoint_prefix == "sts"
assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com"
@mock_aws
def test_create_sts_session_gov_cloud(self):
current_session = session.Session()
aws_region = AWS_REGION_GOV_CLOUD_US_EAST_1
sts_session = AwsProvider.create_sts_session(current_session, aws_region)
assert sts_session._service_model.service_name == "sts"
assert sts_session._client_config.region_name == aws_region
assert sts_session._endpoint._endpoint_prefix == "sts"
assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com"
@mock_aws
def test_create_sts_session_china(self):
current_session = session.Session()
aws_region = AWS_REGION_CN_NORTH_1
sts_session = AwsProvider.create_sts_session(current_session, aws_region)
assert sts_session._service_model.service_name == "sts"
assert sts_session._client_config.region_name == aws_region
assert sts_session._endpoint._endpoint_prefix == "sts"
assert (
sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com.cn"
)
@mock_aws
def test_create_sts_session_eusc(self):
current_session = session.Session()
aws_region = AWS_REGION_EUSC_DE_EAST_1
sts_session = AwsProvider.create_sts_session(current_session, aws_region)
assert sts_session._service_model.service_name == "sts"
assert sts_session._client_config.region_name == aws_region
assert sts_session._endpoint._endpoint_prefix == "sts"
assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.eu"
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_elb_service,
)
def test_get_checks_from_input_arn_elb(self):
expected_checks = [
"elb_insecure_ssl_ciphers",
"elb_internet_facing",
"elb_logging_enabled",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:elasticloadbalancing:us-east-1:{AWS_ACCOUNT_NUMBER}:loadbalancer/test"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_efs_service,
)
def test_get_checks_from_input_arn_efs(self):
expected_checks = [
"efs_encryption_at_rest_enabled",
"efs_have_backup_enabled",
"efs_not_publicly_accessible",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:elasticfilesystem:us-east-1:{AWS_ACCOUNT_NUMBER}:file-system/fs-01234567"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_lambda_service,
)
def test_get_checks_from_input_arn_lambda(self):
expected_checks = [
"awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
"awslambda_function_no_secrets_in_code",
"awslambda_function_url_cors_policy",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
"arn:aws:lambda:us-east-1:123456789:function:test-lambda"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_iam_service,
)
def test_get_checks_from_input_arn_iam(self):
expected_checks = [
"iam_check_saml_providers_sts",
"iam_customer_attached_policy_no_administrative_privileges",
"iam_password_policy_minimum_length_14",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/user-name"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_s3_service,
)
def test_get_checks_from_input_arn_s3(self):
expected_checks = [
"s3_account_level_public_access_blocks",
"s3_bucket_acl_prohibited",
"s3_bucket_policy_public_write_access",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = ["arn:aws:s3:::bucket-name"]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_cloudwatch_service,
)
def test_get_checks_from_input_arn_cloudwatch(self):
expected_checks = [
"cloudwatch_changes_to_network_acls_alarm_configured",
"cloudwatch_changes_to_network_gateways_alarm_configured",
"cloudwatch_changes_to_network_route_tables_alarm_configured",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:logs:us-east-1:{AWS_ACCOUNT_NUMBER}:destination:testDestination"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_eks_service,
)
def test_get_checks_from_input_arn_eks(self):
expected_checks = [
"eks_cluster_not_publicly_accessible",
"eks_cluster_uses_a_supported_version",
"eks_cluster_network_policy_enabled",
"eks_control_plane_logging_all_types_enabled",
]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:eks:us-east-1:{AWS_ACCOUNT_NUMBER}:cluster/test-eks"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert set(recovered_checks) == set(expected_checks)
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_cognito_service,
)
def test_get_checks_from_input_arn_cognito(self):
expected_checks = []
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:cognito-idp:us-east-1:{AWS_ACCOUNT_NUMBER}:userpool/test"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_ec2_service,
)
def test_get_checks_from_input_arn_ec2_security_group(self):
expected_checks = ["ec2_securitygroup_allow_ingress_from_internet_to_any_port"]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:ec2:us-east-1:{AWS_ACCOUNT_NUMBER}:security-group/sg-1111111111"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_ec2_service,
)
def test_get_checks_from_input_arn_ec2_acl(self):
expected_checks = ["ec2_networkacl_allow_ingress_any_port"]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:network-acl/acl-1"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_rds_service,
)
def test_get_checks_from_input_arn_rds_snapshots(self):
expected_checks = ["rds_snapshots_public_access"]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:rds:us-east-2:{AWS_ACCOUNT_NUMBER}:snapshot:rds:snapshot-1",
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_ec2_service,
)
def test_get_checks_from_input_arn_ec2_ami(self):
expected_checks = ["ec2_ami_public"]
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:image/ami-1"
]
recovered_checks = aws_provider.get_checks_from_input_arn()
assert recovered_checks == expected_checks
@mock_aws
def test_get_regions_from_audit_resources_with_regions(self):
audit_resources = [
f"arn:aws:lambda:us-east-1:{AWS_ACCOUNT_NUMBER}:function:test-lambda",
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:policy/test",
f"arn:aws:ec2:eu-west-1:{AWS_ACCOUNT_NUMBER}:security-group/sg-test",
"arn:aws:s3:::bucket-name",
"arn:aws:apigateway:us-east-2::/restapis/api-id/stages/stage-name",
]
expected_regions = {"us-east-1", "eu-west-1", "us-east-2"}
aws_provider = AwsProvider()
recovered_regions = aws_provider.get_regions_from_audit_resources(
audit_resources
)
assert recovered_regions == expected_regions
@mock_aws
def test_get_regions_from_audit_resources_without_regions(self):
audit_resources = ["arn:aws:s3:::bucket-name"]
aws_provider = AwsProvider()
recovered_regions = aws_provider.get_regions_from_audit_resources(
audit_resources
)
assert not recovered_regions
def test_get_regions_all_count(self):
assert len(AwsProvider.get_regions(partition=None)) == 39
def test_get_regions_cn_count(self):
assert len(AwsProvider.get_regions("aws-cn")) == 2
def test_get_regions_aws_count(self):
assert len(AwsProvider.get_regions(partition="aws")) == 34
def test_get_all_regions(self):
with patch(
"prowler.providers.aws.aws_provider.read_aws_regions_file",
return_value={
"services": {
"acm": {
"regions": {
"aws": [
"af-south-1",
],
"aws-cn": [
"cn-north-1",
],
"aws-us-gov": [
"us-gov-west-1",
],
}
}
}
},
):
assert AwsProvider.get_regions(partition=None) == {
"af-south-1",
"cn-north-1",
"us-gov-west-1",
}
def test_get_regions_with_us_gov_partition(self):
with patch(
"prowler.providers.aws.aws_provider.read_aws_regions_file",
return_value={
"services": {
"acm": {
"regions": {
"aws": [
"af-south-1",
],
"aws-cn": [
"cn-north-1",
],
"aws-us-gov": [
"us-gov-west-1",
],
}
}
}
},
):
assert AwsProvider.get_regions("aws-us-gov") == {
"us-gov-west-1",
}
def test_get_regions_with_aws_partition(self):
with patch(
"prowler.providers.aws.aws_provider.read_aws_regions_file",
return_value={
"services": {
"acm": {
"regions": {
"aws": [
"af-south-1",
],
"aws-cn": [
"cn-north-1",
],
"aws-us-gov": [
"us-gov-west-1",
],
}
}
}
},
):
assert AwsProvider.get_regions("aws") == {
"af-south-1",
}
def test_get_regions_with_cn_partition(self):
with patch(
"prowler.providers.aws.aws_provider.read_aws_regions_file",
return_value={
"services": {
"acm": {
"regions": {
"aws": [
"af-south-1",
],
"aws-cn": [
"cn-north-1",
],
"aws-us-gov": [
"us-gov-west-1",
],
}
}
}
},
):
assert AwsProvider.get_regions("aws-cn") == {
"cn-north-1",
}
def test_get_regions_with_unknown_partition(self):
with patch(
"prowler.providers.aws.aws_provider.read_aws_regions_file",
return_value={
"services": {
"acm": {
"regions": {
"aws": [
"af-south-1",
],
"aws-cn": [
"cn-north-1",
],
"aws-us-gov": [
"us-gov-west-1",
],
}
}
}
},
):
partition = "unknown"
with pytest.raises(AWSInvalidPartitionError) as exception:
AwsProvider.get_regions(partition)
assert exception.type == AWSInvalidPartitionError
assert f"Invalid partition: {partition}" in exception.value.args[0]
def test_get_aws_region_for_sts_input_regions_none_session_region_none(self):
input_regions = None
session_region = None
assert (
get_aws_region_for_sts(session_region, input_regions)
== AWS_STS_GLOBAL_ENDPOINT_REGION
)
def test_get_aws_region_for_sts_input_regions_none_session_region_ireland(self):
input_regions = None
session_region = AWS_REGION_EU_WEST_1
assert (
get_aws_region_for_sts(session_region, input_regions)
== AWS_REGION_EU_WEST_1
)
def test_get_aws_region_for_sts_input_regions_empty_session_region_none(self):
input_regions = set()
session_region = None
assert (
get_aws_region_for_sts(session_region, input_regions)
== AWS_STS_GLOBAL_ENDPOINT_REGION
)
def test_get_aws_region_for_sts_input_regions_empty_session_region_ireland(self):
input_regions = set()
session_region = AWS_REGION_EU_WEST_1
assert (
get_aws_region_for_sts(session_region, input_regions)
== AWS_REGION_EU_WEST_1
)
def test_get_aws_region_for_sts_input_regions_ireland_and_virgninia(self):
input_regions = [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
session_region = None
assert (
get_aws_region_for_sts(session_region, input_regions)
== AWS_REGION_EU_WEST_1
)
@mock_aws
def test_set_session_config_default(self):
aws_provider = AwsProvider()
session_config = aws_provider.set_session_config(None)
assert session_config.user_agent_extra == BOTO3_USER_AGENT_EXTRA
assert session_config.retries == {"max_attempts": 3, "mode": "standard"}
@mock_aws
def test_set_session_config_10_max_attempts(self):
aws_provider = AwsProvider()
session_config = aws_provider.set_session_config(10)
assert session_config.user_agent_extra == BOTO3_USER_AGENT_EXTRA
assert session_config.retries == {"max_attempts": 10, "mode": "standard"}
@mock_aws
@patch(
"prowler.lib.check.utils.recover_checks_from_provider",
new=mock_recover_checks_from_aws_provider_ec2_service,
)
def test_get_checks_to_execute_by_audit_resources(self):
aws_provider = AwsProvider()
aws_provider._audit_resources = [
f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:network-acl/acl-1"
]
aws_provider.get_checks_to_execute_by_audit_resources() == {
"ec2_networkacl_allow_ingress_any_port"
}
def test_update_provider_config_aws(self):
aws_provider = set_mocked_aws_provider(
audit_config={"shodan_api_key": "DEFAULT-KEY"}
)
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
assert {
"shodan_api_key": "TEST-API-KEY"
} == Provider.update_provider_config(
aws_provider.audit_config, "shodan_api_key", "TEST-API-KEY"
)
def test_update_provider_config_aws_not_present(self):
aws_provider = set_mocked_aws_provider(
audit_config={"shodan_api_key": "DEFAULT-KEY"}
)
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
assert {"shodan_api_key": "DEFAULT-KEY"} == Provider.update_provider_config(
aws_provider.audit_config, "not_found", "not_value"
)
@mock_aws
def test_refresh_credentials_before_expiration(self):
role_arn = create_role(AWS_REGION_EU_WEST_1)
session_duration = 900
aws_provider = AwsProvider(role_arn=role_arn, session_duration=session_duration)
current_credentials = (
aws_provider._assumed_role_configuration.credentials.__dict__
)
refreshed_credentials = {
"access_key": current_credentials["aws_access_key_id"],
"secret_key": current_credentials["aws_secret_access_key"],
"token": current_credentials["aws_session_token"],
"expiry_time": current_credentials.get(
"expiration", current_credentials.get("expiry_time")
).isoformat(),
}
assert (
AwsProvider.refresh_credentials(
aws_provider._assumed_role_configuration, aws_provider._session
)
== refreshed_credentials
)
@mock_aws
def test_refresh_credentials_after_expiration(self):
role_arn = create_role(AWS_REGION_EU_WEST_1)
session_duration_in_seconds = 900
session_duration = session_duration_in_seconds
aws_provider = AwsProvider(role_arn=role_arn, session_duration=session_duration)
# Manually expire credentials
aws_provider._assumed_role_configuration.credentials.expiration = datetime.now(
get_localzone()
) - timedelta(seconds=session_duration_in_seconds)
current_credentials = aws_provider._assumed_role_configuration.credentials
# Refresh credentials
refreshed_credentials = AwsProvider.refresh_credentials(
aws_provider._assumed_role_configuration, aws_provider._session
)
# Assert that the refreshed credentials are different
access_key = refreshed_credentials.get("access_key")
assert access_key != current_credentials.aws_access_key_id
secret_key = refreshed_credentials.get("secret_key")
assert secret_key != current_credentials.aws_secret_access_key
session_token = refreshed_credentials.get("token")
assert session_token != current_credentials.aws_session_token
expiry_time = refreshed_credentials.get("expiry_time")
expiry_time_formatted = datetime.fromisoformat(expiry_time)
assert expiry_time != current_credentials.expiration
assert datetime.now(get_localzone()) < expiry_time_formatted
# Assert credentials format
assert len(access_key) == 20
assert search(r"^ASIA.*$", access_key)
assert len(secret_key) == 40
assert len(session_token) == 356
assert search(r"^FQoGZXIvYXdzE.*$", session_token)