mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
fix(aws): not show findings when AccessDenieds (#3803)
This commit is contained in:
@@ -178,6 +178,8 @@ class <Service>(ServiceParentClass):
|
||||
f"{<item>.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
```
|
||||
???+note
|
||||
To avoid fake findings, when Prowler can't retrieve the items, because an Access Denied or similar error, we set that items value as `None`.
|
||||
|
||||
#### Service Models
|
||||
|
||||
|
||||
+4
-2
@@ -345,8 +345,10 @@ def prowler():
|
||||
global_provider,
|
||||
global_provider.output_options,
|
||||
)
|
||||
# Only display compliance table if there are findings and it is a default execution
|
||||
if findings and default_execution:
|
||||
# Only display compliance table if there are findings (not all MANUAL) and it is a default execution
|
||||
if (
|
||||
findings and not all(finding.status == "MANUAL" for finding in findings)
|
||||
) and default_execution:
|
||||
compliance_overview = False
|
||||
if not compliance_framework:
|
||||
compliance_framework = get_available_compliance_frameworks(provider)
|
||||
|
||||
@@ -40,11 +40,13 @@ def display_summary_table(
|
||||
entity_type = "Context"
|
||||
audited_entities = provider.identity.context
|
||||
|
||||
if findings:
|
||||
# Check if there are findings and that they are not all MANUAL
|
||||
if findings and not all(finding.status == "MANUAL" for finding in findings):
|
||||
current = {
|
||||
"Service": "",
|
||||
"Provider": "",
|
||||
"Total": 0,
|
||||
"Pass": 0,
|
||||
"Critical": 0,
|
||||
"High": 0,
|
||||
"Medium": 0,
|
||||
@@ -70,9 +72,9 @@ def display_summary_table(
|
||||
):
|
||||
add_service_to_table(findings_table, current)
|
||||
|
||||
current["Total"] = current["Muted"] = current["Critical"] = current[
|
||||
"High"
|
||||
] = current["Medium"] = current["Low"] = 0
|
||||
current["Total"] = current["Pass"] = current["Muted"] = current[
|
||||
"Critical"
|
||||
] = current["High"] = current["Medium"] = current["Low"] = 0
|
||||
|
||||
current["Service"] = finding.check_metadata.ServiceName
|
||||
current["Provider"] = finding.check_metadata.Provider
|
||||
@@ -83,6 +85,7 @@ def display_summary_table(
|
||||
current["Muted"] += 1
|
||||
if finding.status == "PASS":
|
||||
pass_count += 1
|
||||
current["Pass"] += 1
|
||||
elif finding.status == "FAIL":
|
||||
fail_count += 1
|
||||
if finding.check_metadata.Severity == "critical":
|
||||
@@ -155,7 +158,7 @@ def add_service_to_table(findings_table, current):
|
||||
)
|
||||
current["Status"] = f"{Fore.RED}FAIL ({total_fails}){Style.RESET_ALL}"
|
||||
else:
|
||||
current["Status"] = f"{Fore.GREEN}PASS ({current['Total']}){Style.RESET_ALL}"
|
||||
current["Status"] = f"{Fore.GREEN}PASS ({current['Pass']}){Style.RESET_ALL}"
|
||||
|
||||
findings_table["Provider"].append(current["Provider"])
|
||||
findings_table["Service"].append(current["Service"])
|
||||
|
||||
@@ -789,18 +789,25 @@ class AwsProvider(Provider):
|
||||
|
||||
def get_aws_enabled_regions(self, current_session: Session) -> set:
|
||||
"""get_aws_enabled_regions returns a set of enabled AWS regions"""
|
||||
try:
|
||||
# EC2 Client to check enabled regions
|
||||
service = "ec2"
|
||||
default_region = self.get_default_region(service)
|
||||
ec2_client = current_session.client(service, region_name=default_region)
|
||||
|
||||
# EC2 Client to check enabled regions
|
||||
service = "ec2"
|
||||
default_region = self.get_default_region(service)
|
||||
ec2_client = current_session.client(service, region_name=default_region)
|
||||
enabled_regions = set()
|
||||
# With AllRegions=False we only get the enabled regions for the account
|
||||
for region in ec2_client.describe_regions(AllRegions=False).get(
|
||||
"Regions", []
|
||||
):
|
||||
enabled_regions.add(region.get("RegionName"))
|
||||
|
||||
enabled_regions = set()
|
||||
# With AllRegions=False we only get the enabled regions for the account
|
||||
for region in ec2_client.describe_regions(AllRegions=False).get("Regions", []):
|
||||
enabled_regions.add(region.get("RegionName"))
|
||||
|
||||
return enabled_regions
|
||||
return enabled_regions
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return set()
|
||||
|
||||
# TODO: review this function
|
||||
# Maybe this should be done within the AwsProvider and not in __main__.py
|
||||
|
||||
+22
-18
@@ -6,22 +6,26 @@ class account_maintain_different_contact_details_to_security_billing_and_operati
|
||||
Check
|
||||
):
|
||||
def execute(self):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = account_client.region
|
||||
report.resource_id = account_client.audited_account
|
||||
report.resource_arn = account_client.audited_account_arn
|
||||
findings = []
|
||||
if account_client.contact_base:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = account_client.region
|
||||
report.resource_id = account_client.audited_account
|
||||
report.resource_arn = account_client.audited_account_arn
|
||||
|
||||
if (
|
||||
len(account_client.contact_phone_numbers)
|
||||
== account_client.number_of_contacts
|
||||
and len(account_client.contact_names) == account_client.number_of_contacts
|
||||
# This is because the primary contact has no email field
|
||||
and len(account_client.contact_emails)
|
||||
== account_client.number_of_contacts - 1
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact."
|
||||
return [report]
|
||||
if (
|
||||
len(account_client.contact_phone_numbers)
|
||||
== account_client.number_of_contacts
|
||||
and len(account_client.contact_names)
|
||||
== account_client.number_of_contacts
|
||||
# This is because the primary contact has no email field
|
||||
and len(account_client.contact_emails)
|
||||
== account_client.number_of_contacts - 1
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
@@ -18,28 +18,29 @@ class Account(AWSService):
|
||||
self.contacts_security = self.__get_alternate_contact__("SECURITY")
|
||||
self.contacts_operations = self.__get_alternate_contact__("OPERATIONS")
|
||||
|
||||
# Set of contact phone numbers
|
||||
self.contact_phone_numbers = {
|
||||
self.contact_base.phone_number,
|
||||
self.contacts_billing.phone_number,
|
||||
self.contacts_security.phone_number,
|
||||
self.contacts_operations.phone_number,
|
||||
}
|
||||
if self.contact_base:
|
||||
# Set of contact phone numbers
|
||||
self.contact_phone_numbers = {
|
||||
self.contact_base.phone_number,
|
||||
self.contacts_billing.phone_number,
|
||||
self.contacts_security.phone_number,
|
||||
self.contacts_operations.phone_number,
|
||||
}
|
||||
|
||||
# Set of contact names
|
||||
self.contact_names = {
|
||||
self.contact_base.name,
|
||||
self.contacts_billing.name,
|
||||
self.contacts_security.name,
|
||||
self.contacts_operations.name,
|
||||
}
|
||||
# Set of contact names
|
||||
self.contact_names = {
|
||||
self.contact_base.name,
|
||||
self.contacts_billing.name,
|
||||
self.contacts_security.name,
|
||||
self.contacts_operations.name,
|
||||
}
|
||||
|
||||
# Set of contact emails
|
||||
self.contact_emails = {
|
||||
self.contacts_billing.email,
|
||||
self.contacts_security.email,
|
||||
self.contacts_operations.email,
|
||||
}
|
||||
# Set of contact emails
|
||||
self.contact_emails = {
|
||||
self.contacts_billing.email,
|
||||
self.contacts_security.email,
|
||||
self.contacts_operations.email,
|
||||
}
|
||||
|
||||
def __get_contact_information__(self):
|
||||
try:
|
||||
@@ -53,10 +54,16 @@ class Account(AWSService):
|
||||
phone_number=primary_account_contact.get("PhoneNumber"),
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return Contact(type="PRIMARY")
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return Contact(type="PRIMARY")
|
||||
|
||||
def __get_alternate_contact__(self, contact_type: str):
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from botocore.client import ClientError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
@@ -37,6 +38,8 @@ class Backup(AWSService):
|
||||
self.audit_resources,
|
||||
)
|
||||
):
|
||||
if self.backup_vaults is None:
|
||||
self.backup_vaults = []
|
||||
self.backup_vaults.append(
|
||||
BackupVault(
|
||||
arn=configuration.get("BackupVaultArn"),
|
||||
@@ -55,7 +58,13 @@ class Backup(AWSService):
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
except ClientError as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
if not self.backup_vaults:
|
||||
self.backup_vaults = None
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
+17
-17
@@ -5,24 +5,24 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
|
||||
class backup_vaults_encrypted(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for backup_vault in backup_client.backup_vaults:
|
||||
# By default we assume that the result is fail
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Backup Vault {backup_vault.name} is not encrypted."
|
||||
)
|
||||
report.resource_arn = backup_vault.arn
|
||||
report.resource_id = backup_vault.name
|
||||
report.region = backup_vault.region
|
||||
# if it is encrypted we only change the status and the status extended
|
||||
if backup_vault.encryption:
|
||||
report.status = "PASS"
|
||||
if backup_client.backup_vaults:
|
||||
for backup_vault in backup_client.backup_vaults:
|
||||
# By default we assume that the result is fail
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Backup Vault {backup_vault.name} is encrypted."
|
||||
f"Backup Vault {backup_vault.name} is not encrypted."
|
||||
)
|
||||
# then we store the finding
|
||||
findings.append(report)
|
||||
report.resource_arn = backup_vault.arn
|
||||
report.resource_id = backup_vault.name
|
||||
report.region = backup_vault.region
|
||||
# if it is encrypted we only change the status and the status extended
|
||||
if backup_vault.encryption:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Backup Vault {backup_vault.name} is encrypted."
|
||||
)
|
||||
# then we store the finding
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
@@ -5,18 +5,19 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
|
||||
class backup_vaults_exist(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Backup Vault exist."
|
||||
report.resource_arn = backup_client.backup_vault_arn_template
|
||||
report.resource_id = backup_client.audited_account
|
||||
report.region = backup_client.region
|
||||
if backup_client.backup_vaults:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
|
||||
report.resource_arn = backup_client.backup_vaults[0].arn
|
||||
report.resource_id = backup_client.backup_vaults[0].name
|
||||
report.region = backup_client.backup_vaults[0].region
|
||||
if backup_client.backup_vaults is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Backup Vault exist."
|
||||
report.resource_arn = backup_client.backup_vault_arn_template
|
||||
report.resource_id = backup_client.audited_account
|
||||
report.region = backup_client.region
|
||||
if backup_client.backup_vaults:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
|
||||
report.resource_arn = backup_client.backup_vaults[0].arn
|
||||
report.resource_id = backup_client.backup_vaults[0].name
|
||||
report.region = backup_client.backup_vaults[0].region
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+23
-22
@@ -8,28 +8,29 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
class cloudtrail_bucket_requires_mfa_delete(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
|
||||
for bucket in s3_client.buckets:
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.mfa_delete:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled."
|
||||
# check if trail bucket is a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
|
||||
for bucket in s3_client.buckets:
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.mfa_delete:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled."
|
||||
# check if trail bucket is a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+30
-29
@@ -11,37 +11,38 @@ maximum_time_without_logging = 1
|
||||
class cloudtrail_cloudwatch_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = (
|
||||
f"Multiregion trail {trail.name} has been logging the last 24h."
|
||||
)
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h."
|
||||
if trail.latest_cloudwatch_delivery_time:
|
||||
last_log_delivery = (
|
||||
datetime.now().replace(tzinfo=timezone.utc)
|
||||
- trail.latest_cloudwatch_delivery_time
|
||||
)
|
||||
if last_log_delivery > timedelta(days=maximum_time_without_logging):
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} has been logging the last 24h."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h."
|
||||
if trail.latest_cloudwatch_delivery_time:
|
||||
last_log_delivery = (
|
||||
datetime.now().replace(tzinfo=timezone.utc)
|
||||
- trail.latest_cloudwatch_delivery_time
|
||||
)
|
||||
if last_log_delivery > timedelta(
|
||||
days=maximum_time_without_logging
|
||||
):
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h."
|
||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||
findings.append(report)
|
||||
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+14
-15
@@ -7,19 +7,18 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
class cloudtrail_insights_exist(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
|
||||
if trail.has_insight_selectors:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Trail {trail.name} has insight selectors and it is logging."
|
||||
)
|
||||
findings.append(report)
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
|
||||
if trail.has_insight_selectors:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} has insight selectors and it is logging."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+18
-21
@@ -7,32 +7,29 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
class cloudtrail_kms_encryption_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = (
|
||||
f"Multiregion trail {trail.name} has encryption disabled."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"Single region trail {trail.name} has encryption disabled."
|
||||
)
|
||||
if trail.kms_key:
|
||||
report.status = "PASS"
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = (
|
||||
f"Multiregion trail {trail.name} has encryption enabled."
|
||||
f"Multiregion trail {trail.name} has encryption disabled."
|
||||
)
|
||||
else:
|
||||
report.status_extended = (
|
||||
f"Single region trail {trail.name} has encryption enabled."
|
||||
f"Single region trail {trail.name} has encryption disabled."
|
||||
)
|
||||
findings.append(report)
|
||||
if trail.kms_key:
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} has encryption enabled."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} has encryption enabled."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+18
-19
@@ -7,26 +7,25 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
class cloudtrail_log_file_validation_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = (
|
||||
f"Multiregion trail {trail.name} log file validation disabled."
|
||||
)
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} log file validation disabled."
|
||||
if trail.log_file_validation_enabled:
|
||||
report.status = "PASS"
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} log file validation enabled."
|
||||
report.status_extended = f"Multiregion trail {trail.name} log file validation disabled."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} log file validation enabled."
|
||||
findings.append(report)
|
||||
report.status_extended = f"Single region trail {trail.name} log file validation disabled."
|
||||
if trail.log_file_validation_enabled:
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} log file validation enabled."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} log file validation enabled."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+30
-29
@@ -8,35 +8,36 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||
else:
|
||||
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||
for bucket in s3_client.buckets:
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.logging:
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||
break
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||
else:
|
||||
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
|
||||
for bucket in s3_client.buckets:
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.logging:
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||
else:
|
||||
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
|
||||
break
|
||||
|
||||
# check if trail is delivering logs in a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually."
|
||||
findings.append(report)
|
||||
# check if trail is delivering logs in a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+37
-36
@@ -8,41 +8,42 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible."
|
||||
else:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible."
|
||||
for bucket in s3_client.buckets:
|
||||
# Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region
|
||||
# (for example due to a SCP) we are going to try access an attribute from a None type
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.acl_grantees:
|
||||
for grant in bucket.acl_grantees:
|
||||
if (
|
||||
grant.URI
|
||||
== "http://acs.amazonaws.com/groups/global/AllUsers"
|
||||
):
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible."
|
||||
else:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible."
|
||||
break
|
||||
# check if trail bucket is a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||
findings.append(report)
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name:
|
||||
trail_bucket_is_in_account = False
|
||||
trail_bucket = trail.s3_bucket
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible."
|
||||
else:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible."
|
||||
for bucket in s3_client.buckets:
|
||||
# Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region
|
||||
# (for example due to a SCP) we are going to try access an attribute from a None type
|
||||
if trail_bucket == bucket.name:
|
||||
trail_bucket_is_in_account = True
|
||||
if bucket.acl_grantees:
|
||||
for grant in bucket.acl_grantees:
|
||||
if (
|
||||
grant.URI
|
||||
== "http://acs.amazonaws.com/groups/global/AllUsers"
|
||||
):
|
||||
report.status = "FAIL"
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible."
|
||||
else:
|
||||
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible."
|
||||
break
|
||||
# check if trail bucket is a cross account bucket
|
||||
if not trail_bucket_is_in_account:
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+30
-31
@@ -7,36 +7,35 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
class cloudtrail_multi_region_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for region in cloudtrail_client.regional_clients.keys():
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = region
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.region == region or trail.is_multiregion:
|
||||
if trail.is_logging:
|
||||
report.status = "PASS"
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = (
|
||||
f"Trail {trail.name} is multiregion and it is logging."
|
||||
)
|
||||
if cloudtrail_client.trails is not None:
|
||||
for region in cloudtrail_client.regional_clients.keys():
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = region
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.region == region or trail.is_multiregion:
|
||||
if trail.is_logging:
|
||||
report.status = "PASS"
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
if trail.is_multiregion:
|
||||
report.status_extended = f"Trail {trail.name} is multiregion and it is logging."
|
||||
else:
|
||||
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging."
|
||||
# Since there exists a logging trail in that region there is no point in checking the remaining trails
|
||||
# Store the finding and exit the loop
|
||||
findings.append(report)
|
||||
break
|
||||
else:
|
||||
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging."
|
||||
# Since there exists a logging trail in that region there is no point in checking the remaining trails
|
||||
# Store the finding and exit the loop
|
||||
findings.append(report)
|
||||
break
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudTrail trails enabled and logging were found."
|
||||
)
|
||||
report.resource_arn = (
|
||||
cloudtrail_client.__get_trail_arn_template__(region)
|
||||
)
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
# If there are no trails logging it is needed to store the FAIL once all the trails have been checked
|
||||
if report.status == "FAIL":
|
||||
findings.append(report)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudTrail trails enabled and logging were found."
|
||||
)
|
||||
report.resource_arn = (
|
||||
cloudtrail_client.__get_trail_arn_template__(region)
|
||||
)
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
# If there are no trails logging it is needed to store the FAIL once all the trails have been checked
|
||||
if report.status == "FAIL":
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+43
-42
@@ -7,48 +7,49 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
class cloudtrail_multi_region_enabled_logging_management_events(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No trail found with multi-region enabled and logging management events."
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
if cloudtrail_client.trails is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No trail found with multi-region enabled and logging management events."
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
if trail.is_multiregion:
|
||||
for event in trail.data_events:
|
||||
# Classic event selectors
|
||||
if not event.is_advanced:
|
||||
# Check if trail has IncludeManagementEvents and ReadWriteType is All
|
||||
if (
|
||||
event.event_selector["ReadWriteType"] == "All"
|
||||
and event.event_selector["IncludeManagementEvents"]
|
||||
):
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_logging:
|
||||
if trail.is_multiregion:
|
||||
for event in trail.data_events:
|
||||
# Classic event selectors
|
||||
if not event.is_advanced:
|
||||
# Check if trail has IncludeManagementEvents and ReadWriteType is All
|
||||
if (
|
||||
event.event_selector["ReadWriteType"] == "All"
|
||||
and event.event_selector["IncludeManagementEvents"]
|
||||
):
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||
|
||||
# Advanced event selectors
|
||||
elif event.is_advanced:
|
||||
if event.event_selector.get(
|
||||
"Name"
|
||||
) == "Management events selector" and all(
|
||||
[
|
||||
field["Field"] != "readOnly"
|
||||
for field in event.event_selector["FieldSelectors"]
|
||||
]
|
||||
):
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||
findings.append(report)
|
||||
# Advanced event selectors
|
||||
elif event.is_advanced:
|
||||
if event.event_selector.get(
|
||||
"Name"
|
||||
) == "Management events selector" and all(
|
||||
[
|
||||
field["Field"] != "readOnly"
|
||||
for field in event.event_selector[
|
||||
"FieldSelectors"
|
||||
]
|
||||
]
|
||||
):
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+46
-43
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
for data_event in trail.data_events:
|
||||
# classic event selectors
|
||||
if not data_event.is_advanced:
|
||||
# Check if trail has a data event for all S3 Buckets for read
|
||||
if (
|
||||
data_event.event_selector["ReadWriteType"] == "ReadOnly"
|
||||
or data_event.event_selector["ReadWriteType"] == "All"
|
||||
):
|
||||
for resource in data_event.event_selector["DataResources"]:
|
||||
if "AWS::S3::Object" == resource["Type"] and (
|
||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||
in resource["Values"]
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
for data_event in trail.data_events:
|
||||
# classic event selectors
|
||||
if not data_event.is_advanced:
|
||||
# Check if trail has a data event for all S3 Buckets for read
|
||||
if (
|
||||
data_event.event_selector["ReadWriteType"] == "ReadOnly"
|
||||
or data_event.event_selector["ReadWriteType"] == "All"
|
||||
):
|
||||
for resource in data_event.event_selector["DataResources"]:
|
||||
if "AWS::S3::Object" == resource["Type"] and (
|
||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||
in resource["Values"]
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
# advanced event selectors
|
||||
elif data_event.is_advanced:
|
||||
for field_selector in data_event.event_selector[
|
||||
"FieldSelectors"
|
||||
]:
|
||||
if (
|
||||
field_selector["Field"] == "resources.type"
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
# advanced event selectors
|
||||
elif data_event.is_advanced:
|
||||
for field_selector in data_event.event_selector["FieldSelectors"]:
|
||||
if (
|
||||
field_selector["Field"] == "resources.type"
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
if not findings and (
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
if not findings and (
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+46
-43
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
for data_event in trail.data_events:
|
||||
# Classic event selectors
|
||||
if not data_event.is_advanced:
|
||||
# Check if trail has a data event for all S3 Buckets for write
|
||||
if (
|
||||
data_event.event_selector["ReadWriteType"] == "All"
|
||||
or data_event.event_selector["ReadWriteType"] == "WriteOnly"
|
||||
):
|
||||
for resource in data_event.event_selector["DataResources"]:
|
||||
if "AWS::S3::Object" == resource["Type"] and (
|
||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||
in resource["Values"]
|
||||
if cloudtrail_client.trails is not None:
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
for data_event in trail.data_events:
|
||||
# Classic event selectors
|
||||
if not data_event.is_advanced:
|
||||
# Check if trail has a data event for all S3 Buckets for write
|
||||
if (
|
||||
data_event.event_selector["ReadWriteType"] == "All"
|
||||
or data_event.event_selector["ReadWriteType"] == "WriteOnly"
|
||||
):
|
||||
for resource in data_event.event_selector["DataResources"]:
|
||||
if "AWS::S3::Object" == resource["Type"] and (
|
||||
f"arn:{cloudtrail_client.audited_partition}:s3"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
|
||||
in resource["Values"]
|
||||
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
|
||||
in resource["Values"]
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
# Advanced event selectors
|
||||
elif data_event.is_advanced:
|
||||
for field_selector in data_event.event_selector[
|
||||
"FieldSelectors"
|
||||
]:
|
||||
if (
|
||||
field_selector["Field"] == "resources.type"
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
# Advanced event selectors
|
||||
elif data_event.is_advanced:
|
||||
for field_selector in data_event.event_selector["FieldSelectors"]:
|
||||
if (
|
||||
field_selector["Field"] == "resources.type"
|
||||
and field_selector["Equals"][0] == "AWS::S3::Object"
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = trail.region
|
||||
report.resource_id = trail.name
|
||||
report.resource_arn = trail.arn
|
||||
report.resource_tags = trail.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
if not findings and (
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
if not findings and (
|
||||
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_arn = cloudtrail_client.trail_arn_template
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
@@ -17,10 +17,11 @@ class Cloudtrail(AWSService):
|
||||
self.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail"
|
||||
self.trails = {}
|
||||
self.__threading_call__(self.__get_trails__)
|
||||
self.__get_trail_status__()
|
||||
self.__get_insight_selectors__()
|
||||
self.__get_event_selectors__()
|
||||
self.__list_tags_for_resource__()
|
||||
if self.trails:
|
||||
self.__get_trail_status__()
|
||||
self.__get_insight_selectors__()
|
||||
self.__get_event_selectors__()
|
||||
self.__list_tags_for_resource__()
|
||||
|
||||
def __get_trail_arn_template__(self, region):
|
||||
return (
|
||||
@@ -45,6 +46,8 @@ class Cloudtrail(AWSService):
|
||||
kms_key_id = trail["KmsKeyId"]
|
||||
if "CloudWatchLogsLogGroupArn" in trail:
|
||||
log_group_arn = trail["CloudWatchLogsLogGroupArn"]
|
||||
if self.trails is None:
|
||||
self.trails = {}
|
||||
self.trails[trail["TrailARN"]] = Trail(
|
||||
name=trail["Name"],
|
||||
is_multiregion=trail["IsMultiRegionTrail"],
|
||||
@@ -61,12 +64,24 @@ class Cloudtrail(AWSService):
|
||||
has_insight_selectors=trail.get("HasInsightSelectors"),
|
||||
)
|
||||
if trails_count == 0:
|
||||
if self.trails is None:
|
||||
self.trails = {}
|
||||
self.trails[self.__get_trail_arn_template__(regional_client.region)] = (
|
||||
Trail(
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.trails:
|
||||
self.trails = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?CreateNetworkAcl.+\$\.eventName\s*=\s*.?CreateNetworkAclEntry.+\$\.eventName\s*=\s*.?DeleteNetworkAcl.+\$\.eventName\s*=\s*.?DeleteNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclAssociation.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?CreateCustomerGateway.+\$\.eventName\s*=\s*.?DeleteCustomerGateway.+\$\.eventName\s*=\s*.?AttachInternetGateway.+\$\.eventName\s*=\s*.?CreateInternetGateway.+\$\.eventName\s*=\s*.?DeleteInternetGateway.+\$\.eventName\s*=\s*.?DetachInternetGateway.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*.?ec2.amazonaws.com.+\$\.eventName\s*=\s*.?CreateRoute.+\$\.eventName\s*=\s*.?CreateRouteTable.+\$\.eventName\s*=\s*.?ReplaceRoute.+\$\.eventName\s*=\s*.?ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*.?DeleteRouteTable.+\$\.eventName\s*=\s*.?DeleteRoute.+\$\.eventName\s*=\s*.?DisassociateRouteTable.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?CreateVpc.+\$\.eventName\s*=\s*.?DeleteVpc.+\$\.eventName\s*=\s*.?ModifyVpcAttribute.+\$\.eventName\s*=\s*.?AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*.?CreateVpcPeeringConnection.+\$\.eventName\s*=\s*.?DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*.?RejectVpcPeeringConnection.+\$\.eventName\s*=\s*.?AttachClassicLinkVpc.+\$\.eventName\s*=\s*.?DetachClassicLinkVpc.+\$\.eventName\s*=\s*.?DisableVpcClassicLink.+\$\.eventName\s*=\s*.?EnableVpcClassicLink.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+16
-13
@@ -5,17 +5,20 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class cloudwatch_cross_account_sharing_disabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = "CloudWatch doesn't allow cross-account sharing."
|
||||
report.resource_arn = iam_client.role_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
report.region = iam_client.region
|
||||
for role in iam_client.roles:
|
||||
if role.name == "CloudWatch-CrossAccountSharingRole":
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "CloudWatch has allowed cross-account sharing."
|
||||
findings.append(report)
|
||||
if iam_client.roles is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = "CloudWatch doesn't allow cross-account sharing."
|
||||
report.resource_arn = iam_client.role_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
report.region = iam_client.region
|
||||
for role in iam_client.roles:
|
||||
if role.name == "CloudWatch-CrossAccountSharingRole":
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"CloudWatch has allowed cross-account sharing."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+14
-15
@@ -5,19 +5,18 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
class cloudwatch_log_group_kms_encryption_enabled(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
report.resource_tags = log_group.tags
|
||||
if log_group.kms_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Log Group {log_group.name} does not have AWS KMS keys associated."
|
||||
)
|
||||
findings.append(report)
|
||||
if logs_client.log_groups:
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
report.resource_tags = log_group.tags
|
||||
if log_group.kms_id:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Log Group {log_group.name} does not have AWS KMS keys associated."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+77
-69
@@ -11,78 +11,86 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
|
||||
class cloudwatch_log_group_no_secrets_in_logs(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"No secrets found in {log_group.name} log group."
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
log_group_secrets = []
|
||||
if log_group.log_streams:
|
||||
for log_stream_name in log_group.log_streams:
|
||||
log_stream_secrets = {}
|
||||
log_stream_data = "\n".join(
|
||||
[
|
||||
dumps(event["message"])
|
||||
for event in log_group.log_streams[log_stream_name]
|
||||
]
|
||||
)
|
||||
log_stream_secrets_output = detect_secrets_scan(log_stream_data)
|
||||
|
||||
if log_stream_secrets_output:
|
||||
for secret in log_stream_secrets_output:
|
||||
flagged_event = log_group.log_streams[log_stream_name][
|
||||
secret["line_number"] - 1
|
||||
]
|
||||
cloudwatch_timestamp = (
|
||||
convert_to_cloudwatch_timestamp_format(
|
||||
flagged_event["timestamp"]
|
||||
)
|
||||
)
|
||||
if cloudwatch_timestamp not in log_stream_secrets.keys():
|
||||
log_stream_secrets[cloudwatch_timestamp] = SecretsDict()
|
||||
|
||||
try:
|
||||
log_event_data = dumps(
|
||||
loads(flagged_event["message"]), indent=2
|
||||
)
|
||||
except Exception:
|
||||
log_event_data = dumps(
|
||||
flagged_event["message"], indent=2
|
||||
)
|
||||
if len(log_event_data.split("\n")) > 1:
|
||||
# Can get more informative output if there is more than 1 line.
|
||||
# Will rescan just this event to get the type of secret and the line number
|
||||
event_detect_secrets_output = detect_secrets_scan(
|
||||
log_event_data
|
||||
)
|
||||
if event_detect_secrets_output:
|
||||
for secret in event_detect_secrets_output:
|
||||
log_stream_secrets[
|
||||
cloudwatch_timestamp
|
||||
].add_secret(
|
||||
secret["line_number"], secret["type"]
|
||||
)
|
||||
else:
|
||||
log_stream_secrets[cloudwatch_timestamp].add_secret(
|
||||
1, secret["type"]
|
||||
)
|
||||
if log_stream_secrets:
|
||||
secrets_string = "; ".join(
|
||||
if logs_client.log_groups:
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"No secrets found in {log_group.name} log group."
|
||||
)
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
log_group_secrets = []
|
||||
if log_group.log_streams:
|
||||
for log_stream_name in log_group.log_streams:
|
||||
log_stream_secrets = {}
|
||||
log_stream_data = "\n".join(
|
||||
[
|
||||
f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}"
|
||||
for timestamp in log_stream_secrets
|
||||
dumps(event["message"])
|
||||
for event in log_group.log_streams[log_stream_name]
|
||||
]
|
||||
)
|
||||
log_group_secrets.append(
|
||||
f"in log stream {log_stream_name} {secrets_string}"
|
||||
)
|
||||
if log_group_secrets:
|
||||
secrets_string = "; ".join(log_group_secrets)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}."
|
||||
findings.append(report)
|
||||
log_stream_secrets_output = detect_secrets_scan(log_stream_data)
|
||||
|
||||
if log_stream_secrets_output:
|
||||
for secret in log_stream_secrets_output:
|
||||
flagged_event = log_group.log_streams[log_stream_name][
|
||||
secret["line_number"] - 1
|
||||
]
|
||||
cloudwatch_timestamp = (
|
||||
convert_to_cloudwatch_timestamp_format(
|
||||
flagged_event["timestamp"]
|
||||
)
|
||||
)
|
||||
if (
|
||||
cloudwatch_timestamp
|
||||
not in log_stream_secrets.keys()
|
||||
):
|
||||
log_stream_secrets[cloudwatch_timestamp] = (
|
||||
SecretsDict()
|
||||
)
|
||||
|
||||
try:
|
||||
log_event_data = dumps(
|
||||
loads(flagged_event["message"]), indent=2
|
||||
)
|
||||
except Exception:
|
||||
log_event_data = dumps(
|
||||
flagged_event["message"], indent=2
|
||||
)
|
||||
if len(log_event_data.split("\n")) > 1:
|
||||
# Can get more informative output if there is more than 1 line.
|
||||
# Will rescan just this event to get the type of secret and the line number
|
||||
event_detect_secrets_output = detect_secrets_scan(
|
||||
log_event_data
|
||||
)
|
||||
if event_detect_secrets_output:
|
||||
for secret in event_detect_secrets_output:
|
||||
log_stream_secrets[
|
||||
cloudwatch_timestamp
|
||||
].add_secret(
|
||||
secret["line_number"], secret["type"]
|
||||
)
|
||||
else:
|
||||
log_stream_secrets[cloudwatch_timestamp].add_secret(
|
||||
1, secret["type"]
|
||||
)
|
||||
if log_stream_secrets:
|
||||
secrets_string = "; ".join(
|
||||
[
|
||||
f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}"
|
||||
for timestamp in log_stream_secrets
|
||||
]
|
||||
)
|
||||
log_group_secrets.append(
|
||||
f"in log stream {log_stream_name} {secrets_string}"
|
||||
)
|
||||
if log_group_secrets:
|
||||
secrets_string = "; ".join(log_group_secrets)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
|
||||
|
||||
+19
-18
@@ -10,23 +10,24 @@ class cloudwatch_log_group_retention_policy_specific_days_enabled(Check):
|
||||
specific_retention_days = logs_client.audit_config.get(
|
||||
"log_group_retention_days", 365
|
||||
)
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
report.resource_tags = log_group.tags
|
||||
if (
|
||||
log_group.never_expire is False
|
||||
and log_group.retention_days < specific_retention_days
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
if log_group.never_expire is True:
|
||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires."
|
||||
if logs_client.log_groups:
|
||||
for log_group in logs_client.log_groups:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = log_group.region
|
||||
report.resource_id = log_group.name
|
||||
report.resource_arn = log_group.arn
|
||||
report.resource_tags = log_group.tags
|
||||
if (
|
||||
log_group.never_expire is False
|
||||
and log_group.retention_days < specific_retention_days
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
|
||||
else:
|
||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days."
|
||||
findings.append(report)
|
||||
report.status = "PASS"
|
||||
if log_group.never_expire is True:
|
||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires."
|
||||
else:
|
||||
report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_authentication_failures(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_aws_organizations_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*.?organizations\.amazonaws\.com.+\$\.eventName\s*=\s*.?AcceptHandshake.+\$\.eventName\s*=\s*.?AttachPolicy.+\$\.eventName\s*=\s*.?CancelHandshake.+\$\.eventName\s*=\s*.?CreateAccount.+\$\.eventName\s*=\s*.?CreateOrganization.+\$\.eventName\s*=\s*.?CreateOrganizationalUnit.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeclineHandshake.+\$\.eventName\s*=\s*.?DeleteOrganization.+\$\.eventName\s*=\s*.?DeleteOrganizationalUnit.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?EnableAllFeatures.+\$\.eventName\s*=\s*.?EnablePolicyType.+\$\.eventName\s*=\s*.?InviteAccountToOrganization.+\$\.eventName\s*=\s*.?LeaveOrganization.+\$\.eventName\s*=\s*.?DetachPolicy.+\$\.eventName\s*=\s*.?DisablePolicyType.+\$\.eventName\s*=\s*.?MoveAccount.+\$\.eventName\s*=\s*.?RemoveAccountFromOrganization.+\$\.eventName\s*=\s*.?UpdateOrganizationalUnit.+\$\.eventName\s*=\s*.?UpdatePolicy.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Chec
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,22 +15,25 @@ class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventSource\s*=\s*.?s3.amazonaws.com.+\$\.eventName\s*=\s*.?PutBucketAcl.+\$\.eventName\s*=\s*.?PutBucketPolicy.+\$\.eventName\s*=\s*.?PutBucketCors.+\$\.eventName\s*=\s*.?PutBucketLifecycle.+\$\.eventName\s*=\s*.?PutBucketReplication.+\$\.eventName\s*=\s*.?DeleteBucketPolicy.+\$\.eventName\s*=\s*.?DeleteBucketCors.+\$\.eventName\s*=\s*.?DeleteBucketLifecycle.+\$\.eventName\s*=\s*.?DeleteBucketReplication.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_policy_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?DeleteGroupPolicy.+\$\.eventName\s*=\s*.?DeleteRolePolicy.+\$\.eventName\s*=\s*.?DeleteUserPolicy.+\$\.eventName\s*=\s*.?PutGroupPolicy.+\$\.eventName\s*=\s*.?PutRolePolicy.+\$\.eventName\s*=\s*.?PutUserPolicy.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?CreatePolicyVersion.+\$\.eventName\s*=\s*.?DeletePolicyVersion.+\$\.eventName\s*=\s*.?AttachRolePolicy.+\$\.eventName\s*=\s*.?DetachRolePolicy.+\$\.eventName\s*=\s*.?AttachUserPolicy.+\$\.eventName\s*=\s*.?DetachUserPolicy.+\$\.eventName\s*=\s*.?AttachGroupPolicy.+\$\.eventName\s*=\s*.?DetachGroupPolicy.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_root_usage(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_security_group_changes(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*.?AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*.?CreateSecurityGroup.+\$\.eventName\s*=\s*.?DeleteSecurityGroup.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_sign_in_without_mfa(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+19
-16
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_unauthorized_api_calls(Check):
|
||||
def execute(self):
|
||||
pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?"
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"No CloudWatch log groups found with metric filters or alarms associated."
|
||||
)
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
if (
|
||||
cloudtrail_client.trails is not None
|
||||
and logs_client.metric_filters is not None
|
||||
and cloudwatch_client.metric_alarms is not None
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
|
||||
report.region = logs_client.region
|
||||
report.resource_id = logs_client.audited_account
|
||||
report.resource_arn = logs_client.log_group_arn_template
|
||||
report = check_cloudwatch_log_metric_filter(
|
||||
pattern,
|
||||
cloudtrail_client.trails,
|
||||
logs_client.metric_filters,
|
||||
cloudwatch_client.metric_alarms,
|
||||
report,
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
@@ -16,7 +16,8 @@ class CloudWatch(AWSService):
|
||||
super().__init__(__class__.__name__, provider)
|
||||
self.metric_alarms = []
|
||||
self.__threading_call__(self.__describe_alarms__)
|
||||
self.__list_tags_for_resource__()
|
||||
if self.metric_alarms:
|
||||
self.__list_tags_for_resource__()
|
||||
|
||||
def __describe_alarms__(self, regional_client):
|
||||
logger.info("CloudWatch - Describing alarms...")
|
||||
@@ -33,6 +34,8 @@ class CloudWatch(AWSService):
|
||||
namespace = None
|
||||
if "Namespace" in alarm:
|
||||
namespace = alarm["Namespace"]
|
||||
if self.metric_alarms is None:
|
||||
self.metric_alarms = []
|
||||
self.metric_alarms.append(
|
||||
MetricAlarm(
|
||||
arn=alarm["AlarmArn"],
|
||||
@@ -42,6 +45,17 @@ class CloudWatch(AWSService):
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDenied":
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.metric_alarms:
|
||||
self.metric_alarms = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -72,15 +86,16 @@ class Logs(AWSService):
|
||||
self.log_groups = []
|
||||
self.__threading_call__(self.__describe_metric_filters__)
|
||||
self.__threading_call__(self.__describe_log_groups__)
|
||||
if (
|
||||
"cloudwatch_log_group_no_secrets_in_logs"
|
||||
in provider.audit_metadata.expected_checks
|
||||
):
|
||||
self.events_per_log_group_threshold = (
|
||||
1000 # The threshold for number of events to return per log group.
|
||||
)
|
||||
self.__threading_call__(self.__get_log_events__)
|
||||
self.__list_tags_for_resource__()
|
||||
if self.log_groups:
|
||||
if (
|
||||
"cloudwatch_log_group_no_secrets_in_logs"
|
||||
in provider.audit_metadata.expected_checks
|
||||
):
|
||||
self.events_per_log_group_threshold = (
|
||||
1000 # The threshold for number of events to return per log group.
|
||||
)
|
||||
self.__threading_call__(self.__get_log_events__)
|
||||
self.__list_tags_for_resource__()
|
||||
|
||||
def __describe_metric_filters__(self, regional_client):
|
||||
logger.info("CloudWatch Logs - Describing metric filters...")
|
||||
@@ -94,6 +109,8 @@ class Logs(AWSService):
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(arn, self.audit_resources)
|
||||
):
|
||||
if self.metric_filters is None:
|
||||
self.metric_filters = []
|
||||
self.metric_filters.append(
|
||||
MetricFilter(
|
||||
arn=arn,
|
||||
@@ -104,6 +121,17 @@ class Logs(AWSService):
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.metric_filters:
|
||||
self.metric_filters = []
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -126,6 +154,8 @@ class Logs(AWSService):
|
||||
if not retention_days:
|
||||
never_expire = True
|
||||
retention_days = 9999
|
||||
if self.log_groups is None:
|
||||
self.log_groups = []
|
||||
self.log_groups.append(
|
||||
LogGroup(
|
||||
arn=log_group["arn"],
|
||||
@@ -136,6 +166,17 @@ class Logs(AWSService):
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.log_groups:
|
||||
self.log_groups = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
@@ -12,9 +12,10 @@ def check_cloudwatch_log_metric_filter(
|
||||
):
|
||||
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
|
||||
log_groups = []
|
||||
for trail in trails.values():
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
if trails is not None:
|
||||
for trail in trails.values():
|
||||
if trail.log_group_arn:
|
||||
log_groups.append(trail.log_group_arn.split(":")[6])
|
||||
# 2. Describe metric filters for previous log groups
|
||||
for metric_filter in metric_filters:
|
||||
if metric_filter.log_group in log_groups:
|
||||
|
||||
@@ -52,6 +52,11 @@ class FMS(AWSService):
|
||||
):
|
||||
# FMS is not enabled in this account
|
||||
self.fms_admin_account = False
|
||||
else:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||
)
|
||||
self.fms_admin_account = None
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy expiration exists
|
||||
if iam_client.password_policy.max_age:
|
||||
if iam_client.password_policy.max_age <= 90:
|
||||
@@ -22,9 +21,6 @@ class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password expiration is not set."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_lowercase(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if lowercase flag is set
|
||||
if iam_client.password_policy.lowercase:
|
||||
report.status = "PASS"
|
||||
@@ -20,8 +19,5 @@ class iam_password_policy_lowercase(Check):
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "IAM password policy does not require at least one lowercase letter."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_minimum_length_14(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check password policy length
|
||||
if (
|
||||
iam_client.password_policy.length
|
||||
@@ -23,8 +22,5 @@ class iam_password_policy_minimum_length_14(Check):
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "IAM password policy does not require minimum length of 14 characters."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_number(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if number flag is set
|
||||
if iam_client.password_policy.numbers:
|
||||
report.status = "PASS"
|
||||
@@ -22,8 +21,5 @@ class iam_password_policy_number(Check):
|
||||
report.status_extended = (
|
||||
"IAM password policy does not require at least one number."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_reuse_24(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if reuse prevention flag is set
|
||||
if (
|
||||
iam_client.password_policy.reuse_prevention
|
||||
@@ -25,8 +24,5 @@ class iam_password_policy_reuse_24(Check):
|
||||
report.status_extended = (
|
||||
"IAM password policy reuse prevention is less than 24 or not set."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_symbol(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if symbol flag is set
|
||||
if iam_client.password_policy.symbols:
|
||||
report.status = "PASS"
|
||||
@@ -22,8 +21,5 @@ class iam_password_policy_symbol(Check):
|
||||
report.status_extended = (
|
||||
"IAM password policy does not require at least one symbol."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+5
-9
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_password_policy_uppercase(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if password policy exists
|
||||
if iam_client.password_policy:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.password_policy_arn_template
|
||||
report.resource_id = iam_client.audited_account
|
||||
# Check if uppercase flag is set
|
||||
if iam_client.password_policy.uppercase:
|
||||
report.status = "PASS"
|
||||
@@ -20,8 +19,5 @@ class iam_password_policy_uppercase(Check):
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "IAM password policy does not require at least one uppercase letter."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Password policy cannot be found."
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+17
-18
@@ -5,24 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_role_administratoraccess_policy(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
for role in iam_client.roles:
|
||||
if (
|
||||
not role.is_service_role
|
||||
): # Avoid service roles since they cannot be modified by the user
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"IAM Role {role.name} does not have AdministratorAccess policy."
|
||||
)
|
||||
for policy in role.attached_policies:
|
||||
if policy["PolicyName"] == "AdministratorAccess":
|
||||
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
|
||||
report.status = "FAIL"
|
||||
if iam_client.roles:
|
||||
for role in iam_client.roles:
|
||||
if (
|
||||
not role.is_service_role
|
||||
): # Avoid service roles since they cannot be modified by the user
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"IAM Role {role.name} does not have AdministratorAccess policy."
|
||||
for policy in role.attached_policies:
|
||||
if policy["PolicyName"] == "AdministratorAccess":
|
||||
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
|
||||
report.status = "FAIL"
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+63
-59
@@ -5,78 +5,82 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_role_cross_account_readonlyaccess_policy(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
for role in iam_client.roles:
|
||||
if (
|
||||
not role.is_service_role
|
||||
): # Avoid service roles since they cannot be modified by the user
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"IAM Role {role.name} does not have ReadOnlyAccess policy."
|
||||
)
|
||||
for policy in role.attached_policies:
|
||||
if policy["PolicyName"] == "ReadOnlyAccess":
|
||||
report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account."
|
||||
cross_account_access = False
|
||||
if isinstance(role.assume_role_policy["Statement"], list):
|
||||
for statement in role.assume_role_policy["Statement"]:
|
||||
if not cross_account_access:
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and "AWS" in statement["Principal"]
|
||||
):
|
||||
if isinstance(
|
||||
statement["Principal"]["AWS"], list
|
||||
if iam_client.roles:
|
||||
for role in iam_client.roles:
|
||||
if (
|
||||
not role.is_service_role
|
||||
): # Avoid service roles since they cannot be modified by the user
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"IAM Role {role.name} does not have ReadOnlyAccess policy."
|
||||
)
|
||||
for policy in role.attached_policies:
|
||||
if policy["PolicyName"] == "ReadOnlyAccess":
|
||||
report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account."
|
||||
cross_account_access = False
|
||||
if isinstance(role.assume_role_policy["Statement"], list):
|
||||
for statement in role.assume_role_policy["Statement"]:
|
||||
if not cross_account_access:
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and "AWS" in statement["Principal"]
|
||||
):
|
||||
for aws_account in statement["Principal"][
|
||||
"AWS"
|
||||
]:
|
||||
if isinstance(
|
||||
statement["Principal"]["AWS"], list
|
||||
):
|
||||
for aws_account in statement[
|
||||
"Principal"
|
||||
]["AWS"]:
|
||||
if (
|
||||
iam_client.audited_account
|
||||
not in aws_account
|
||||
or "*" == aws_account
|
||||
):
|
||||
cross_account_access = True
|
||||
break
|
||||
else:
|
||||
if (
|
||||
iam_client.audited_account
|
||||
not in aws_account
|
||||
or "*" == aws_account
|
||||
not in statement["Principal"]["AWS"]
|
||||
or "*"
|
||||
== statement["Principal"]["AWS"]
|
||||
):
|
||||
cross_account_access = True
|
||||
break
|
||||
else:
|
||||
else:
|
||||
break
|
||||
else:
|
||||
statement = role.assume_role_policy["Statement"]
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and "AWS" in statement["Principal"]
|
||||
):
|
||||
if isinstance(statement["Principal"]["AWS"], list):
|
||||
for aws_account in statement["Principal"][
|
||||
"AWS"
|
||||
]:
|
||||
if (
|
||||
iam_client.audited_account
|
||||
not in statement["Principal"]["AWS"]
|
||||
or "*" == statement["Principal"]["AWS"]
|
||||
not in aws_account
|
||||
or "*" == aws_account
|
||||
):
|
||||
cross_account_access = True
|
||||
else:
|
||||
break
|
||||
else:
|
||||
statement = role.assume_role_policy["Statement"]
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and "AWS" in statement["Principal"]
|
||||
):
|
||||
if isinstance(statement["Principal"]["AWS"], list):
|
||||
for aws_account in statement["Principal"]["AWS"]:
|
||||
break
|
||||
else:
|
||||
if (
|
||||
iam_client.audited_account
|
||||
not in aws_account
|
||||
or "*" == aws_account
|
||||
not in statement["Principal"]["AWS"]
|
||||
or "*" == statement["Principal"]["AWS"]
|
||||
):
|
||||
cross_account_access = True
|
||||
break
|
||||
else:
|
||||
if (
|
||||
iam_client.audited_account
|
||||
not in statement["Principal"]["AWS"]
|
||||
or "*" == statement["Principal"]["AWS"]
|
||||
):
|
||||
cross_account_access = True
|
||||
if cross_account_access:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"IAM Role {role.name} gives cross account read-only access."
|
||||
if cross_account_access:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"IAM Role {role.name} gives cross account read-only access."
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+31
-30
@@ -8,36 +8,37 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_role_cross_service_confused_deputy_prevention(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
for role in iam_client.roles:
|
||||
# This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed)
|
||||
if role.is_service_role and "aws-service-role" not in role.arn:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack."
|
||||
for statement in role.assume_role_policy["Statement"]:
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and (
|
||||
"sts:AssumeRole" in statement["Action"]
|
||||
or "sts:*" in statement["Action"]
|
||||
or "*" in statement["Action"]
|
||||
)
|
||||
# Need to make sure we are checking the part of the assume role policy document that provides a service access
|
||||
and "Service" in statement["Principal"]
|
||||
# Check to see if the appropriate condition statements have been implemented
|
||||
and "Condition" in statement
|
||||
and is_condition_block_restrictive(
|
||||
statement["Condition"], iam_client.audited_account
|
||||
)
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack."
|
||||
break
|
||||
if iam_client.roles:
|
||||
for role in iam_client.roles:
|
||||
# This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed)
|
||||
if role.is_service_role and "aws-service-role" not in role.arn:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = role.arn
|
||||
report.resource_id = role.name
|
||||
report.resource_tags = role.tags
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack."
|
||||
for statement in role.assume_role_policy["Statement"]:
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
and (
|
||||
"sts:AssumeRole" in statement["Action"]
|
||||
or "sts:*" in statement["Action"]
|
||||
or "*" in statement["Action"]
|
||||
)
|
||||
# Need to make sure we are checking the part of the assume role policy document that provides a service access
|
||||
and "Service" in statement["Principal"]
|
||||
# Check to see if the appropriate condition statements have been implemented
|
||||
and "Condition" in statement
|
||||
and is_condition_block_restrictive(
|
||||
statement["Condition"], iam_client.audited_account
|
||||
)
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+14
-11
@@ -5,15 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_securityaudit_role_created(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_id = "SecurityAudit"
|
||||
report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit"
|
||||
if iam_client.entities_role_attached_to_securityaudit_policy:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "SecurityAudit policy is not attached to any role."
|
||||
findings.append(report)
|
||||
if iam_client.entities_role_attached_to_securityaudit_policy is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_id = "SecurityAudit"
|
||||
report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit"
|
||||
if iam_client.entities_role_attached_to_securityaudit_policy:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
"SecurityAudit policy is not attached to any role."
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
@@ -117,6 +117,16 @@ class IAM(AWSService):
|
||||
is_service_role=is_service_role(role),
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDenied":
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
roles = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -219,11 +229,28 @@ class IAM(AWSService):
|
||||
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
# Password policy does not exist
|
||||
stored_password_policy = None
|
||||
# Password policy is the IAM default
|
||||
stored_password_policy = PasswordPolicy(
|
||||
length=8,
|
||||
symbols=False,
|
||||
numbers=False,
|
||||
uppercase=False,
|
||||
lowercase=False,
|
||||
allow_change=True,
|
||||
expiration=False,
|
||||
max_age=None,
|
||||
reuse_prevention=None,
|
||||
hard_expiry=None,
|
||||
)
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
elif error.response["Error"]["Code"] == "AccessDenied":
|
||||
# User does not have permission to get password policy
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
stored_password_policy = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -395,34 +422,35 @@ class IAM(AWSService):
|
||||
def __list_attached_role_policies__(self):
|
||||
logger.info("IAM - List Attached User Policies...")
|
||||
try:
|
||||
for role in self.roles:
|
||||
try:
|
||||
attached_role_policies = []
|
||||
list_attached_role_policies_paginator = self.client.get_paginator(
|
||||
"list_attached_role_policies"
|
||||
)
|
||||
for page in list_attached_role_policies_paginator.paginate(
|
||||
RoleName=role.name
|
||||
):
|
||||
for policy in page["AttachedPolicies"]:
|
||||
attached_role_policies.append(policy)
|
||||
|
||||
role.attached_policies = attached_role_policies
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
if self.roles:
|
||||
for role in self.roles:
|
||||
try:
|
||||
attached_role_policies = []
|
||||
list_attached_role_policies_paginator = (
|
||||
self.client.get_paginator("list_attached_role_policies")
|
||||
)
|
||||
else:
|
||||
for page in list_attached_role_policies_paginator.paginate(
|
||||
RoleName=role.name
|
||||
):
|
||||
for policy in page["AttachedPolicies"]:
|
||||
attached_role_policies.append(policy)
|
||||
|
||||
role.attached_policies = attached_role_policies
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -548,66 +576,67 @@ class IAM(AWSService):
|
||||
|
||||
def __list_inline_role_policies__(self):
|
||||
logger.info("IAM - List Inline Role Policies...")
|
||||
for role in self.roles:
|
||||
try:
|
||||
inline_role_policies = []
|
||||
get_role_inline_policies_paginator = self.client.get_paginator(
|
||||
"list_role_policies"
|
||||
)
|
||||
for page in get_role_inline_policies_paginator.paginate(
|
||||
RoleName=role.name
|
||||
):
|
||||
for policy in page["PolicyNames"]:
|
||||
try:
|
||||
inline_role_policies.append(policy)
|
||||
# Get inline policies & their policy documents here:
|
||||
inline_policy = self.client.get_role_policy(
|
||||
RoleName=role.name, PolicyName=policy
|
||||
)
|
||||
inline_role_policy_doc = inline_policy["PolicyDocument"]
|
||||
self.policies.append(
|
||||
Policy(
|
||||
name=policy,
|
||||
arn=role.arn,
|
||||
entity=role.name,
|
||||
type="Inline",
|
||||
attached=True,
|
||||
version_id="v1",
|
||||
document=inline_role_policy_doc,
|
||||
if self.roles:
|
||||
for role in self.roles:
|
||||
try:
|
||||
inline_role_policies = []
|
||||
get_role_inline_policies_paginator = self.client.get_paginator(
|
||||
"list_role_policies"
|
||||
)
|
||||
for page in get_role_inline_policies_paginator.paginate(
|
||||
RoleName=role.name
|
||||
):
|
||||
for policy in page["PolicyNames"]:
|
||||
try:
|
||||
inline_role_policies.append(policy)
|
||||
# Get inline policies & their policy documents here:
|
||||
inline_policy = self.client.get_role_policy(
|
||||
RoleName=role.name, PolicyName=policy
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
inline_role_policy_doc = inline_policy["PolicyDocument"]
|
||||
self.policies.append(
|
||||
Policy(
|
||||
name=policy,
|
||||
arn=role.arn,
|
||||
entity=role.name,
|
||||
type="Inline",
|
||||
attached=True,
|
||||
version_id="v1",
|
||||
document=inline_role_policy_doc,
|
||||
)
|
||||
)
|
||||
else:
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
role.inline_policies = inline_role_policies
|
||||
|
||||
role.inline_policies = inline_role_policies
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
logger.warning(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
else:
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __list_entities_role_for_policy__(self, policy_arn):
|
||||
logger.info("IAM - List Entities Role For Policy...")
|
||||
try:
|
||||
@@ -615,6 +644,17 @@ class IAM(AWSService):
|
||||
roles = self.client.list_entities_for_policy(
|
||||
PolicyArn=policy_arn, EntityFilter="Role"
|
||||
)["PolicyRoles"]
|
||||
return roles
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDenied":
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
roles = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -717,21 +757,24 @@ class IAM(AWSService):
|
||||
def __list_tags_for_resource__(self):
|
||||
logger.info("IAM - List Tags...")
|
||||
try:
|
||||
for role in self.roles:
|
||||
try:
|
||||
response = self.client.list_role_tags(RoleName=role.name)["Tags"]
|
||||
role.tags = response
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
role.tags = []
|
||||
else:
|
||||
if self.roles:
|
||||
for role in self.roles:
|
||||
try:
|
||||
response = self.client.list_role_tags(RoleName=role.name)[
|
||||
"Tags"
|
||||
]
|
||||
role.tags = response
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "NoSuchEntity":
|
||||
role.tags = []
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
|
||||
+14
-13
@@ -5,17 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_support_role_created(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_id = "AWSSupportServiceRolePolicy"
|
||||
report.resource_arn = (
|
||||
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
if iam_client.entities_role_attached_to_support_policy:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Support policy is not attached to any role."
|
||||
findings.append(report)
|
||||
if iam_client.entities_role_attached_to_support_policy is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_id = "AWSSupportServiceRolePolicy"
|
||||
report.resource_arn = (
|
||||
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
if iam_client.entities_role_attached_to_support_policy:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "Support policy is not attached to any role."
|
||||
findings.append(report)
|
||||
return findings
|
||||
|
||||
+14
-13
@@ -7,18 +7,19 @@ from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_client i
|
||||
class resourceexplorer2_indexes_found(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Resource Explorer Indexes found."
|
||||
report.region = resource_explorer_2_client.region
|
||||
report.resource_arn = "NoResourceExplorer"
|
||||
report.resource_id = resource_explorer_2_client.audited_account
|
||||
report.resource_arn = resource_explorer_2_client.index_arn_template
|
||||
if resource_explorer_2_client.indexes:
|
||||
report.region = resource_explorer_2_client.indexes[0].region
|
||||
report.resource_arn = resource_explorer_2_client.indexes[0].arn
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}."
|
||||
findings.append(report)
|
||||
if resource_explorer_2_client.indexes is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No Resource Explorer Indexes found."
|
||||
report.region = resource_explorer_2_client.region
|
||||
report.resource_arn = "NoResourceExplorer"
|
||||
report.resource_id = resource_explorer_2_client.audited_account
|
||||
report.resource_arn = resource_explorer_2_client.index_arn_template
|
||||
if resource_explorer_2_client.indexes:
|
||||
report.region = resource_explorer_2_client.indexes[0].region
|
||||
report.resource_arn = resource_explorer_2_client.indexes[0].arn
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from botocore.client import ClientError
|
||||
from pydantic import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
@@ -23,6 +24,8 @@ class ResourceExplorer2(AWSService):
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(index["Arn"], self.audit_resources)
|
||||
):
|
||||
if self.indexes is None:
|
||||
self.indexes = []
|
||||
self.indexes.append(
|
||||
Indexes(
|
||||
arn=index["Arn"],
|
||||
@@ -30,6 +33,17 @@ class ResourceExplorer2(AWSService):
|
||||
type=index["Type"],
|
||||
)
|
||||
)
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.indexes:
|
||||
self.indexes = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
+17
-16
@@ -7,22 +7,23 @@ from prowler.providers.aws.services.ssmincidents.ssmincidents_client import (
|
||||
class ssmincidents_enabled_with_plans(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No SSM Incidents replication set exists."
|
||||
report.resource_arn = ssmincidents_client.replication_set_arn_template
|
||||
report.resource_id = ssmincidents_client.audited_account
|
||||
report.region = ssmincidents_client.region
|
||||
if ssmincidents_client.replication_set:
|
||||
report.resource_arn = ssmincidents_client.replication_set[0].arn
|
||||
report.resource_tags = [] # Not supported for replication sets
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE."
|
||||
if ssmincidents_client.replication_set[0].status == "ACTIVE":
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist."
|
||||
if ssmincidents_client.response_plans:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans."
|
||||
if ssmincidents_client.replication_set is not None:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = "No SSM Incidents replication set exists."
|
||||
report.resource_arn = ssmincidents_client.replication_set_arn_template
|
||||
report.resource_id = ssmincidents_client.audited_account
|
||||
report.region = ssmincidents_client.region
|
||||
if ssmincidents_client.replication_set:
|
||||
report.resource_arn = ssmincidents_client.replication_set[0].arn
|
||||
report.resource_tags = [] # Not supported for replication sets
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE."
|
||||
if ssmincidents_client.replication_set[0].status == "ACTIVE":
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist."
|
||||
if ssmincidents_client.response_plans:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans."
|
||||
|
||||
findings.append(report)
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
@@ -45,6 +45,17 @@ class SSMIncidents(AWSService):
|
||||
arn=replication_set,
|
||||
)
|
||||
]
|
||||
except ClientError as error:
|
||||
if error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if not self.replication_set:
|
||||
self.replication_set = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
|
||||
|
||||
+24
-23
@@ -7,28 +7,29 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
|
||||
class trustedadvisor_errors_and_warnings(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
if trustedadvisor_client.premium_support.enabled:
|
||||
if trustedadvisor_client.checks:
|
||||
for check in trustedadvisor_client.checks:
|
||||
if (
|
||||
check.status != "not_available"
|
||||
): # avoid not_available checks since there are no resources that apply
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = check.region
|
||||
report.resource_id = check.id
|
||||
report.resource_arn = check.arn
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
|
||||
if check.status == "ok":
|
||||
report.status = "PASS"
|
||||
findings.append(report)
|
||||
else:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
|
||||
report.resource_id = trustedadvisor_client.audited_account
|
||||
report.resource_arn = trustedadvisor_client.account_arn_template
|
||||
report.region = trustedadvisor_client.region
|
||||
findings.append(report)
|
||||
if trustedadvisor_client.premium_support:
|
||||
if trustedadvisor_client.premium_support.enabled:
|
||||
if trustedadvisor_client.checks:
|
||||
for check in trustedadvisor_client.checks:
|
||||
if (
|
||||
check.status != "not_available"
|
||||
): # avoid not_available checks since there are no resources that apply
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = check.region
|
||||
report.resource_id = check.id
|
||||
report.resource_arn = check.arn
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
|
||||
if check.status == "ok":
|
||||
report.status = "PASS"
|
||||
findings.append(report)
|
||||
else:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
|
||||
report.resource_id = trustedadvisor_client.audited_account
|
||||
report.resource_arn = trustedadvisor_client.account_arn_template
|
||||
report.region = trustedadvisor_client.region
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
|
||||
+6
-1
@@ -7,7 +7,12 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
|
||||
class trustedadvisor_premium_support_plan_subscribed(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
if trustedadvisor_client.audit_config.get("verify_premium_support_plans", True):
|
||||
if (
|
||||
trustedadvisor_client.premium_support
|
||||
and trustedadvisor_client.audit_config.get(
|
||||
"verify_premium_support_plans", True
|
||||
)
|
||||
):
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
|
||||
@@ -22,7 +22,6 @@ class TrustedAdvisor(AWSService):
|
||||
support_region = "us-east-1"
|
||||
else:
|
||||
support_region = "us-gov-west-1"
|
||||
|
||||
self.client = self.session.client(self.service, region_name=support_region)
|
||||
self.client.region = support_region
|
||||
self.__describe_services__()
|
||||
@@ -106,6 +105,19 @@ class TrustedAdvisor(AWSService):
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
elif error.response["Error"]["Code"] == "AccessDeniedException":
|
||||
logger.error(
|
||||
f"{self.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
self.premium_support = None
|
||||
else:
|
||||
logger.error(
|
||||
f"{self.region} --"
|
||||
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
|
||||
f" {error}"
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
|
||||
+26
@@ -101,3 +101,29 @@ class Test_account_maintain_different_contact_details_to_security_billing_and_op
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].resource_arn == AWS_ACCOUNT_ARN
|
||||
|
||||
def test_access_denied(self):
|
||||
account_client = mock.MagicMock
|
||||
account_client.region = AWS_REGION
|
||||
account_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
account_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||
account_client.contact_base = None
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.account.account_service.Account",
|
||||
new=account_client,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.account.account_client.account_client",
|
||||
new=account_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.account.account_maintain_different_contact_details_to_security_billing_and_operations.account_maintain_different_contact_details_to_security_billing_and_operations import (
|
||||
account_maintain_different_contact_details_to_security_billing_and_operations,
|
||||
)
|
||||
|
||||
check = (
|
||||
account_maintain_different_contact_details_to_security_billing_and_operations()
|
||||
)
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@@ -85,3 +85,28 @@ class Test_backup_vaults_exist:
|
||||
assert result[0].resource_id == "MyBackupVault"
|
||||
assert result[0].resource_arn == backup_vault_arn
|
||||
assert result[0].region == AWS_REGION
|
||||
|
||||
def test_access_denied(self):
|
||||
backup_client = mock.MagicMock
|
||||
backup_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
backup_client.audited_account_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
backup_client.region = AWS_REGION
|
||||
backup_client.audited_partition = "aws"
|
||||
backup_client.backup_vault_arn_template = f"arn:{backup_client.audited_partition}:backup:{backup_client.region}:{backup_client.audited_account}:backup-vault"
|
||||
backup_client.__get_backup_vault_arn_template__ = mock.MagicMock(
|
||||
return_value=backup_client.backup_vault_arn_template
|
||||
)
|
||||
backup_client.backup_vaults = None
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.backup.backup_service.Backup",
|
||||
new=backup_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.backup.backup_vaults_exist.backup_vaults_exist import (
|
||||
backup_vaults_exist,
|
||||
)
|
||||
|
||||
check = backup_vaults_exist()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+32
@@ -254,3 +254,35 @@ class Test_cloudtrail_bucket_requires_mfa_delete:
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
@patch(
|
||||
"botocore.client.BaseClient._make_api_call",
|
||||
new=mock_make_api_call_getbucketversioning_mfadelete_enabled,
|
||||
)
|
||||
def test_access_denied(self):
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_client, mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.s3_client",
|
||||
new=S3(aws_provider),
|
||||
) as s3_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete import (
|
||||
cloudtrail_bucket_requires_mfa_delete,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
s3_client.buckets = []
|
||||
|
||||
check = cloudtrail_bucket_requires_mfa_delete()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+31
@@ -303,3 +303,34 @@ class Test_cloudtrail_cloudwatch_logging_enabled:
|
||||
== f"Single region trail {trail_name_eu} is not logging in the last 24h or not configured to deliver logs."
|
||||
)
|
||||
assert report.resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
),
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(
|
||||
set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
)
|
||||
),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import (
|
||||
cloudtrail_cloudwatch_logging_enabled,
|
||||
)
|
||||
|
||||
service_client.trails = None
|
||||
|
||||
check = cloudtrail_cloudwatch_logging_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+23
@@ -128,3 +128,26 @@ class Test_cloudtrail_insights_exist:
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
)
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist import (
|
||||
cloudtrail_insights_exist,
|
||||
)
|
||||
|
||||
service_client.trails = None
|
||||
check = cloudtrail_insights_exist()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+29
@@ -137,3 +137,32 @@ class Test_cloudtrail_kms_encryption_enabled:
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled import (
|
||||
cloudtrail_kms_encryption_enabled,
|
||||
)
|
||||
|
||||
service_client.trails = None
|
||||
check = cloudtrail_kms_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+28
@@ -155,3 +155,31 @@ class Test_cloudtrail_log_file_validation_enabled:
|
||||
assert report.resource_arn == trail_eu["TrailARN"]
|
||||
assert report.resource_tags == []
|
||||
assert report.region == AWS_REGION_EU_WEST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled import (
|
||||
cloudtrail_log_file_validation_enabled,
|
||||
)
|
||||
|
||||
service_client.trails = None
|
||||
|
||||
check = cloudtrail_log_file_validation_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+37
@@ -226,3 +226,40 @@ class Test_cloudtrail_logs_s3_bucket_access_logging_enabled:
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.s3.s3_service import S3
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as cloudtrail_client, mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client",
|
||||
new=S3(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as s3_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled import (
|
||||
cloudtrail_logs_s3_bucket_access_logging_enabled,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
s3_client.buckets = []
|
||||
|
||||
check = cloudtrail_logs_s3_bucket_access_logging_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+37
@@ -289,3 +289,40 @@ class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible:
|
||||
)
|
||||
assert result[0].resource_tags == []
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.s3.s3_service import S3
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client",
|
||||
new=Cloudtrail(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as cloudtrail_client, mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.s3_client",
|
||||
new=S3(
|
||||
set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1])
|
||||
),
|
||||
) as s3_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import (
|
||||
cloudtrail_logs_s3_bucket_is_not_publicly_accessible,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
s3_client.buckets = []
|
||||
|
||||
check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+28
@@ -289,3 +289,31 @@ class Test_cloudtrail_multi_region_enabled:
|
||||
assert report.resource_id == trail_name_us
|
||||
assert report.resource_arn == trail_us["TrailARN"]
|
||||
assert report.resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import (
|
||||
cloudtrail_multi_region_enabled,
|
||||
)
|
||||
|
||||
service_client.trails = None
|
||||
check = cloudtrail_multi_region_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+26
@@ -273,3 +273,29 @@ class Test_cloudtrail_multi_region_enabled_logging_management_events:
|
||||
result[0].status_extended
|
||||
== "No trail found with multi-region enabled and logging management events."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events import (
|
||||
cloudtrail_multi_region_enabled_logging_management_events,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
check = cloudtrail_multi_region_enabled_logging_management_events()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+30
@@ -391,3 +391,33 @@ class Test_cloudtrail_s3_dataevents_read_enabled:
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.s3.s3_service import S3
|
||||
|
||||
aws_provider = set_mocked_aws_provider()
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_client, mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.s3_client",
|
||||
new=S3(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled import (
|
||||
cloudtrail_s3_dataevents_read_enabled,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
check = cloudtrail_s3_dataevents_read_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+30
@@ -389,3 +389,33 @@ class Test_cloudtrail_s3_dataevents_write_enabled:
|
||||
assert result[0].resource_arn == trail_us["TrailARN"]
|
||||
assert result[0].resource_tags == []
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.s3.s3_service import S3
|
||||
|
||||
aws_provider = set_mocked_aws_provider()
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_service, mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.s3_client",
|
||||
new=S3(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled import (
|
||||
cloudtrail_s3_dataevents_write_enabled,
|
||||
)
|
||||
|
||||
cloudtrail_service.trails = None
|
||||
check = cloudtrail_s3_dataevents_write_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+48
@@ -561,3 +561,51 @@ class Test_cloudwatch_changes_to_network_acls_alarm_configured:
|
||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]
|
||||
)
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client",
|
||||
new=Logs(aws_provider),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client",
|
||||
new=CloudWatch(aws_provider),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import (
|
||||
cloudwatch_changes_to_network_acls_alarm_configured,
|
||||
)
|
||||
|
||||
cloudtrail_client.trails = None
|
||||
check = cloudwatch_changes_to_network_acls_alarm_configured()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+36
@@ -99,3 +99,39 @@ class Test_cloudwatch_cross_account_sharing_disabled:
|
||||
== "CloudWatch has allowed cross-account sharing."
|
||||
)
|
||||
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole"
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||
)
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as iam_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled import (
|
||||
cloudwatch_cross_account_sharing_disabled,
|
||||
)
|
||||
|
||||
iam_client.roles = None
|
||||
check = cloudwatch_cross_account_sharing_disabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+36
@@ -138,3 +138,39 @@ class Test_cloudwatch_log_group_kms_encryption_enabled:
|
||||
== "Log Group test does have AWS KMS key test_kms_id associated."
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||
)
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client",
|
||||
new=Logs(aws_provider),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import (
|
||||
cloudwatch_log_group_kms_encryption_enabled,
|
||||
)
|
||||
|
||||
service_client.log_groups = None
|
||||
check = cloudwatch_log_group_kms_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+36
@@ -154,3 +154,39 @@ class Test_cloudwatch_log_group_no_secrets_in_logs:
|
||||
"Potential secrets found in log group", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == "test"
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||
)
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs.logs_client",
|
||||
new=Logs(aws_provider),
|
||||
) as logs_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs import (
|
||||
cloudwatch_log_group_no_secrets_in_logs,
|
||||
)
|
||||
|
||||
logs_client.log_groups = None
|
||||
check = cloudwatch_log_group_no_secrets_in_logs()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+44
@@ -207,3 +207,47 @@ class Test_cloudwatch_log_group_retention_policy_specific_days_enabled:
|
||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:test"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
# Generate Logs Client
|
||||
logs_client = client("logs", region_name=AWS_REGION_US_EAST_1)
|
||||
# Request Logs group
|
||||
logs_client.create_log_group(
|
||||
logGroupName="test",
|
||||
)
|
||||
logs_client.put_retention_policy(logGroupName="test", retentionInDays=7)
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||
)
|
||||
aws_provider._audit_config = {"log_group_retention_days": 365}
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client",
|
||||
new=Logs(aws_provider),
|
||||
) as service_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import (
|
||||
cloudwatch_log_group_retention_policy_specific_days_enabled,
|
||||
)
|
||||
|
||||
service_client.log_groups = None
|
||||
check = cloudwatch_log_group_retention_policy_specific_days_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+50
@@ -575,3 +575,53 @@ class Test_cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_c
|
||||
== f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
|
||||
CloudWatch,
|
||||
Logs,
|
||||
)
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
[AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
|
||||
)
|
||||
|
||||
from prowler.providers.common.models import Audit_Metadata
|
||||
|
||||
aws_provider.audit_metadata = Audit_Metadata(
|
||||
services_scanned=0,
|
||||
# We need to set this check to call __describe_log_groups__
|
||||
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"],
|
||||
completed_checks=0,
|
||||
audit_progress=0,
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client",
|
||||
new=Logs(aws_provider),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client",
|
||||
new=CloudWatch(aws_provider),
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
) as cloudtrail_client:
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled,
|
||||
)
|
||||
|
||||
check = (
|
||||
cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled()
|
||||
)
|
||||
cloudtrail_client.trails = None
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@@ -26,6 +26,24 @@ class Test_fms_policy_compliant:
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_access_denied(self):
|
||||
fms_client = mock.MagicMock
|
||||
fms_client.region = AWS_REGION_US_EAST_1
|
||||
fms_client.fms_admin_account = None
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.fms.fms_service.FMS",
|
||||
new=fms_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.fms.fms_policy_compliant.fms_policy_compliant import (
|
||||
fms_policy_compliant,
|
||||
)
|
||||
|
||||
check = fms_policy_compliant()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_fms_admin_with_non_compliant_policies(self):
|
||||
fms_client = mock.MagicMock
|
||||
fms_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
|
||||
+22
@@ -145,3 +145,25 @@ class Test_iam_password_policy_expires_passwords_within_90_days_or_less:
|
||||
"Password expiration is set lower than 90 days",
|
||||
result[0].status_extended,
|
||||
)
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less import (
|
||||
iam_password_policy_expires_passwords_within_90_days_or_less,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_expires_passwords_within_90_days_or_less()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -85,3 +85,25 @@ class Test_iam_password_policy_lowercase:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase import (
|
||||
iam_password_policy_lowercase,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_lowercase()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -129,3 +129,25 @@ class Test_iam_password_policy_minimum_length_14:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14 import (
|
||||
iam_password_policy_minimum_length_14,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_minimum_length_14()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -92,3 +92,25 @@ class Test_iam_password_policy_number:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number import (
|
||||
iam_password_policy_number,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_number()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -89,3 +89,25 @@ class Test_iam_password_policy_reuse_24:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24 import (
|
||||
iam_password_policy_reuse_24,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_reuse_24()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -92,3 +92,25 @@ class Test_iam_password_policy_symbol:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol import (
|
||||
iam_password_policy_symbol,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_symbol()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -89,3 +89,25 @@ class Test_iam_password_policy_uppercase:
|
||||
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
|
||||
)
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase import (
|
||||
iam_password_policy_uppercase,
|
||||
)
|
||||
|
||||
service_client.password_policy = None
|
||||
check = iam_password_policy_uppercase()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+23
@@ -264,3 +264,26 @@ class Test_iam_role_administratoraccess_policy:
|
||||
check = iam_role_administratoraccess_policy()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||
def test_access_denied(self):
|
||||
iam_client = mock.MagicMock
|
||||
iam_client.roles = None
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
|
||||
new=iam_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
|
||||
iam_role_administratoraccess_policy,
|
||||
)
|
||||
|
||||
check = iam_role_administratoraccess_policy()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -85,3 +85,25 @@ class Test_iam_securityaudit_role_created:
|
||||
assert result[0].resource_id == "SecurityAudit"
|
||||
assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit"
|
||||
assert result[0].region == "us-east-1"
|
||||
|
||||
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||
def test_access_denied(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created import (
|
||||
iam_securityaudit_role_created,
|
||||
)
|
||||
|
||||
service_client.entities_role_attached_to_securityaudit_policy = None
|
||||
|
||||
check = iam_securityaudit_role_created()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+22
@@ -95,3 +95,25 @@ class Test_iam_support_role_created:
|
||||
result[0].resource_arn
|
||||
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
|
||||
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
|
||||
def test_access_denied(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.common.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
), mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
) as service_client:
|
||||
from prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created import (
|
||||
iam_support_role_created,
|
||||
)
|
||||
|
||||
service_client.entities_role_attached_to_support_policy = None
|
||||
|
||||
check = iam_support_role_created()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+28
@@ -80,3 +80,31 @@ class Test_resourceexplorer2_indexes_found:
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].resource_arn == INDEX_ARN
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
resourceexplorer2_client = mock.MagicMock
|
||||
resourceexplorer2_client.indexes = None
|
||||
resourceexplorer2_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
resourceexplorer2_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
resourceexplorer2_client.audited_partition = "aws"
|
||||
resourceexplorer2_client.region = AWS_REGION_US_EAST_1
|
||||
resourceexplorer2_client.index_arn_template = f"arn:{resourceexplorer2_client.audited_partition}:resource-explorer:{resourceexplorer2_client.region}:{resourceexplorer2_client.audited_account}:index"
|
||||
resourceexplorer2_client.__get_index_arn_template__ = mock.MagicMock(
|
||||
return_value=resourceexplorer2_client.index_arn_template
|
||||
)
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_service.ResourceExplorer2",
|
||||
new=resourceexplorer2_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_indexes_found.resourceexplorer2_indexes_found import (
|
||||
resourceexplorer2_indexes_found,
|
||||
)
|
||||
|
||||
check = resourceexplorer2_indexes_found()
|
||||
result = check.execute()
|
||||
|
||||
# Assertions
|
||||
assert len(result) == 0
|
||||
|
||||
+27
@@ -164,3 +164,30 @@ class Test_ssmincidents_enabled_with_plans:
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].resource_arn == REPLICATION_SET_ARN
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
def test_access_denied(self):
|
||||
ssmincidents_client = mock.MagicMock
|
||||
ssmincidents_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
ssmincidents_client.audited_partition = "aws"
|
||||
ssmincidents_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
ssmincidents_client.region = AWS_REGION_US_EAST_1
|
||||
ssmincidents_client.replication_set_arn_template = f"arn:{ssmincidents_client.audited_partition}:ssm-incidents:{ssmincidents_client.region}:{ssmincidents_client.audited_account}:replication-set"
|
||||
ssmincidents_client.__get_replication_set_arn_template__ = mock.MagicMock(
|
||||
return_value=ssmincidents_client.replication_set_arn_template
|
||||
)
|
||||
ssmincidents_client.replication_set = None
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.ssmincidents.ssmincidents_service.SSMIncidents",
|
||||
new=ssmincidents_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ssmincidents.ssmincidents_enabled_with_plans.ssmincidents_enabled_with_plans import (
|
||||
ssmincidents_enabled_with_plans,
|
||||
)
|
||||
|
||||
check = ssmincidents_enabled_with_plans()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
+27
@@ -144,3 +144,30 @@ class Test_trustedadvisor_errors_and_warnings:
|
||||
check = trustedadvisor_errors_and_warnings()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_access_denied(self):
|
||||
trustedadvisor_client = mock.MagicMock
|
||||
trustedadvisor_client.checks = []
|
||||
trustedadvisor_client.premium_support = None
|
||||
trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||
trustedadvisor_client.checks.append(
|
||||
Check(
|
||||
id=CHECK_NAME,
|
||||
name=CHECK_NAME,
|
||||
arn=CHECK_ARN,
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
status="not_available",
|
||||
)
|
||||
)
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor",
|
||||
trustedadvisor_client,
|
||||
):
|
||||
from prowler.providers.aws.services.trustedadvisor.trustedadvisor_errors_and_warnings.trustedadvisor_errors_and_warnings import (
|
||||
trustedadvisor_errors_and_warnings,
|
||||
)
|
||||
|
||||
check = trustedadvisor_errors_and_warnings()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
+27
@@ -86,3 +86,30 @@ class Test_trustedadvisor_premium_support_plan_subscribed:
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:trusted-advisor:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
|
||||
def test_access_denied(self):
|
||||
trustedadvisor_client = mock.MagicMock
|
||||
trustedadvisor_client.checks = []
|
||||
trustedadvisor_client.premium_support = None
|
||||
trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN
|
||||
trustedadvisor_client.audited_partition = "aws"
|
||||
trustedadvisor_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
# Set verify_premium_support_plans config
|
||||
trustedadvisor_client.audit_config = {"verify_premium_support_plans": True}
|
||||
trustedadvisor_client.account_arn_template = f"arn:{trustedadvisor_client.audited_partition}:trusted-advisor:{trustedadvisor_client.region}:{trustedadvisor_client.audited_account}:account"
|
||||
trustedadvisor_client.__get_account_arn_template__ = mock.MagicMock(
|
||||
return_value=trustedadvisor_client.account_arn_template
|
||||
)
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor",
|
||||
trustedadvisor_client,
|
||||
):
|
||||
from prowler.providers.aws.services.trustedadvisor.trustedadvisor_premium_support_plan_subscribed.trustedadvisor_premium_support_plan_subscribed import (
|
||||
trustedadvisor_premium_support_plan_subscribed,
|
||||
)
|
||||
|
||||
check = trustedadvisor_premium_support_plan_subscribed()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
Reference in New Issue
Block a user