fix(aws): handle AccessDenied when retrieving resource policy (#6908)

Co-authored-by: Pedro Martín <pedromarting3@gmail.com>
(cherry picked from commit d1053375b7)

# Conflicts:
#	prowler/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py
#	prowler/providers/aws/services/dynamodb/dynamodb_table_cross_account_access/dynamodb_table_cross_account_access.py
#	prowler/providers/aws/services/ecr/ecr_repositories_not_publicly_accessible/ecr_repositories_not_publicly_accessible.py
#	prowler/providers/aws/services/efs/efs_not_publicly_accessible/efs_not_publicly_accessible.py
#	prowler/providers/aws/services/eventbridge/eventbridge_bus_cross_account_access/eventbridge_bus_cross_account_access.py
#	prowler/providers/aws/services/eventbridge/eventbridge_bus_exposed/eventbridge_bus_exposed.py
#	prowler/providers/aws/services/eventbridge/eventbridge_schema_registry_cross_account_access/eventbridge_schema_registry_cross_account_access.py
#	prowler/providers/aws/services/glue/glue_data_catalogs_not_publicly_accessible/glue_data_catalogs_not_publicly_accessible.py
#	prowler/providers/aws/services/s3/s3_bucket_cross_account_access/s3_bucket_cross_account_access.py
#	prowler/providers/aws/services/s3/s3_bucket_policy_public_write_access/s3_bucket_policy_public_write_access.py
#	prowler/providers/aws/services/secretsmanager/secretsmanager_not_publicly_accessible/secretsmanager_not_publicly_accessible.py
#	prowler/providers/aws/services/ses/ses_identity_not_publicly_accessible/ses_identity_not_publicly_accessible.py
This commit is contained in:
Sergio Garcia
2025-02-12 15:31:26 -05:00
parent c88663b7ef
commit ad868b78d5
19 changed files with 201 additions and 83 deletions

View File

@@ -7,11 +7,17 @@ class awslambda_function_not_publicly_accessible(Check):
def execute(self):
findings = []
for function in awslambda_client.functions.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = function.region
report.resource_id = function.name
report.resource_arn = function.arn
report.resource_tags = function.tags
=======
if function.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=function)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = f"Lambda function {function.name} has a resource-based policy without public access."

View File

@@ -6,12 +6,19 @@ from prowler.providers.aws.services.iam.lib.policy import is_policy_public
class dynamodb_table_cross_account_access(Check):
def execute(self):
findings = []
<<<<<<< HEAD
for table_arn, table in dynamodb_client.tables.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = table.name
report.resource_arn = table_arn
report.resource_tags = table.tags
report.region = table.region
=======
for table in dynamodb_client.tables.values():
if table.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=table)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"DynamoDB table {table.name} does not have a resource-based policy."

View File

@@ -8,11 +8,17 @@ class ecr_repositories_not_publicly_accessible(Check):
findings = []
for registry in ecr_client.registries.values():
for repository in registry.repositories:
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = repository.region
report.resource_id = repository.name
report.resource_arn = repository.arn
report.resource_tags = repository.tags
=======
if repository.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=repository)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"Repository {repository.name} is not publicly accessible."

View File

@@ -88,6 +88,7 @@ class ECR(AWSService):
logger.warning(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
repository.policy = {}
except Exception as error:
if "RepositoryPolicyNotFoundException" not in str(error):

View File

@@ -7,11 +7,17 @@ class efs_not_publicly_accessible(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
report.resource_arn = fs.arn
report.resource_tags = fs.tags
=======
if fs.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=fs)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = f"EFS {fs.id} has a policy which does not allow access to any client within the VPC."
if not fs.policy:

View File

@@ -165,7 +165,7 @@ class FileSystem(BaseModel):
id: str
arn: str
region: str
policy: Optional[dict] = {}
policy: Optional[dict]
backup_policy: Optional[str] = "DISABLED"
encrypted: bool
availability_zone_id: Optional[str]

View File

@@ -9,11 +9,17 @@ class eventbridge_bus_cross_account_access(Check):
def execute(self):
findings = []
for bus in eventbridge_client.buses.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.resource_id = bus.name
report.resource_arn = bus.arn
report.resource_tags = bus.tags
report.region = bus.region
=======
if bus.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=bus)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"EventBridge event bus {bus.name} does not allow cross-account access."

View File

@@ -9,7 +9,13 @@ class eventbridge_bus_exposed(Check):
def execute(self):
findings = []
for bus in eventbridge_client.buses.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
=======
if bus.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=bus)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"EventBridge event bus {bus.name} is not exposed to everyone."

View File

