mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
feat(aws): support excluding regions from scans via CLI, env var, and config (#10688)
This commit is contained in:
1 parent
9e31160887
commit
43913b1592
9 files changed
+358
-44
No files matched your search
@@ -33,6 +33,41 @@ To scan a particular AWS region with Prowler, use:
|
||||
prowler aws -f/--region eu-west-1 us-east-1
|
||||
```
|
||||
|
||||
### Excluding Specific Regions
|
||||
|
||||
To scan all supported AWS regions except a specific subset, use the `--excluded-region` flag:
|
||||
|
||||
```console
|
||||
prowler aws --excluded-region eu-west-1 me-south-1
|
||||
```
|
||||
|
||||
You can also configure the exclusion list with the `PROWLER_AWS_DISALLOWED_REGIONS` environment variable as a comma-separated list:
|
||||
|
||||
```console
|
||||
export PROWLER_AWS_DISALLOWED_REGIONS="eu-west-1,me-south-1"
|
||||
prowler aws
|
||||
```
|
||||
|
||||
Or with the AWS provider configuration in `config.yaml`:
|
||||
|
||||
```yaml
|
||||
aws:
|
||||
disallowed_regions:
|
||||
- eu-west-1
|
||||
- me-south-1
|
||||
```
|
||||
|
||||
When more than one source is set, precedence is:
|
||||
|
||||
1. `--excluded-region`
|
||||
2. `PROWLER_AWS_DISALLOWED_REGIONS`
|
||||
3. `aws.disallowed_regions` in `config.yaml`
|
||||
|
||||
<Note>
|
||||
For self-hosted App or API-triggered scans, set `PROWLER_AWS_DISALLOWED_REGIONS` in the runtime environment of the backend scan containers such as `api` and `worker`. The `ui` container does not enforce AWS region selection.
|
||||
|
||||
</Note>
|
||||
|
||||
### AWS Credentials Configuration
|
||||
|
||||
For details on configuring AWS credentials, refer to the following [Botocore](https://github.com/boto/botocore) [file](https://github.com/boto/botocore/blob/22a19ea7c4c2c4dd7df4ab8c32733cba0c7597a4/botocore/data/partitions.json).
|
||||
|
||||
@@ -17,6 +17,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `entra_conditional_access_policy_mfa_enforced_for_guest_users` check for M365 provider [(#10616)](https://github.com/prowler-cloud/prowler/pull/10616)
|
||||
- `entra_conditional_access_policy_corporate_device_sign_in_frequency_enforced` check for m365 provider [(#10618)](https://github.com/prowler-cloud/prowler/pull/10618)
|
||||
- `entra_conditional_access_policy_block_unknown_device_platforms` check for m365 provider [(#10615)](https://github.com/prowler-cloud/prowler/pull/10615)
|
||||
- `--excluded-region` CLI flag, `PROWLER_AWS_DISALLOWED_REGIONS` environment variable, and `aws.disallowed_regions` config entry to skip specific AWS regions during scans [(#10688)](https://github.com/prowler-cloud/prowler/pull/10688)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
+7
-3
@@ -69,11 +69,11 @@ from prowler.lib.outputs.compliance.cis.cis_gcp import GCPCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_github import GithubCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_googleworkspace import GoogleWorkspaceCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_oraclecloud import OracleCloudCIS
|
||||
from prowler.lib.outputs.compliance.cisa_scuba.cisa_scuba_googleworkspace import (
|
||||
GoogleWorkspaceCISASCuBA,
|
||||
)
|
||||
from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_oraclecloud import OracleCloudCIS
|
||||
from prowler.lib.outputs.compliance.compliance import display_compliance_table
|
||||
from prowler.lib.outputs.compliance.csa.csa_alibabacloud import AlibabaCloudCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_aws import AWSCSA
|
||||
@@ -1311,8 +1311,12 @@ def prowler():
|
||||
global_provider.identity.audited_regions,
|
||||
)
|
||||
if not global_provider.identity.audited_regions
|
||||
else global_provider.identity.audited_regions
|
||||
else set(global_provider.identity.audited_regions)
|
||||
)
|
||||
if global_provider._enabled_regions is not None:
|
||||
security_hub_regions = security_hub_regions.intersection(
|
||||
global_provider._enabled_regions
|
||||
)
|
||||
|
||||
security_hub = SecurityHub(
|
||||
aws_account_id=global_provider.identity.account,
|
||||
|
||||
@@ -3,6 +3,10 @@ aws:
|
||||
# AWS Global Configuration
|
||||
# aws.mute_non_default_regions --> Set to True to muted failed findings in non-default regions for AccessAnalyzer, GuardDuty, SecurityHub, DRS and Config
|
||||
mute_non_default_regions: False
|
||||
# aws.disallowed_regions --> List of AWS regions to exclude from the scan.
|
||||
# Also settable via the PROWLER_AWS_DISALLOWED_REGIONS environment variable or
|
||||
# the --excluded-region CLI flag. Precedence: CLI > env var > config file.
|
||||
# disallowed_regions: []
|
||||
# If you want to mute failed findings only in specific regions, create a file with the following syntax and run it with `prowler aws -w mutelist.yaml`:
|
||||
# Mutelist:
|
||||
# Accounts:
|
||||
|
||||
@@ -111,6 +111,7 @@ class AwsProvider(Provider):
|
||||
mfa: bool = False,
|
||||
profile: str = None,
|
||||
regions: set = set(),
|
||||
excluded_regions: set = None,
|
||||
organizations_role_arn: str = None,
|
||||
scan_unused_services: bool = False,
|
||||
resource_tags: list[str] = [],
|
||||
@@ -136,6 +137,10 @@ class AwsProvider(Provider):
|
||||
- mfa: A boolean indicating whether MFA is enabled.
|
||||
- profile: The name of the AWS CLI profile to use.
|
||||
- regions: A set of regions to audit.
|
||||
- excluded_regions: A set of regions to skip during the scan. Applied
|
||||
on top of `regions` and of the account's enabled regions. Also
|
||||
settable via the PROWLER_AWS_DISALLOWED_REGIONS environment variable
|
||||
or the `disallowed_regions` key in the provider config file.
|
||||
- organizations_role_arn: The ARN of the AWS Organizations IAM role to assume.
|
||||
- scan_unused_services: A boolean indicating whether to scan unused services. False by default.
|
||||
- resource_tags: A list of tags to filter the resources to audit.
|
||||
@@ -190,6 +195,33 @@ class AwsProvider(Provider):
|
||||
|
||||
logger.info("Initializing AWS provider ...")
|
||||
|
||||
# Load provider config early because provider-level settings can affect
|
||||
# bootstrap region selection before the scan starts.
|
||||
if config_content is not None:
|
||||
self._audit_config = config_content
|
||||
else:
|
||||
if not config_path:
|
||||
config_path = default_config_file_path
|
||||
self._audit_config = load_and_validate_config_file(self._type, config_path)
|
||||
|
||||
excluded_regions = self.resolve_excluded_regions(
|
||||
excluded_regions, self._audit_config
|
||||
)
|
||||
|
||||
# Normalize excluded_regions and prune the include-list up front so
|
||||
# every downstream consumer (identity, STS region, service/region
|
||||
# enumeration) sees an already-filtered view.
|
||||
if excluded_regions and regions:
|
||||
regions = set(regions) - excluded_regions
|
||||
if not regions:
|
||||
raise AWSArgumentTypeValidationError(
|
||||
message=(
|
||||
"All requested AWS regions are excluded by the "
|
||||
"disallowed regions configuration."
|
||||
),
|
||||
file=pathlib.Path(__file__).name,
|
||||
)
|
||||
|
||||
######## AWS Session
|
||||
logger.info("Generating original session ...")
|
||||
|
||||
@@ -215,7 +247,7 @@ class AwsProvider(Provider):
|
||||
# After the session is created, validate it
|
||||
logger.info("Validating credentials ...")
|
||||
sts_region = get_aws_region_for_sts(
|
||||
self.session.current_session.region_name, regions
|
||||
self.session.current_session.region_name, regions, excluded_regions
|
||||
)
|
||||
|
||||
# Validate the credentials
|
||||
@@ -229,7 +261,9 @@ class AwsProvider(Provider):
|
||||
|
||||
######## AWS Provider Identity
|
||||
# Get profile region
|
||||
profile_region = self.get_profile_region(self._session.current_session)
|
||||
profile_region = self.get_profile_region(
|
||||
self._session.current_session, excluded_regions
|
||||
)
|
||||
|
||||
# Set identity
|
||||
self._identity = self.set_identity(
|
||||
@@ -332,7 +366,26 @@ class AwsProvider(Provider):
|
||||
)
|
||||
########
|
||||
|
||||
# Parse Scan Tags
|
||||
# Get Enabled Regions
|
||||
self._enabled_regions = self.get_aws_enabled_regions(
|
||||
self._session.current_session
|
||||
)
|
||||
|
||||
# Apply the exclusion to the account's enabled regions. This is the
|
||||
# gate used by generate_regional_clients, so skipped regions never get
|
||||
# a boto3 client created for them and cannot stall the scan.
|
||||
if excluded_regions:
|
||||
if self._enabled_regions is not None:
|
||||
self._enabled_regions = self._enabled_regions - excluded_regions
|
||||
if self._identity.audited_regions:
|
||||
self._identity.audited_regions = (
|
||||
set(self._identity.audited_regions) - excluded_regions
|
||||
)
|
||||
logger.info(f"Excluding AWS regions from scan: {sorted(excluded_regions)}")
|
||||
self._excluded_regions = excluded_regions
|
||||
|
||||
# Parse Scan Tags after region exclusions are applied so tag discovery
|
||||
# also skips disallowed regions.
|
||||
if resource_tags:
|
||||
self._audit_resources = self.get_tagged_resources(resource_tags)
|
||||
|
||||
@@ -340,22 +393,9 @@ class AwsProvider(Provider):
|
||||
if resource_arn:
|
||||
self._audit_resources = resource_arn
|
||||
|
||||
# Get Enabled Regions
|
||||
self._enabled_regions = self.get_aws_enabled_regions(
|
||||
self._session.current_session
|
||||
)
|
||||
|
||||
# Set ignore unused services
|
||||
self._scan_unused_services = scan_unused_services
|
||||
|
||||
# Audit Config
|
||||
if config_content:
|
||||
self._audit_config = config_content
|
||||
else:
|
||||
if not config_path:
|
||||
config_path = default_config_file_path
|
||||
self._audit_config = load_and_validate_config_file(self._type, config_path)
|
||||
|
||||
# Fixer Config
|
||||
self._fixer_config = fixer_config
|
||||
|
||||
@@ -468,12 +508,53 @@ class AwsProvider(Provider):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_profile_region(session: Session):
|
||||
profile_region = AWS_REGION_US_EAST_1
|
||||
if session.region_name:
|
||||
profile_region = session.region_name
|
||||
def resolve_excluded_regions(
|
||||
excluded_regions: set | list | tuple | None,
|
||||
audit_config: dict | None,
|
||||
) -> set[str]:
|
||||
"""Resolve AWS region exclusions with precedence arg > env > config."""
|
||||
if excluded_regions is not None:
|
||||
raw_regions = excluded_regions
|
||||
else:
|
||||
raw_regions = Provider.get_excluded_regions_from_env()
|
||||
if not raw_regions and isinstance(audit_config, dict):
|
||||
raw_regions = audit_config.get("disallowed_regions") or []
|
||||
|
||||
return profile_region
|
||||
return {str(region).strip() for region in raw_regions if str(region).strip()}
|
||||
|
||||
@staticmethod
|
||||
def get_bootstrap_region_candidates(session_region: str | None) -> tuple[str, ...]:
|
||||
"""Return safe fallback regions for bootstrap AWS calls."""
|
||||
if session_region:
|
||||
if session_region.startswith("cn-"):
|
||||
return ("cn-north-1", "cn-northwest-1")
|
||||
if session_region.startswith("us-gov-"):
|
||||
return ("us-gov-east-1", "us-gov-west-1")
|
||||
if session_region.startswith("eusc-"):
|
||||
return ("eusc-de-east-1",)
|
||||
if session_region.startswith("us-iso"):
|
||||
return (session_region,)
|
||||
|
||||
return (AWS_STS_GLOBAL_ENDPOINT_REGION, "us-east-2", "us-west-2", "eu-west-1")
|
||||
|
||||
@staticmethod
|
||||
def get_profile_region(
|
||||
session: Session, excluded_regions: set[str] | None = None
|
||||
) -> str:
|
||||
excluded_regions = set(excluded_regions or ())
|
||||
session_region = session.region_name
|
||||
if session_region and session_region not in excluded_regions:
|
||||
return session_region
|
||||
|
||||
for region in AwsProvider.get_bootstrap_region_candidates(session_region):
|
||||
if region not in excluded_regions:
|
||||
if session_region and session_region != region:
|
||||
logger.info(
|
||||
f"Configured AWS profile region {session_region} is excluded; using {region} for bootstrap clients."
|
||||
)
|
||||
return region
|
||||
|
||||
return session_region or AWS_REGION_US_EAST_1
|
||||
|
||||
@staticmethod
|
||||
def set_identity(
|
||||
@@ -701,12 +782,15 @@ class AwsProvider(Provider):
|
||||
Caller Identity ARN: arn:aws:iam::123456789012:user/prowler
|
||||
```
|
||||
"""
|
||||
# Beautify audited regions, set "all" if there is no filter region
|
||||
regions = (
|
||||
", ".join(self._identity.audited_regions)
|
||||
if self._identity.audited_regions is not None
|
||||
else "all"
|
||||
)
|
||||
# Beautify audited regions. If the scan includes all regions but some
|
||||
# are explicitly excluded, reflect that in the banner instead of
|
||||
# showing the misleading "all" label.
|
||||
if self._identity.audited_regions:
|
||||
regions = ", ".join(sorted(self._identity.audited_regions))
|
||||
elif getattr(self, "_excluded_regions", None):
|
||||
regions = f"all except {', '.join(sorted(self._excluded_regions))}"
|
||||
else:
|
||||
regions = "all"
|
||||
# Beautify audited profile, set "default" if there is no profile set
|
||||
profile = (
|
||||
self._identity.profile if self._identity.profile is not None else "default"
|
||||
@@ -745,6 +829,8 @@ class AwsProvider(Provider):
|
||||
service_regions = AwsProvider.get_available_aws_service_regions(
|
||||
service, self._identity.partition, self._identity.audited_regions
|
||||
)
|
||||
if getattr(self, "_excluded_regions", None):
|
||||
service_regions = service_regions - self._excluded_regions
|
||||
|
||||
# Get the regions enabled for the account and get the intersection with the service available regions
|
||||
if self._enabled_regions is not None:
|
||||
@@ -962,6 +1048,8 @@ class AwsProvider(Provider):
|
||||
service_regions = AwsProvider.get_available_aws_service_regions(
|
||||
service, self._identity.partition, self._identity.audited_regions
|
||||
)
|
||||
if getattr(self, "_excluded_regions", None):
|
||||
service_regions = service_regions - self._excluded_regions
|
||||
default_region = self.get_global_region()
|
||||
# global region of the partition when all regions are audited and there is no profile region
|
||||
if self._identity.profile_region in service_regions:
|
||||
@@ -1565,13 +1653,19 @@ def read_aws_regions_file() -> dict:
|
||||
|
||||
|
||||
# TODO: This can be moved to another class since it doesn't need self
|
||||
def get_aws_region_for_sts(session_region: str, regions: set[str]) -> str:
|
||||
def get_aws_region_for_sts(
|
||||
session_region: str,
|
||||
regions: set[str],
|
||||
excluded_regions: set[str] | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get the AWS region for the STS Assume Role operation.
|
||||
|
||||
Args:
|
||||
- session_region (str): The region configured in the AWS session.
|
||||
- regions (set[str]): The regions passed with the -f/--region/--filter-region option.
|
||||
- excluded_regions (set[str] | None): Regions that should be avoided for
|
||||
bootstrap calls when possible.
|
||||
|
||||
Returns:
|
||||
str: The AWS region for the STS Assume Role operation
|
||||
@@ -1579,20 +1673,21 @@ def get_aws_region_for_sts(session_region: str, regions: set[str]) -> str:
|
||||
Example:
|
||||
aws_region = get_aws_region_for_sts(session_region, regions)
|
||||
"""
|
||||
# If there is no region passed with -f/--region/--filter-region
|
||||
if regions is None or len(regions) == 0:
|
||||
# If you have a region configured in your AWS config or credentials file
|
||||
if session_region is not None:
|
||||
aws_region = session_region
|
||||
else:
|
||||
# If there is no region set passed with -f/--region
|
||||
# we use the Global STS Endpoint Region, us-east-1
|
||||
aws_region = AWS_STS_GLOBAL_ENDPOINT_REGION
|
||||
else:
|
||||
# Get the first region passed to the -f/--region
|
||||
aws_region = list(regions)[0]
|
||||
excluded_regions = set(excluded_regions or ())
|
||||
|
||||
return aws_region
|
||||
if regions:
|
||||
for region in regions:
|
||||
if region not in excluded_regions:
|
||||
return region
|
||||
|
||||
if session_region and session_region not in excluded_regions:
|
||||
return session_region
|
||||
|
||||
for region in AwsProvider.get_bootstrap_region_candidates(session_region):
|
||||
if region not in excluded_regions:
|
||||
return region
|
||||
|
||||
return session_region or AWS_STS_GLOBAL_ENDPOINT_REGION
|
||||
|
||||
|
||||
# TODO: this duplicates the provider arguments validation library
|
||||
|
||||
@@ -66,6 +66,16 @@ def init_parser(self):
|
||||
help="AWS region names to run Prowler against",
|
||||
choices=AwsProvider.get_regions(partition=None),
|
||||
)
|
||||
aws_regions_subparser.add_argument(
|
||||
"--excluded-region",
|
||||
"--excluded-regions",
|
||||
nargs="+",
|
||||
help=(
|
||||
"AWS region names to exclude from the scan. Overrides the "
|
||||
"PROWLER_AWS_DISALLOWED_REGIONS environment variable when set."
|
||||
),
|
||||
choices=AwsProvider.get_regions(partition=None),
|
||||
)
|
||||
# AWS Organizations
|
||||
aws_orgs_subparser = aws_parser.add_argument_group("AWS Organizations")
|
||||
aws_orgs_subparser.add_argument(
|
||||
|
||||
@@ -30,10 +30,12 @@ def quick_inventory(provider: AwsProvider, args):
|
||||
ec2_client = provider.session.current_session.client(
|
||||
"ec2", region_name=provider.identity.profile_region
|
||||
)
|
||||
excluded_regions = getattr(provider, "_excluded_regions", set())
|
||||
# Get all the available regions
|
||||
provider.identity.audited_regions = [
|
||||
region["RegionName"]
|
||||
for region in ec2_client.describe_regions()["Regions"]
|
||||
if region["RegionName"] not in excluded_regions
|
||||
]
|
||||
|
||||
with alive_bar(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import importlib
|
||||
import os
|
||||
import pkgutil
|
||||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -135,6 +136,18 @@ class Provider(ABC):
|
||||
"""
|
||||
return set()
|
||||
|
||||
@staticmethod
|
||||
def get_excluded_regions_from_env() -> set:
|
||||
"""Parse the PROWLER_AWS_DISALLOWED_REGIONS environment variable.
|
||||
|
||||
The variable is a comma-separated list of region identifiers to skip
|
||||
during scans (e.g. "me-south-1, ap-east-1"). Whitespace around entries
|
||||
is tolerated and empty entries are dropped. Returns an empty set when
|
||||
the variable is unset or contains no usable values.
|
||||
"""
|
||||
raw = os.environ.get("PROWLER_AWS_DISALLOWED_REGIONS", "")
|
||||
return {region.strip() for region in raw.split(",") if region.strip()}
|
||||
|
||||
@staticmethod
|
||||
def get_global_provider() -> "Provider":
|
||||
return Provider._global
|
||||
@@ -160,6 +173,11 @@ class Provider(ABC):
|
||||
|
||||
if not isinstance(Provider._global, provider_class):
|
||||
if "aws" in provider_class_name.lower():
|
||||
excluded_regions = (
|
||||
set(arguments.excluded_region)
|
||||
if getattr(arguments, "excluded_region", None)
|
||||
else None
|
||||
)
|
||||
provider_class(
|
||||
retries_max_attempts=arguments.aws_retries_max_attempts,
|
||||
role_arn=arguments.role,
|
||||
@@ -169,6 +187,7 @@ class Provider(ABC):
|
||||
mfa=arguments.mfa,
|
||||
profile=arguments.profile,
|
||||
regions=set(arguments.region) if arguments.region else None,
|
||||
excluded_regions=excluded_regions,
|
||||
organizations_role_arn=arguments.organizations_role,
|
||||
scan_unused_services=arguments.scan_unused_services,
|
||||
resource_tags=arguments.resource_tag,
|
||||
|
||||
@@ -839,6 +839,132 @@ aws:
|
||||
|
||||
assert isinstance(aws_provider, AwsProvider)
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_removed_from_enabled_regions(self):
|
||||
aws_provider = AwsProvider(excluded_regions={AWS_REGION_EU_WEST_1})
|
||||
|
||||
assert AWS_REGION_EU_WEST_1 not in aws_provider._enabled_regions
|
||||
assert AWS_REGION_EU_WEST_1 not in aws_provider.generate_regional_clients("ec2")
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_pruned_from_input_regions(self):
|
||||
aws_provider = AwsProvider(
|
||||
regions={AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1},
|
||||
excluded_regions={AWS_REGION_EU_WEST_1},
|
||||
)
|
||||
|
||||
assert AWS_REGION_EU_WEST_1 not in aws_provider._identity.audited_regions
|
||||
assert AWS_REGION_US_EAST_1 in aws_provider._identity.audited_regions
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_from_config_file(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
|
||||
tmp.write(f"aws:\n disallowed_regions:\n - {AWS_REGION_EU_WEST_1}\n")
|
||||
config_path = tmp.name
|
||||
try:
|
||||
aws_provider = AwsProvider(config_path=config_path)
|
||||
assert AWS_REGION_EU_WEST_1 not in aws_provider._enabled_regions
|
||||
assert aws_provider._excluded_regions == {AWS_REGION_EU_WEST_1}
|
||||
finally:
|
||||
os.remove(config_path)
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_from_env_on_direct_provider_init(self):
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"PROWLER_AWS_DISALLOWED_REGIONS": AWS_REGION_EU_WEST_1},
|
||||
clear=False,
|
||||
):
|
||||
aws_provider = AwsProvider()
|
||||
|
||||
assert aws_provider._excluded_regions == {AWS_REGION_EU_WEST_1}
|
||||
assert AWS_REGION_EU_WEST_1 not in aws_provider._enabled_regions
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_precedence_explicit_over_env_and_config(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
|
||||
tmp.write(f"aws:\n disallowed_regions:\n - {AWS_REGION_EU_WEST_1}\n")
|
||||
config_path = tmp.name
|
||||
try:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"PROWLER_AWS_DISALLOWED_REGIONS": AWS_REGION_US_EAST_1},
|
||||
clear=False,
|
||||
):
|
||||
aws_provider = AwsProvider(
|
||||
config_path=config_path,
|
||||
excluded_regions={AWS_REGION_US_EAST_2},
|
||||
)
|
||||
|
||||
assert aws_provider._excluded_regions == {AWS_REGION_US_EAST_2}
|
||||
assert AWS_REGION_US_EAST_2 not in aws_provider._enabled_regions
|
||||
assert AWS_REGION_EU_WEST_1 in aws_provider._enabled_regions
|
||||
assert AWS_REGION_US_EAST_1 in aws_provider._enabled_regions
|
||||
finally:
|
||||
os.remove(config_path)
|
||||
|
||||
@mock_aws
|
||||
def test_excluded_regions_from_config_avoid_excluded_profile_region(
|
||||
self, monkeypatch
|
||||
):
|
||||
monkeypatch.setenv("AWS_DEFAULT_REGION", AWS_REGION_EU_WEST_1)
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
|
||||
tmp.write(f"aws:\n disallowed_regions:\n - {AWS_REGION_EU_WEST_1}\n")
|
||||
config_path = tmp.name
|
||||
try:
|
||||
aws_provider = AwsProvider(config_path=config_path)
|
||||
|
||||
assert aws_provider.identity.profile_region == AWS_REGION_US_EAST_1
|
||||
finally:
|
||||
os.remove(config_path)
|
||||
|
||||
@mock_aws
|
||||
def test_aws_provider_raises_when_all_input_regions_are_excluded(self):
|
||||
with raises(AWSArgumentTypeValidationError):
|
||||
AwsProvider(
|
||||
regions={AWS_REGION_EU_WEST_1},
|
||||
excluded_regions={AWS_REGION_EU_WEST_1},
|
||||
)
|
||||
|
||||
def test_get_excluded_regions_from_env_parses_comma_list(self):
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"PROWLER_AWS_DISALLOWED_REGIONS": " me-south-1 , ap-east-1 ,, "},
|
||||
):
|
||||
assert Provider.get_excluded_regions_from_env() == {
|
||||
"me-south-1",
|
||||
"ap-east-1",
|
||||
}
|
||||
|
||||
def test_get_excluded_regions_from_env_ignores_legacy_generic_name(self):
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"PROWLER_DISALLOWED_REGIONS": "me-south-1"},
|
||||
clear=True,
|
||||
):
|
||||
assert Provider.get_excluded_regions_from_env() == set()
|
||||
|
||||
def test_get_excluded_regions_from_env_unset(self):
|
||||
with mock.patch.dict(os.environ, {}, clear=True):
|
||||
assert Provider.get_excluded_regions_from_env() == set()
|
||||
|
||||
@mock_aws
|
||||
def test_print_credentials_shows_all_except_excluded_regions(self):
|
||||
aws_provider = AwsProvider(
|
||||
excluded_regions={AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"prowler.providers.aws.aws_provider.print_boxes"
|
||||
) as mock_print_boxes:
|
||||
aws_provider.print_credentials()
|
||||
|
||||
report_lines = mock_print_boxes.call_args.args[0]
|
||||
assert any(
|
||||
"AWS Regions:" in line and "all except eu-west-1, us-east-1" in line
|
||||
for line in report_lines
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_generate_regional_clients_all_enabled_regions(self):
|
||||
aws_provider = AwsProvider()
|
||||
@@ -2033,6 +2159,24 @@ aws:
|
||||
== AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
def test_get_aws_region_for_sts_avoids_excluded_session_region(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}
|
||||
)
|
||||
== AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
def test_get_profile_region_avoids_excluded_session_region(self):
|
||||
mocked_session = mock.Mock(region_name=AWS_REGION_EU_WEST_1)
|
||||
|
||||
assert (
|
||||
AwsProvider.get_profile_region(mocked_session, {AWS_REGION_EU_WEST_1})
|
||||
== AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_set_session_config_default(self):
|
||||
aws_provider = AwsProvider()
|
||||
|
||||
Reference in new issue
Block a user