@@ -7,11 +7,17 @@ class eventbridge_schema_registry_cross_account_access(Check):
def execute(self):
findings = []
for registry in schema_client.registries.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.resource_id = registry.name
report.resource_arn = registry.arn
report.resource_tags = registry.tags
report.region = registry.region
=======
if registry.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=registry)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = f"EventBridge schema registry {registry.name} does not allow cross-account access."
if is_policy_public(

View File

@@ -7,8 +7,14 @@ class glue_data_catalogs_not_publicly_accessible(Check):
def execute(self):
findings = []
for data_catalog in glue_client.data_catalogs.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = data_catalog.region
=======
if data_catalog.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=data_catalog)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.resource_id = glue_client.audited_account
report.resource_arn = glue_client._get_data_catalog_arn_template(
data_catalog.region

View File

@@ -21,7 +21,7 @@ class opensearch_service_domains_not_publicly_accessible(Check):
if domain.vpc_id:
report.status_extended = f"Opensearch domain {domain.name} is in a VPC, then it is not publicly accessible."
elif domain.access_policy and is_policy_public(
elif domain.access_policy is not None and is_policy_public(
domain.access_policy, opensearch_client.audited_account
):
report.status = "FAIL"

View File

@@ -6,12 +6,19 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
class s3_bucket_cross_account_access(Check):
def execute(self):
findings = []
<<<<<<< HEAD
for arn, bucket in s3_client.buckets.items():
report = Check_Report_AWS(self.metadata())
report.region = bucket.region
report.resource_id = bucket.name
report.resource_arn = arn
report.resource_tags = bucket.tags
=======
for bucket in s3_client.buckets.values():
if bucket.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=bucket)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = f"S3 Bucket {bucket.name} has a bucket policy but it does not allow cross account access."

View File

@@ -7,12 +7,19 @@ from prowler.providers.aws.services.s3.s3control_client import s3control_client
class s3_bucket_policy_public_write_access(Check):
def execute(self):
findings = []
<<<<<<< HEAD
for arn, bucket in s3_client.buckets.items():
report = Check_Report_AWS(self.metadata())
report.region = bucket.region
report.resource_id = bucket.name
report.resource_arn = arn
report.resource_tags = bucket.tags
=======
for bucket in s3_client.buckets.values():
if bucket.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=bucket)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
# Check if bucket policy allow public write access
if not bucket.policy:
report.status = "PASS"

View File

@@ -46,7 +46,9 @@ class s3_bucket_public_access(Check):
report.status_extended = f"S3 Bucket {bucket.name} has public access due to bucket ACL."
# 4. Check bucket policy
if is_policy_public(bucket.policy, s3_client.audited_account):
if bucket.policy is not None and is_policy_public(
bucket.policy, s3_client.audited_account
):
report.status = "FAIL"
report.status_extended = f"S3 Bucket {bucket.name} has public access due to bucket policy."
findings.append(report)

View File

@@ -678,7 +678,7 @@ class Bucket(BaseModel):
logging: bool = False
public_access_block: Optional[PublicAccessBlock]
acl_grantees: List[ACL_Grantee] = Field(default_factory=list)
policy: Dict = Field(default_factory=dict)
policy: Optional[dict]
encryption: Optional[str]
region: str
logging_target_bucket: Optional[str]

View File

@@ -9,11 +9,17 @@ class secretsmanager_not_publicly_accessible(Check):
def execute(self):
findings = []
for secret in secretsmanager_client.secrets.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = secret.region
report.resource_id = secret.name
report.resource_arn = secret.arn
report.resource_tags = secret.tags
=======
if secret.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=secret)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"SecretsManager secret {secret.name} is not publicly accessible."

View File

@@ -7,11 +7,17 @@ class ses_identity_not_publicly_accessible(Check):
def execute(self):
findings = []
for identity in ses_client.email_identities.values():
<<<<<<< HEAD
report = Check_Report_AWS(self.metadata())
report.region = identity.region
report.resource_id = identity.name
report.resource_arn = identity.arn
report.resource_tags = identity.tags
=======
if identity.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=identity)
>>>>>>> d1053375b (fix(aws): handle `AccessDenied` when retrieving resource policy (#6908))
report.status = "PASS"
report.status_extended = (
f"SES identity {identity.name} is not publicly accessible."

View File

@@ -104,12 +104,15 @@ class Test_dynamodb_table_cross_account_access:
def test_no_tables(self):
dynamodb_client = mock.MagicMock
dynamodb_client.tables = {}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -131,15 +134,19 @@ class Test_dynamodb_table_cross_account_access:
arn=arn,
name=test_table_name,
region=AWS_REGION_EU_WEST_1,
policy={},
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -172,12 +179,15 @@ class Test_dynamodb_table_cross_account_access:
policy=test_restricted_policy,
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -211,12 +221,15 @@ class Test_dynamodb_table_cross_account_access:
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -251,12 +264,15 @@ class Test_dynamodb_table_cross_account_access:
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -291,12 +307,15 @@ class Test_dynamodb_table_cross_account_access:
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -330,12 +349,15 @@ class Test_dynamodb_table_cross_account_access:
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,
@@ -369,12 +391,15 @@ class Test_dynamodb_table_cross_account_access:
)
}
with mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
), mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
with (
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_service.DynamoDB",
new=dynamodb_client,
),
mock.patch(
"prowler.providers.aws.services.dynamodb.dynamodb_client.dynamodb_client",
new=dynamodb_client,
),
):
from prowler.providers.aws.services.dynamodb.dynamodb_table_cross_account_access.dynamodb_table_cross_account_access import (
dynamodb_table_cross_account_access,

View File

@@ -43,12 +43,15 @@ class Test_ecr_repositories_not_publicly_accessible:
ecr_client = mock.MagicMock
ecr_client.registries = {}
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
@@ -70,12 +73,15 @@ class Test_ecr_repositories_not_publicly_accessible:
rules=[],
)
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
@@ -109,12 +115,15 @@ class Test_ecr_repositories_not_publicly_accessible:
rules=[],
)
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
@@ -145,7 +154,7 @@ class Test_ecr_repositories_not_publicly_accessible:
arn=repository_arn,
region=AWS_REGION_EU_WEST_1,
scan_on_push=True,
policy=None,
policy={},
images_details=None,
lifecycle_policy=None,
)
@@ -153,12 +162,15 @@ class Test_ecr_repositories_not_publicly_accessible:
rules=[],
)
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,
@@ -199,12 +211,15 @@ class Test_ecr_repositories_not_publicly_accessible:
rules=[],
)
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
), mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_aws_provider(),
),
mock.patch(
"prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible.ecr_client",
ecr_client,
),
):
from prowler.providers.aws.services.ecr.ecr_repositories_not_publicly_accessible.ecr_repositories_not_publicly_accessible import (
ecr_repositories_not_publicly_accessible,