fix(aws): not show findings when AccessDenieds (#3803)

This commit is contained in:
Sergio Garcia
2024-04-29 17:42:44 +02:00
committed by GitHub
parent b36152484d
commit 35c8ea5e3f
97 changed files with 2332 additions and 1126 deletions
+2
View File
@@ -178,6 +178,8 @@ class <Service>(ServiceParentClass):
f"{<item>.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" 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 #### Service Models
+4 -2
View File
@@ -345,8 +345,10 @@ def prowler():
global_provider, global_provider,
global_provider.output_options, global_provider.output_options,
) )
# Only display compliance table if there are findings and it is a default execution # Only display compliance table if there are findings (not all MANUAL) and it is a default execution
if findings and default_execution: if (
findings and not all(finding.status == "MANUAL" for finding in findings)
) and default_execution:
compliance_overview = False compliance_overview = False
if not compliance_framework: if not compliance_framework:
compliance_framework = get_available_compliance_frameworks(provider) compliance_framework = get_available_compliance_frameworks(provider)
+8 -5
View File
@@ -40,11 +40,13 @@ def display_summary_table(
entity_type = "Context" entity_type = "Context"
audited_entities = provider.identity.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 = { current = {
"Service": "", "Service": "",
"Provider": "", "Provider": "",
"Total": 0, "Total": 0,
"Pass": 0,
"Critical": 0, "Critical": 0,
"High": 0, "High": 0,
"Medium": 0, "Medium": 0,
@@ -70,9 +72,9 @@ def display_summary_table(
): ):
add_service_to_table(findings_table, current) add_service_to_table(findings_table, current)
current["Total"] = current["Muted"] = current["Critical"] = current[ current["Total"] = current["Pass"] = current["Muted"] = current[
"High" "Critical"
] = current["Medium"] = current["Low"] = 0 ] = current["High"] = current["Medium"] = current["Low"] = 0
current["Service"] = finding.check_metadata.ServiceName current["Service"] = finding.check_metadata.ServiceName
current["Provider"] = finding.check_metadata.Provider current["Provider"] = finding.check_metadata.Provider
@@ -83,6 +85,7 @@ def display_summary_table(
current["Muted"] += 1 current["Muted"] += 1
if finding.status == "PASS": if finding.status == "PASS":
pass_count += 1 pass_count += 1
current["Pass"] += 1
elif finding.status == "FAIL": elif finding.status == "FAIL":
fail_count += 1 fail_count += 1
if finding.check_metadata.Severity == "critical": 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}" current["Status"] = f"{Fore.RED}FAIL ({total_fails}){Style.RESET_ALL}"
else: 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["Provider"].append(current["Provider"])
findings_table["Service"].append(current["Service"]) findings_table["Service"].append(current["Service"])
+17 -10
View File
@@ -789,18 +789,25 @@ class AwsProvider(Provider):
def get_aws_enabled_regions(self, current_session: Session) -> set: def get_aws_enabled_regions(self, current_session: Session) -> set:
"""get_aws_enabled_regions returns a set of enabled AWS regions""" """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 enabled_regions = set()
service = "ec2" # With AllRegions=False we only get the enabled regions for the account
default_region = self.get_default_region(service) for region in ec2_client.describe_regions(AllRegions=False).get(
ec2_client = current_session.client(service, region_name=default_region) "Regions", []
):
enabled_regions.add(region.get("RegionName"))
enabled_regions = set() return enabled_regions
# With AllRegions=False we only get the enabled regions for the account except Exception as error:
for region in ec2_client.describe_regions(AllRegions=False).get("Regions", []): logger.error(
enabled_regions.add(region.get("RegionName")) f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return enabled_regions return set()
# TODO: review this function # TODO: review this function
# Maybe this should be done within the AwsProvider and not in __main__.py # Maybe this should be done within the AwsProvider and not in __main__.py
@@ -6,22 +6,26 @@ class account_maintain_different_contact_details_to_security_billing_and_operati
Check Check
): ):
def execute(self): def execute(self):
report = Check_Report_AWS(self.metadata()) findings = []
report.region = account_client.region if account_client.contact_base:
report.resource_id = account_client.audited_account report = Check_Report_AWS(self.metadata())
report.resource_arn = account_client.audited_account_arn report.region = account_client.region
report.resource_id = account_client.audited_account
report.resource_arn = account_client.audited_account_arn
if ( if (
len(account_client.contact_phone_numbers) len(account_client.contact_phone_numbers)
== account_client.number_of_contacts == account_client.number_of_contacts
and len(account_client.contact_names) == account_client.number_of_contacts and len(account_client.contact_names)
# This is because the primary contact has no email field == account_client.number_of_contacts
and len(account_client.contact_emails) # This is because the primary contact has no email field
== account_client.number_of_contacts - 1 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." report.status = "PASS"
else: report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact."
report.status = "FAIL" else:
report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact." report.status = "FAIL"
return [report] 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_security = self.__get_alternate_contact__("SECURITY")
self.contacts_operations = self.__get_alternate_contact__("OPERATIONS") self.contacts_operations = self.__get_alternate_contact__("OPERATIONS")
# Set of contact phone numbers if self.contact_base:
self.contact_phone_numbers = { # Set of contact phone numbers
self.contact_base.phone_number, self.contact_phone_numbers = {
self.contacts_billing.phone_number, self.contact_base.phone_number,
self.contacts_security.phone_number, self.contacts_billing.phone_number,
self.contacts_operations.phone_number, self.contacts_security.phone_number,
} self.contacts_operations.phone_number,
}
# Set of contact names # Set of contact names
self.contact_names = { self.contact_names = {
self.contact_base.name, self.contact_base.name,
self.contacts_billing.name, self.contacts_billing.name,
self.contacts_security.name, self.contacts_security.name,
self.contacts_operations.name, self.contacts_operations.name,
} }
# Set of contact emails # Set of contact emails
self.contact_emails = { self.contact_emails = {
self.contacts_billing.email, self.contacts_billing.email,
self.contacts_security.email, self.contacts_security.email,
self.contacts_operations.email, self.contacts_operations.email,
} }
def __get_contact_information__(self): def __get_contact_information__(self):
try: try:
@@ -53,10 +54,16 @@ class Account(AWSService):
phone_number=primary_account_contact.get("PhoneNumber"), phone_number=primary_account_contact.get("PhoneNumber"),
) )
except Exception as error: except Exception as error:
logger.error( if error.response["Error"]["Code"] == "AccessDeniedException":
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" logger.error(
) f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
return Contact(type="PRIMARY") )
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): def __get_alternate_contact__(self, contact_type: str):
try: try:
@@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from botocore.client import ClientError
from pydantic import BaseModel from pydantic import BaseModel
from prowler.lib.logger import logger from prowler.lib.logger import logger
@@ -37,6 +38,8 @@ class Backup(AWSService):
self.audit_resources, self.audit_resources,
) )
): ):
if self.backup_vaults is None:
self.backup_vaults = []
self.backup_vaults.append( self.backup_vaults.append(
BackupVault( BackupVault(
arn=configuration.get("BackupVaultArn"), 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -5,24 +5,24 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
class backup_vaults_encrypted(Check): class backup_vaults_encrypted(Check):
def execute(self): def execute(self):
findings = [] findings = []
if backup_client.backup_vaults:
for backup_vault in backup_client.backup_vaults: for backup_vault in backup_client.backup_vaults:
# By default we assume that the result is fail # By default we assume that the result is fail
report = Check_Report_AWS(self.metadata()) report = Check_Report_AWS(self.metadata())
report.status = "FAIL" 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"
report.status_extended = ( 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 report.resource_arn = backup_vault.arn
findings.append(report) 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 return findings
@@ -5,18 +5,19 @@ from prowler.providers.aws.services.backup.backup_client import backup_client
class backup_vaults_exist(Check): class backup_vaults_exist(Check):
def execute(self): def execute(self):
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if backup_client.backup_vaults is not None:
report.status = "FAIL" report = Check_Report_AWS(self.metadata())
report.status_extended = "No Backup Vault exist." report.status = "FAIL"
report.resource_arn = backup_client.backup_vault_arn_template report.status_extended = "No Backup Vault exist."
report.resource_id = backup_client.audited_account report.resource_arn = backup_client.backup_vault_arn_template
report.region = backup_client.region report.resource_id = backup_client.audited_account
if backup_client.backup_vaults: report.region = backup_client.region
report.status = "PASS" if backup_client.backup_vaults:
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}." report.status = "PASS"
report.resource_arn = backup_client.backup_vaults[0].arn report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
report.resource_id = backup_client.backup_vaults[0].name report.resource_arn = backup_client.backup_vaults[0].arn
report.region = backup_client.backup_vaults[0].region 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 return findings
@@ -8,28 +8,29 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudtrail_bucket_requires_mfa_delete(Check): class cloudtrail_bucket_requires_mfa_delete(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.is_logging: for trail in cloudtrail_client.trails.values():
trail_bucket_is_in_account = False if trail.is_logging:
trail_bucket = trail.s3_bucket trail_bucket_is_in_account = False
report = Check_Report_AWS(self.metadata()) trail_bucket = trail.s3_bucket
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "FAIL" report.resource_tags = trail.tags
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled." report.status = "FAIL"
for bucket in s3_client.buckets: report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled."
if trail_bucket == bucket.name: for bucket in s3_client.buckets:
trail_bucket_is_in_account = True if trail_bucket == bucket.name:
if bucket.mfa_delete: trail_bucket_is_in_account = True
report.status = "PASS" if bucket.mfa_delete:
report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled." report.status = "PASS"
# check if trail bucket is a cross account bucket report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled."
if not trail_bucket_is_in_account: # check if trail bucket is a cross account bucket
report.status = "MANUAL" if not trail_bucket_is_in_account:
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." 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 return findings
@@ -11,37 +11,38 @@ maximum_time_without_logging = 1
class cloudtrail_cloudwatch_logging_enabled(Check): class cloudtrail_cloudwatch_logging_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.name: for trail in cloudtrail_client.trails.values():
report = Check_Report_AWS(self.metadata()) if trail.name:
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "PASS" report.resource_tags = trail.tags
if trail.is_multiregion: report.status = "PASS"
report.status_extended = ( if trail.is_multiregion:
f"Multiregion trail {trail.name} has been logging the last 24h." report.status_extended = f"Multiregion trail {trail.name} has been logging the last 24h."
) else:
else: report.status_extended = f"Single region trail {trail.name} has been logging the last 24h."
report.status_extended = f"Single region trail {trail.name} has been logging the last 24h." if trail.latest_cloudwatch_delivery_time:
if trail.latest_cloudwatch_delivery_time: last_log_delivery = (
last_log_delivery = ( datetime.now().replace(tzinfo=timezone.utc)
datetime.now().replace(tzinfo=timezone.utc) - trail.latest_cloudwatch_delivery_time
- trail.latest_cloudwatch_delivery_time )
) if last_log_delivery > timedelta(
if last_log_delivery > timedelta(days=maximum_time_without_logging): 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" report.status = "FAIL"
if trail.is_multiregion: 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: else:
report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h." report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs."
else: findings.append(report)
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)
return findings return findings
@@ -7,19 +7,18 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
class cloudtrail_insights_exist(Check): class cloudtrail_insights_exist(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.is_logging: for trail in cloudtrail_client.trails.values():
report = Check_Report_AWS(self.metadata()) if trail.is_logging:
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "FAIL" report.resource_tags = trail.tags
report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging." report.status = "FAIL"
if trail.has_insight_selectors: report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging."
report.status = "PASS" if trail.has_insight_selectors:
report.status_extended = ( report.status = "PASS"
f"Trail {trail.name} has insight selectors and it is logging." report.status_extended = f"Trail {trail.name} has insight selectors and it is logging."
) findings.append(report)
findings.append(report)
return findings return findings
@@ -7,32 +7,29 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
class cloudtrail_kms_encryption_enabled(Check): class cloudtrail_kms_encryption_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.name: for trail in cloudtrail_client.trails.values():
report = Check_Report_AWS(self.metadata()) if trail.name:
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "FAIL" report.resource_tags = trail.tags
if trail.is_multiregion: report.status = "FAIL"
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 trail.is_multiregion: if trail.is_multiregion:
report.status_extended = ( report.status_extended = (
f"Multiregion trail {trail.name} has encryption enabled." f"Multiregion trail {trail.name} has encryption disabled."
) )
else: else:
report.status_extended = ( 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 return findings
@@ -7,26 +7,25 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
class cloudtrail_log_file_validation_enabled(Check): class cloudtrail_log_file_validation_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.name: for trail in cloudtrail_client.trails.values():
report = Check_Report_AWS(self.metadata()) if trail.name:
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "FAIL" report.resource_tags = trail.tags
if trail.is_multiregion: report.status = "FAIL"
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 trail.is_multiregion: 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: else:
report.status_extended = f"Single region trail {trail.name} log file validation enabled." report.status_extended = f"Single region trail {trail.name} log file validation disabled."
findings.append(report) 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 return findings
@@ -8,35 +8,36 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudtrail_logs_s3_bucket_access_logging_enabled(Check): class cloudtrail_logs_s3_bucket_access_logging_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.name: for trail in cloudtrail_client.trails.values():
trail_bucket_is_in_account = False if trail.name:
trail_bucket = trail.s3_bucket trail_bucket_is_in_account = False
report = Check_Report_AWS(self.metadata()) trail_bucket = trail.s3_bucket
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "FAIL" report.resource_tags = trail.tags
if trail.is_multiregion: report.status = "FAIL"
report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." if trail.is_multiregion:
else: report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." else:
for bucket in s3_client.buckets: report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}."
if trail_bucket == bucket.name: for bucket in s3_client.buckets:
trail_bucket_is_in_account = True if trail_bucket == bucket.name:
if bucket.logging: trail_bucket_is_in_account = True
report.status = "PASS" if bucket.logging:
if trail.is_multiregion: report.status = "PASS"
report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." if trail.is_multiregion:
else: report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}."
report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." else:
break 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 # check if trail is delivering logs in a cross account bucket
if not trail_bucket_is_in_account: if not trail_bucket_is_in_account:
report.status = "MANUAL" 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." 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) findings.append(report)
return findings return findings
@@ -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): class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
if trail.name: for trail in cloudtrail_client.trails.values():
trail_bucket_is_in_account = False if trail.name:
trail_bucket = trail.s3_bucket trail_bucket_is_in_account = False
report = Check_Report_AWS(self.metadata()) trail_bucket = trail.s3_bucket
report.region = trail.region report = Check_Report_AWS(self.metadata())
report.resource_id = trail.name report.region = trail.region
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
report.status = "PASS" report.resource_tags = trail.tags
if trail.is_multiregion: report.status = "PASS"
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible." if trail.is_multiregion:
else: report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible."
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible." else:
for bucket in s3_client.buckets: report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible."
# 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 bucket in s3_client.buckets:
# (for example due to a SCP) we are going to try access an attribute from a None type # 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
if trail_bucket == bucket.name: # (for example due to a SCP) we are going to try access an attribute from a None type
trail_bucket_is_in_account = True if trail_bucket == bucket.name:
if bucket.acl_grantees: trail_bucket_is_in_account = True
for grant in bucket.acl_grantees: if bucket.acl_grantees:
if ( for grant in bucket.acl_grantees:
grant.URI if (
== "http://acs.amazonaws.com/groups/global/AllUsers" grant.URI
): == "http://acs.amazonaws.com/groups/global/AllUsers"
report.status = "FAIL" ):
if trail.is_multiregion: report.status = "FAIL"
report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible." if trail.is_multiregion:
else: report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible."
report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible." else:
break report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible."
# check if trail bucket is a cross account bucket break
if not trail_bucket_is_in_account: # check if trail bucket is a cross account bucket
report.status = "MANUAL" if not trail_bucket_is_in_account:
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." report.status = "MANUAL"
findings.append(report) 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 return findings
@@ -7,36 +7,35 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
class cloudtrail_multi_region_enabled(Check): class cloudtrail_multi_region_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for region in cloudtrail_client.regional_clients.keys(): if cloudtrail_client.trails is not None:
report = Check_Report_AWS(self.metadata()) for region in cloudtrail_client.regional_clients.keys():
report.region = region report = Check_Report_AWS(self.metadata())
for trail in cloudtrail_client.trails.values(): report.region = region
if trail.region == region or trail.is_multiregion: for trail in cloudtrail_client.trails.values():
if trail.is_logging: if trail.region == region or trail.is_multiregion:
report.status = "PASS" if trail.is_logging:
report.resource_id = trail.name report.status = "PASS"
report.resource_arn = trail.arn report.resource_id = trail.name
report.resource_tags = trail.tags report.resource_arn = trail.arn
if trail.is_multiregion: report.resource_tags = trail.tags
report.status_extended = ( if trail.is_multiregion:
f"Trail {trail.name} is multiregion and it is logging." 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: else:
report.status_extended = f"Trail {trail.name} is not multiregion and it is logging." report.status = "FAIL"
# Since there exists a logging trail in that region there is no point in checking the remaining trails report.status_extended = (
# Store the finding and exit the loop "No CloudTrail trails enabled and logging were found."
findings.append(report) )
break report.resource_arn = (
else: cloudtrail_client.__get_trail_arn_template__(region)
report.status = "FAIL" )
report.status_extended = ( report.resource_id = cloudtrail_client.audited_account
"No CloudTrail trails enabled and logging were found." # If there are no trails logging it is needed to store the FAIL once all the trails have been checked
) if report.status == "FAIL":
report.resource_arn = ( findings.append(report)
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 return findings
@@ -7,48 +7,49 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
class cloudtrail_multi_region_enabled_logging_management_events(Check): class cloudtrail_multi_region_enabled_logging_management_events(Check):
def execute(self): def execute(self):
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if cloudtrail_client.trails is not None:
report.status = "FAIL" report = Check_Report_AWS(self.metadata())
report.status_extended = ( report.status = "FAIL"
"No trail found with multi-region enabled and logging management events." report.status_extended = "No trail found with multi-region enabled and logging management events."
) report.region = cloudtrail_client.region
report.region = cloudtrail_client.region report.resource_id = cloudtrail_client.audited_account
report.resource_id = cloudtrail_client.audited_account report.resource_arn = cloudtrail_client.trail_arn_template
report.resource_arn = cloudtrail_client.trail_arn_template
for trail in cloudtrail_client.trails.values(): for trail in cloudtrail_client.trails.values():
if trail.is_logging: if trail.is_logging:
if trail.is_multiregion: if trail.is_multiregion:
for event in trail.data_events: for event in trail.data_events:
# Classic event selectors # Classic event selectors
if not event.is_advanced: if not event.is_advanced:
# Check if trail has IncludeManagementEvents and ReadWriteType is All # Check if trail has IncludeManagementEvents and ReadWriteType is All
if ( if (
event.event_selector["ReadWriteType"] == "All" event.event_selector["ReadWriteType"] == "All"
and event.event_selector["IncludeManagementEvents"] and event.event_selector["IncludeManagementEvents"]
): ):
report.region = trail.region report.region = trail.region
report.resource_id = trail.name report.resource_id = trail.name
report.resource_arn = trail.arn report.resource_arn = trail.arn
report.resource_tags = trail.tags report.resource_tags = trail.tags
report.status = "PASS" 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." 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 # Advanced event selectors
elif event.is_advanced: elif event.is_advanced:
if event.event_selector.get( if event.event_selector.get(
"Name" "Name"
) == "Management events selector" and all( ) == "Management events selector" and all(
[ [
field["Field"] != "readOnly" field["Field"] != "readOnly"
for field in event.event_selector["FieldSelectors"] for field in event.event_selector[
] "FieldSelectors"
): ]
report.region = trail.region ]
report.resource_id = trail.name ):
report.resource_arn = trail.arn report.region = trail.region
report.resource_tags = trail.tags report.resource_id = trail.name
report.status = "PASS" report.resource_arn = trail.arn
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled." report.resource_tags = trail.tags
findings.append(report) 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 return findings
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudtrail_s3_dataevents_read_enabled(Check): class cloudtrail_s3_dataevents_read_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
for data_event in trail.data_events: for trail in cloudtrail_client.trails.values():
# classic event selectors for data_event in trail.data_events:
if not data_event.is_advanced: # classic event selectors
# Check if trail has a data event for all S3 Buckets for read if not data_event.is_advanced:
if ( # Check if trail has a data event for all S3 Buckets for read
data_event.event_selector["ReadWriteType"] == "ReadOnly" if (
or data_event.event_selector["ReadWriteType"] == "All" 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 ( for resource in data_event.event_selector["DataResources"]:
f"arn:{cloudtrail_client.audited_partition}:s3" if "AWS::S3::Object" == resource["Type"] and (
in resource["Values"] f"arn:{cloudtrail_client.audited_partition}:s3"
or f"arn:{cloudtrail_client.audited_partition}:s3:::" in resource["Values"]
in resource["Values"] or f"arn:{cloudtrail_client.audited_partition}:s3:::"
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" in resource["Values"]
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 = Check_Report_AWS(self.metadata())
report.region = trail.region report.region = trail.region
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_read_enabled(Check):
report.resource_arn = trail.arn report.resource_arn = trail.arn
report.resource_tags = trail.tags report.resource_tags = trail.tags
report.status = "PASS" 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) findings.append(report)
# advanced event selectors if not findings and (
elif data_event.is_advanced: s3_client.buckets or cloudtrail_client.provider.scan_unused_services
for field_selector in data_event.event_selector["FieldSelectors"]: ):
if ( report = Check_Report_AWS(self.metadata())
field_selector["Field"] == "resources.type" report.region = cloudtrail_client.region
and field_selector["Equals"][0] == "AWS::S3::Object" report.resource_arn = cloudtrail_client.trail_arn_template
): report.resource_id = cloudtrail_client.audited_account
report = Check_Report_AWS(self.metadata()) report.status = "FAIL"
report.region = trail.region report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
report.resource_id = trail.name findings.append(report)
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)
return findings return findings
@@ -8,23 +8,41 @@ from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudtrail_s3_dataevents_write_enabled(Check): class cloudtrail_s3_dataevents_write_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for trail in cloudtrail_client.trails.values(): if cloudtrail_client.trails is not None:
for data_event in trail.data_events: for trail in cloudtrail_client.trails.values():
# Classic event selectors for data_event in trail.data_events:
if not data_event.is_advanced: # Classic event selectors
# Check if trail has a data event for all S3 Buckets for write if not data_event.is_advanced:
if ( # Check if trail has a data event for all S3 Buckets for write
data_event.event_selector["ReadWriteType"] == "All" if (
or data_event.event_selector["ReadWriteType"] == "WriteOnly" 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 ( for resource in data_event.event_selector["DataResources"]:
f"arn:{cloudtrail_client.audited_partition}:s3" if "AWS::S3::Object" == resource["Type"] and (
in resource["Values"] f"arn:{cloudtrail_client.audited_partition}:s3"
or f"arn:{cloudtrail_client.audited_partition}:s3:::" in resource["Values"]
in resource["Values"] or f"arn:{cloudtrail_client.audited_partition}:s3:::"
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" in resource["Values"]
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 = Check_Report_AWS(self.metadata())
report.region = trail.region report.region = trail.region
@@ -32,31 +50,16 @@ class cloudtrail_s3_dataevents_write_enabled(Check):
report.resource_arn = trail.arn report.resource_arn = trail.arn
report.resource_tags = trail.tags report.resource_tags = trail.tags
report.status = "PASS" 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) findings.append(report)
# Advanced event selectors if not findings and (
elif data_event.is_advanced: s3_client.buckets or cloudtrail_client.provider.scan_unused_services
for field_selector in data_event.event_selector["FieldSelectors"]: ):
if ( report = Check_Report_AWS(self.metadata())
field_selector["Field"] == "resources.type" report.region = cloudtrail_client.region
and field_selector["Equals"][0] == "AWS::S3::Object" report.resource_arn = cloudtrail_client.trail_arn_template
): report.resource_id = cloudtrail_client.audited_account
report = Check_Report_AWS(self.metadata()) report.status = "FAIL"
report.region = trail.region report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
report.resource_id = trail.name findings.append(report)
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)
return findings 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.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail"
self.trails = {} self.trails = {}
self.__threading_call__(self.__get_trails__) self.__threading_call__(self.__get_trails__)
self.__get_trail_status__() if self.trails:
self.__get_insight_selectors__() self.__get_trail_status__()
self.__get_event_selectors__() self.__get_insight_selectors__()
self.__list_tags_for_resource__() self.__get_event_selectors__()
self.__list_tags_for_resource__()
def __get_trail_arn_template__(self, region): def __get_trail_arn_template__(self, region):
return ( return (
@@ -45,6 +46,8 @@ class Cloudtrail(AWSService):
kms_key_id = trail["KmsKeyId"] kms_key_id = trail["KmsKeyId"]
if "CloudWatchLogsLogGroupArn" in trail: if "CloudWatchLogsLogGroupArn" in trail:
log_group_arn = trail["CloudWatchLogsLogGroupArn"] log_group_arn = trail["CloudWatchLogsLogGroupArn"]
if self.trails is None:
self.trails = {}
self.trails[trail["TrailARN"]] = Trail( self.trails[trail["TrailARN"]] = Trail(
name=trail["Name"], name=trail["Name"],
is_multiregion=trail["IsMultiRegionTrail"], is_multiregion=trail["IsMultiRegionTrail"],
@@ -61,12 +64,24 @@ class Cloudtrail(AWSService):
has_insight_selectors=trail.get("HasInsightSelectors"), has_insight_selectors=trail.get("HasInsightSelectors"),
) )
if trails_count == 0: if trails_count == 0:
if self.trails is None:
self.trails = {}
self.trails[self.__get_trail_arn_template__(regional_client.region)] = ( self.trails[self.__get_trail_arn_template__(regional_client.region)] = (
Trail( Trail(
region=regional_client.region, 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -5,17 +5,20 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class cloudwatch_cross_account_sharing_disabled(Check): class cloudwatch_cross_account_sharing_disabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if iam_client.roles is not None:
report.status = "PASS" report = Check_Report_AWS(self.metadata())
report.status_extended = "CloudWatch doesn't allow cross-account sharing." report.status = "PASS"
report.resource_arn = iam_client.role_arn_template report.status_extended = "CloudWatch doesn't allow cross-account sharing."
report.resource_id = iam_client.audited_account report.resource_arn = iam_client.role_arn_template
report.region = iam_client.region report.resource_id = iam_client.audited_account
for role in iam_client.roles: report.region = iam_client.region
if role.name == "CloudWatch-CrossAccountSharingRole": for role in iam_client.roles:
report.resource_arn = role.arn if role.name == "CloudWatch-CrossAccountSharingRole":
report.resource_id = role.name report.resource_arn = role.arn
report.status = "FAIL" report.resource_id = role.name
report.status_extended = "CloudWatch has allowed cross-account sharing." report.status = "FAIL"
findings.append(report) report.status_extended = (
"CloudWatch has allowed cross-account sharing."
)
findings.append(report)
return findings return findings
@@ -5,19 +5,18 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
class cloudwatch_log_group_kms_encryption_enabled(Check): class cloudwatch_log_group_kms_encryption_enabled(Check):
def execute(self): def execute(self):
findings = [] findings = []
for log_group in logs_client.log_groups: if logs_client.log_groups:
report = Check_Report_AWS(self.metadata()) for log_group in logs_client.log_groups:
report.region = log_group.region report = Check_Report_AWS(self.metadata())
report.resource_id = log_group.name report.region = log_group.region
report.resource_arn = log_group.arn report.resource_id = log_group.name
report.resource_tags = log_group.tags report.resource_arn = log_group.arn
if log_group.kms_id: report.resource_tags = log_group.tags
report.status = "PASS" if log_group.kms_id:
report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated." report.status = "PASS"
else: report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated."
report.status = "FAIL" else:
report.status_extended = ( report.status = "FAIL"
f"Log Group {log_group.name} does not have AWS KMS keys associated." report.status_extended = f"Log Group {log_group.name} does not have AWS KMS keys associated."
) findings.append(report)
findings.append(report)
return findings return findings
@@ -11,78 +11,86 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
class cloudwatch_log_group_no_secrets_in_logs(Check): class cloudwatch_log_group_no_secrets_in_logs(Check):
def execute(self): def execute(self):
findings = [] findings = []
for log_group in logs_client.log_groups: if logs_client.log_groups:
report = Check_Report_AWS(self.metadata()) for log_group in logs_client.log_groups:
report.status = "PASS" report = Check_Report_AWS(self.metadata())
report.status_extended = f"No secrets found in {log_group.name} log group." report.status = "PASS"
report.region = log_group.region report.status_extended = (
report.resource_id = log_group.name f"No secrets found in {log_group.name} log group."
report.resource_arn = log_group.arn )
log_group_secrets = [] report.region = log_group.region
if log_group.log_streams: report.resource_id = log_group.name
for log_stream_name in log_group.log_streams: report.resource_arn = log_group.arn
log_stream_secrets = {} log_group_secrets = []
log_stream_data = "\n".join( if log_group.log_streams:
[ for log_stream_name in log_group.log_streams:
dumps(event["message"]) log_stream_secrets = {}
for event in log_group.log_streams[log_stream_name] log_stream_data = "\n".join(
]
)
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()}" dumps(event["message"])
for timestamp in log_stream_secrets for event in log_group.log_streams[log_stream_name]
] ]
) )
log_group_secrets.append( log_stream_secrets_output = detect_secrets_scan(log_stream_data)
f"in log stream {log_stream_name} {secrets_string}"
) if log_stream_secrets_output:
if log_group_secrets: for secret in log_stream_secrets_output:
secrets_string = "; ".join(log_group_secrets) flagged_event = log_group.log_streams[log_stream_name][
report.status = "FAIL" secret["line_number"] - 1
report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}." ]
findings.append(report) 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 return findings
@@ -10,23 +10,24 @@ class cloudwatch_log_group_retention_policy_specific_days_enabled(Check):
specific_retention_days = logs_client.audit_config.get( specific_retention_days = logs_client.audit_config.get(
"log_group_retention_days", 365 "log_group_retention_days", 365
) )
for log_group in logs_client.log_groups: if logs_client.log_groups:
report = Check_Report_AWS(self.metadata()) for log_group in logs_client.log_groups:
report.region = log_group.region report = Check_Report_AWS(self.metadata())
report.resource_id = log_group.name report.region = log_group.region
report.resource_arn = log_group.arn report.resource_id = log_group.name
report.resource_tags = log_group.tags report.resource_arn = log_group.arn
if ( report.resource_tags = log_group.tags
log_group.never_expire is False if (
and log_group.retention_days < specific_retention_days 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)." report.status = "FAIL"
else: report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)."
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: 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." report.status = "PASS"
findings.append(report) 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 return findings
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change
def execute(self): 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.?" pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_authentication_failures(Check):
def execute(self): def execute(self):
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?" pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_aws_organizations_changes(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Chec
def execute(self): def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?" pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,22 +15,25 @@ class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template 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( report = check_cloudwatch_log_metric_filter(
pattern, pattern,
cloudtrail_client.trails, cloudtrail_client.trails,
logs_client.metric_filters, logs_client.metric_filters,
cloudwatch_client.metric_alarms, cloudwatch_client.metric_alarms,
report, report,
) )
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_policy_changes(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_root_usage(Check):
def execute(self): def execute(self):
pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?" pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_security_group_changes(Check):
def execute(self): 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.?" 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 = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_sign_in_without_mfa(Check):
def execute(self): def execute(self):
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?" pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_unauthorized_api_calls(Check):
def execute(self): def execute(self):
pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?" pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?"
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if (
report.status = "FAIL" cloudtrail_client.trails is not None
report.status_extended = ( and logs_client.metric_filters is not None
"No CloudWatch log groups found with metric filters or alarms associated." and cloudwatch_client.metric_alarms is not None
) ):
report.region = logs_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = logs_client.audited_account report.status = "FAIL"
report.resource_arn = logs_client.log_group_arn_template report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report = check_cloudwatch_log_metric_filter( report.region = logs_client.region
pattern, report.resource_id = logs_client.audited_account
cloudtrail_client.trails, report.resource_arn = logs_client.log_group_arn_template
logs_client.metric_filters, report = check_cloudwatch_log_metric_filter(
cloudwatch_client.metric_alarms, pattern,
report, cloudtrail_client.trails,
) logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
findings.append(report) findings.append(report)
return findings return findings
@@ -16,7 +16,8 @@ class CloudWatch(AWSService):
super().__init__(__class__.__name__, provider) super().__init__(__class__.__name__, provider)
self.metric_alarms = [] self.metric_alarms = []
self.__threading_call__(self.__describe_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): def __describe_alarms__(self, regional_client):
logger.info("CloudWatch - Describing alarms...") logger.info("CloudWatch - Describing alarms...")
@@ -33,6 +34,8 @@ class CloudWatch(AWSService):
namespace = None namespace = None
if "Namespace" in alarm: if "Namespace" in alarm:
namespace = alarm["Namespace"] namespace = alarm["Namespace"]
if self.metric_alarms is None:
self.metric_alarms = []
self.metric_alarms.append( self.metric_alarms.append(
MetricAlarm( MetricAlarm(
arn=alarm["AlarmArn"], arn=alarm["AlarmArn"],
@@ -42,6 +45,17 @@ class CloudWatch(AWSService):
region=regional_client.region, 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -72,15 +86,16 @@ class Logs(AWSService):
self.log_groups = [] self.log_groups = []
self.__threading_call__(self.__describe_metric_filters__) self.__threading_call__(self.__describe_metric_filters__)
self.__threading_call__(self.__describe_log_groups__) self.__threading_call__(self.__describe_log_groups__)
if ( if self.log_groups:
"cloudwatch_log_group_no_secrets_in_logs" if (
in provider.audit_metadata.expected_checks "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.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__() self.__threading_call__(self.__get_log_events__)
self.__list_tags_for_resource__()
def __describe_metric_filters__(self, regional_client): def __describe_metric_filters__(self, regional_client):
logger.info("CloudWatch Logs - Describing metric filters...") logger.info("CloudWatch Logs - Describing metric filters...")
@@ -94,6 +109,8 @@ class Logs(AWSService):
if not self.audit_resources or ( if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources) is_resource_filtered(arn, self.audit_resources)
): ):
if self.metric_filters is None:
self.metric_filters = []
self.metric_filters.append( self.metric_filters.append(
MetricFilter( MetricFilter(
arn=arn, arn=arn,
@@ -104,6 +121,17 @@ class Logs(AWSService):
region=regional_client.region, 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -126,6 +154,8 @@ class Logs(AWSService):
if not retention_days: if not retention_days:
never_expire = True never_expire = True
retention_days = 9999 retention_days = 9999
if self.log_groups is None:
self.log_groups = []
self.log_groups.append( self.log_groups.append(
LogGroup( LogGroup(
arn=log_group["arn"], arn=log_group["arn"],
@@ -136,6 +166,17 @@ class Logs(AWSService):
region=regional_client.region, 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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 # 1. Iterate for CloudWatch Log Group in CloudTrail trails
log_groups = [] log_groups = []
for trail in trails.values(): if trails is not None:
if trail.log_group_arn: for trail in trails.values():
log_groups.append(trail.log_group_arn.split(":")[6]) if trail.log_group_arn:
log_groups.append(trail.log_group_arn.split(":")[6])
# 2. Describe metric filters for previous log groups # 2. Describe metric filters for previous log groups
for metric_filter in metric_filters: for metric_filter in metric_filters:
if metric_filter.log_group in log_groups: if metric_filter.log_group in log_groups:
@@ -52,6 +52,11 @@ class FMS(AWSService):
): ):
# FMS is not enabled in this account # FMS is not enabled in this account
self.fms_admin_account = False 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: except Exception as error:
logger.error( logger.error(
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
@@ -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): class iam_password_policy_expires_passwords_within_90_days_or_less(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if password policy expiration exists
if iam_client.password_policy.max_age: if iam_client.password_policy.max_age:
if iam_client.password_policy.max_age <= 90: 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: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = "Password expiration is not set." report.status_extended = "Password expiration is not set."
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_lowercase(Check): class iam_password_policy_lowercase(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if lowercase flag is set
if iam_client.password_policy.lowercase: if iam_client.password_policy.lowercase:
report.status = "PASS" report.status = "PASS"
@@ -20,8 +19,5 @@ class iam_password_policy_lowercase(Check):
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = "IAM password policy does not require at least one lowercase letter." report.status_extended = "IAM password policy does not require at least one lowercase letter."
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_minimum_length_14(Check): class iam_password_policy_minimum_length_14(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check password policy length
if ( if (
iam_client.password_policy.length iam_client.password_policy.length
@@ -23,8 +22,5 @@ class iam_password_policy_minimum_length_14(Check):
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = "IAM password policy does not require minimum length of 14 characters." report.status_extended = "IAM password policy does not require minimum length of 14 characters."
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_number(Check): class iam_password_policy_number(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if number flag is set
if iam_client.password_policy.numbers: if iam_client.password_policy.numbers:
report.status = "PASS" report.status = "PASS"
@@ -22,8 +21,5 @@ class iam_password_policy_number(Check):
report.status_extended = ( report.status_extended = (
"IAM password policy does not require at least one number." "IAM password policy does not require at least one number."
) )
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_reuse_24(Check): class iam_password_policy_reuse_24(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if reuse prevention flag is set
if ( if (
iam_client.password_policy.reuse_prevention iam_client.password_policy.reuse_prevention
@@ -25,8 +24,5 @@ class iam_password_policy_reuse_24(Check):
report.status_extended = ( report.status_extended = (
"IAM password policy reuse prevention is less than 24 or not set." "IAM password policy reuse prevention is less than 24 or not set."
) )
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_symbol(Check): class iam_password_policy_symbol(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if symbol flag is set
if iam_client.password_policy.symbols: if iam_client.password_policy.symbols:
report.status = "PASS" report.status = "PASS"
@@ -22,8 +21,5 @@ class iam_password_policy_symbol(Check):
report.status_extended = ( report.status_extended = (
"IAM password policy does not require at least one symbol." "IAM password policy does not require at least one symbol."
) )
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,12 +5,11 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_password_policy_uppercase(Check): class iam_password_policy_uppercase(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] 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: 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 # Check if uppercase flag is set
if iam_client.password_policy.uppercase: if iam_client.password_policy.uppercase:
report.status = "PASS" report.status = "PASS"
@@ -20,8 +19,5 @@ class iam_password_policy_uppercase(Check):
else: else:
report.status = "FAIL" report.status = "FAIL"
report.status_extended = "IAM password policy does not require at least one uppercase letter." report.status_extended = "IAM password policy does not require at least one uppercase letter."
else: findings.append(report)
report.status = "FAIL"
report.status_extended = "Password policy cannot be found."
findings.append(report)
return findings return findings
@@ -5,24 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_role_administratoraccess_policy(Check): class iam_role_administratoraccess_policy(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] findings = []
for role in iam_client.roles: if iam_client.roles:
if ( for role in iam_client.roles:
not role.is_service_role if (
): # Avoid service roles since they cannot be modified by the user not role.is_service_role
report = Check_Report_AWS(self.metadata()) ): # Avoid service roles since they cannot be modified by the user
report.region = iam_client.region report = Check_Report_AWS(self.metadata())
report.resource_arn = role.arn report.region = iam_client.region
report.resource_id = role.name report.resource_arn = role.arn
report.resource_tags = role.tags report.resource_id = role.name
report.status = "PASS" report.resource_tags = role.tags
report.status_extended = ( report.status = "PASS"
f"IAM Role {role.name} does not have AdministratorAccess policy." report.status_extended = f"IAM Role {role.name} does not have AdministratorAccess policy."
) for policy in role.attached_policies:
for policy in role.attached_policies: if policy["PolicyName"] == "AdministratorAccess":
if policy["PolicyName"] == "AdministratorAccess": report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached." report.status = "FAIL"
report.status = "FAIL"
findings.append(report) findings.append(report)
return findings return findings
@@ -5,78 +5,82 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_role_cross_account_readonlyaccess_policy(Check): class iam_role_cross_account_readonlyaccess_policy(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] findings = []
for role in iam_client.roles: if iam_client.roles:
if ( for role in iam_client.roles:
not role.is_service_role if (
): # Avoid service roles since they cannot be modified by the user not role.is_service_role
report = Check_Report_AWS(self.metadata()) ): # Avoid service roles since they cannot be modified by the user
report.region = iam_client.region report = Check_Report_AWS(self.metadata())
report.resource_arn = role.arn report.region = iam_client.region
report.resource_id = role.name report.resource_arn = role.arn
report.resource_tags = role.tags report.resource_id = role.name
report.status = "PASS" report.resource_tags = role.tags
report.status_extended = ( report.status = "PASS"
f"IAM Role {role.name} does not have ReadOnlyAccess policy." report.status_extended = (
) f"IAM Role {role.name} does not have ReadOnlyAccess policy."
for policy in role.attached_policies: )
if policy["PolicyName"] == "ReadOnlyAccess": for policy in role.attached_policies:
report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account." if policy["PolicyName"] == "ReadOnlyAccess":
cross_account_access = False report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account."
if isinstance(role.assume_role_policy["Statement"], list): cross_account_access = False
for statement in role.assume_role_policy["Statement"]: if isinstance(role.assume_role_policy["Statement"], list):
if not cross_account_access: for statement in role.assume_role_policy["Statement"]:
if ( if not cross_account_access:
statement["Effect"] == "Allow" if (
and "AWS" in statement["Principal"] statement["Effect"] == "Allow"
): and "AWS" in statement["Principal"]
if isinstance(
statement["Principal"]["AWS"], list
): ):
for aws_account in statement["Principal"][ if isinstance(
"AWS" 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 ( if (
iam_client.audited_account iam_client.audited_account
not in aws_account not in statement["Principal"]["AWS"]
or "*" == aws_account or "*"
== statement["Principal"]["AWS"]
): ):
cross_account_access = True 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 ( if (
iam_client.audited_account iam_client.audited_account
not in statement["Principal"]["AWS"] not in aws_account
or "*" == statement["Principal"]["AWS"] or "*" == aws_account
): ):
cross_account_access = True cross_account_access = True
else: break
break else:
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 ( if (
iam_client.audited_account iam_client.audited_account
not in aws_account not in statement["Principal"]["AWS"]
or "*" == aws_account or "*" == statement["Principal"]["AWS"]
): ):
cross_account_access = True cross_account_access = True
break if cross_account_access:
else: report.status = "FAIL"
if ( report.status_extended = f"IAM Role {role.name} gives cross account read-only access."
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."
findings.append(report) findings.append(report)
return findings return findings
@@ -8,36 +8,37 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_role_cross_service_confused_deputy_prevention(Check): class iam_role_cross_service_confused_deputy_prevention(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] findings = []
for role in iam_client.roles: if iam_client.roles:
# This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed) for role in iam_client.roles:
if role.is_service_role and "aws-service-role" not in role.arn: # This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed)
report = Check_Report_AWS(self.metadata()) if role.is_service_role and "aws-service-role" not in role.arn:
report.region = iam_client.region report = Check_Report_AWS(self.metadata())
report.resource_arn = role.arn report.region = iam_client.region
report.resource_id = role.name report.resource_arn = role.arn
report.resource_tags = role.tags report.resource_id = role.name
report.status = "FAIL" report.resource_tags = role.tags
report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack." report.status = "FAIL"
for statement in role.assume_role_policy["Statement"]: report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack."
if ( for statement in role.assume_role_policy["Statement"]:
statement["Effect"] == "Allow" if (
and ( statement["Effect"] == "Allow"
"sts:AssumeRole" in statement["Action"] and (
or "sts:*" in statement["Action"] "sts:AssumeRole" in statement["Action"]
or "*" 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"] # Need to make sure we are checking the part of the assume role policy document that provides a service access
# Check to see if the appropriate condition statements have been implemented and "Service" in statement["Principal"]
and "Condition" in statement # Check to see if the appropriate condition statements have been implemented
and is_condition_block_restrictive( and "Condition" in statement
statement["Condition"], iam_client.audited_account 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." report.status = "PASS"
break 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 return findings
@@ -5,15 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_securityaudit_role_created(Check): class iam_securityaudit_role_created(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if iam_client.entities_role_attached_to_securityaudit_policy is not None:
report.region = iam_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = "SecurityAudit" report.region = iam_client.region
report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit" report.resource_id = "SecurityAudit"
if iam_client.entities_role_attached_to_securityaudit_policy: report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit"
report.status = "PASS" if iam_client.entities_role_attached_to_securityaudit_policy:
report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}." report.status = "PASS"
else: report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}."
report.status = "FAIL" else:
report.status_extended = "SecurityAudit policy is not attached to any role." report.status = "FAIL"
findings.append(report) report.status_extended = (
"SecurityAudit policy is not attached to any role."
)
findings.append(report)
return findings return findings
+128 -85
View File
@@ -117,6 +117,16 @@ class IAM(AWSService):
is_service_role=is_service_role(role), 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: except Exception as error:
logger.error( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -219,11 +229,28 @@ class IAM(AWSService):
except ClientError as error: except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity": if error.response["Error"]["Code"] == "NoSuchEntity":
# Password policy does not exist # Password policy is the IAM default
stored_password_policy = None 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( logger.warning(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" 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: else:
logger.error( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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): def __list_attached_role_policies__(self):
logger.info("IAM - List Attached User Policies...") logger.info("IAM - List Attached User Policies...")
try: try:
for role in self.roles: if self.roles:
try: for role in self.roles:
attached_role_policies = [] try:
list_attached_role_policies_paginator = self.client.get_paginator( attached_role_policies = []
"list_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}"
) )
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( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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: except Exception as error:
logger.error( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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): def __list_inline_role_policies__(self):
logger.info("IAM - List Inline Role Policies...") logger.info("IAM - List Inline Role Policies...")
for role in self.roles: if self.roles:
try: for role in self.roles:
inline_role_policies = [] try:
get_role_inline_policies_paginator = self.client.get_paginator( inline_role_policies = []
"list_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 page in get_role_inline_policies_paginator.paginate(
): RoleName=role.name
for policy in page["PolicyNames"]: ):
try: for policy in page["PolicyNames"]:
inline_role_policies.append(policy) try:
# Get inline policies & their policy documents here: inline_role_policies.append(policy)
inline_policy = self.client.get_role_policy( # Get inline policies & their policy documents here:
RoleName=role.name, PolicyName=policy 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,
) )
) inline_role_policy_doc = inline_policy["PolicyDocument"]
except ClientError as error: self.policies.append(
if error.response["Error"]["Code"] == "NoSuchEntity": Policy(
logger.warning( name=policy,
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" 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( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
) )
except Exception as error: role.inline_policies = inline_role_policies
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
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: except Exception as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
logger.warning(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
else:
logger.error( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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): def __list_entities_role_for_policy__(self, policy_arn):
logger.info("IAM - List Entities Role For Policy...") logger.info("IAM - List Entities Role For Policy...")
try: try:
@@ -615,6 +644,17 @@ class IAM(AWSService):
roles = self.client.list_entities_for_policy( roles = self.client.list_entities_for_policy(
PolicyArn=policy_arn, EntityFilter="Role" PolicyArn=policy_arn, EntityFilter="Role"
)["PolicyRoles"] )["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: except Exception as error:
logger.error( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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): def __list_tags_for_resource__(self):
logger.info("IAM - List Tags...") logger.info("IAM - List Tags...")
try: try:
for role in self.roles: if self.roles:
try: for role in self.roles:
response = self.client.list_role_tags(RoleName=role.name)["Tags"] try:
role.tags = response response = self.client.list_role_tags(RoleName=role.name)[
except ClientError as error: "Tags"
if error.response["Error"]["Code"] == "NoSuchEntity": ]
role.tags = [] role.tags = response
else: 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( logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {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: except Exception as error:
logger.error( logger.error(
@@ -5,17 +5,18 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_support_role_created(Check): class iam_support_role_created(Check):
def execute(self) -> Check_Report_AWS: def execute(self) -> Check_Report_AWS:
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if iam_client.entities_role_attached_to_support_policy is not None:
report.region = iam_client.region report = Check_Report_AWS(self.metadata())
report.resource_id = "AWSSupportServiceRolePolicy" report.region = iam_client.region
report.resource_arn = ( report.resource_id = "AWSSupportServiceRolePolicy"
"arn:aws:iam::aws:policy/aws-service-role/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" if iam_client.entities_role_attached_to_support_policy:
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}." report.status = "PASS"
else: report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
report.status = "FAIL" else:
report.status_extended = "Support policy is not attached to any role." report.status = "FAIL"
findings.append(report) report.status_extended = "Support policy is not attached to any role."
findings.append(report)
return findings return findings
@@ -7,18 +7,19 @@ from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_client i
class resourceexplorer2_indexes_found(Check): class resourceexplorer2_indexes_found(Check):
def execute(self): def execute(self):
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if resource_explorer_2_client.indexes is not None:
report.status = "FAIL" report = Check_Report_AWS(self.metadata())
report.status_extended = "No Resource Explorer Indexes found." report.status = "FAIL"
report.region = resource_explorer_2_client.region report.status_extended = "No Resource Explorer Indexes found."
report.resource_arn = "NoResourceExplorer" report.region = resource_explorer_2_client.region
report.resource_id = resource_explorer_2_client.audited_account report.resource_arn = "NoResourceExplorer"
report.resource_arn = resource_explorer_2_client.index_arn_template report.resource_id = resource_explorer_2_client.audited_account
if resource_explorer_2_client.indexes: report.resource_arn = resource_explorer_2_client.index_arn_template
report.region = resource_explorer_2_client.indexes[0].region if resource_explorer_2_client.indexes:
report.resource_arn = resource_explorer_2_client.indexes[0].arn report.region = resource_explorer_2_client.indexes[0].region
report.status = "PASS" report.resource_arn = resource_explorer_2_client.indexes[0].arn
report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}." report.status = "PASS"
findings.append(report) report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}."
findings.append(report)
return findings return findings
@@ -1,3 +1,4 @@
from botocore.client import ClientError
from pydantic import BaseModel from pydantic import BaseModel
from prowler.lib.logger import logger from prowler.lib.logger import logger
@@ -23,6 +24,8 @@ class ResourceExplorer2(AWSService):
if not self.audit_resources or ( if not self.audit_resources or (
is_resource_filtered(index["Arn"], self.audit_resources) is_resource_filtered(index["Arn"], self.audit_resources)
): ):
if self.indexes is None:
self.indexes = []
self.indexes.append( self.indexes.append(
Indexes( Indexes(
arn=index["Arn"], arn=index["Arn"],
@@ -30,6 +33,17 @@ class ResourceExplorer2(AWSService):
type=index["Type"], 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: except Exception as error:
logger.error( logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -7,22 +7,23 @@ from prowler.providers.aws.services.ssmincidents.ssmincidents_client import (
class ssmincidents_enabled_with_plans(Check): class ssmincidents_enabled_with_plans(Check):
def execute(self): def execute(self):
findings = [] findings = []
report = Check_Report_AWS(self.metadata()) if ssmincidents_client.replication_set is not None:
report.status = "FAIL" report = Check_Report_AWS(self.metadata())
report.status_extended = "No SSM Incidents replication set exists." report.status = "FAIL"
report.resource_arn = ssmincidents_client.replication_set_arn_template report.status_extended = "No SSM Incidents replication set exists."
report.resource_id = ssmincidents_client.audited_account report.resource_arn = ssmincidents_client.replication_set_arn_template
report.region = ssmincidents_client.region report.resource_id = ssmincidents_client.audited_account
if ssmincidents_client.replication_set: report.region = ssmincidents_client.region
report.resource_arn = ssmincidents_client.replication_set[0].arn if ssmincidents_client.replication_set:
report.resource_tags = [] # Not supported for replication sets report.resource_arn = ssmincidents_client.replication_set[0].arn
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE." report.resource_tags = [] # Not supported for replication sets
if ssmincidents_client.replication_set[0].status == "ACTIVE": report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not 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.replication_set[0].status == "ACTIVE":
if ssmincidents_client.response_plans: report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist."
report.status = "PASS" if ssmincidents_client.response_plans:
report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has 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 return findings
@@ -45,6 +45,17 @@ class SSMIncidents(AWSService):
arn=replication_set, 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: except Exception as error:
logger.error( logger.error(
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
@@ -7,28 +7,29 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
class trustedadvisor_errors_and_warnings(Check): class trustedadvisor_errors_and_warnings(Check):
def execute(self): def execute(self):
findings = [] findings = []
if trustedadvisor_client.premium_support.enabled: if trustedadvisor_client.premium_support:
if trustedadvisor_client.checks: if trustedadvisor_client.premium_support.enabled:
for check in trustedadvisor_client.checks: if trustedadvisor_client.checks:
if ( for check in trustedadvisor_client.checks:
check.status != "not_available" if (
): # avoid not_available checks since there are no resources that apply check.status != "not_available"
report = Check_Report_AWS(self.metadata()) ): # avoid not_available checks since there are no resources that apply
report.region = check.region report = Check_Report_AWS(self.metadata())
report.resource_id = check.id report.region = check.region
report.resource_arn = check.arn report.resource_id = check.id
report.status = "FAIL" report.resource_arn = check.arn
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}." report.status = "FAIL"
if check.status == "ok": report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
report.status = "PASS" if check.status == "ok":
findings.append(report) report.status = "PASS"
else: findings.append(report)
report = Check_Report_AWS(self.metadata()) else:
report.status = "MANUAL" report = Check_Report_AWS(self.metadata())
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service." report.status = "MANUAL"
report.resource_id = trustedadvisor_client.audited_account report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
report.resource_arn = trustedadvisor_client.account_arn_template report.resource_id = trustedadvisor_client.audited_account
report.region = trustedadvisor_client.region report.resource_arn = trustedadvisor_client.account_arn_template
findings.append(report) report.region = trustedadvisor_client.region
findings.append(report)
return findings return findings
@@ -7,7 +7,12 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
class trustedadvisor_premium_support_plan_subscribed(Check): class trustedadvisor_premium_support_plan_subscribed(Check):
def execute(self): def execute(self):
findings = [] 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 = Check_Report_AWS(self.metadata())
report.status = "FAIL" report.status = "FAIL"
report.status_extended = ( report.status_extended = (
@@ -22,7 +22,6 @@ class TrustedAdvisor(AWSService):
support_region = "us-east-1" support_region = "us-east-1"
else: else:
support_region = "us-gov-west-1" support_region = "us-gov-west-1"
self.client = self.session.client(self.service, region_name=support_region) self.client = self.session.client(self.service, region_name=support_region)
self.client.region = support_region self.client.region = support_region
self.__describe_services__() self.__describe_services__()
@@ -106,6 +105,19 @@ class TrustedAdvisor(AWSService):
f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
f" {error}" 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: except Exception as error:
logger.error( logger.error(
@@ -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].region == AWS_REGION
assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert result[0].resource_arn == AWS_ACCOUNT_ARN 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_id == "MyBackupVault"
assert result[0].resource_arn == backup_vault_arn assert result[0].resource_arn == backup_vault_arn
assert result[0].region == AWS_REGION 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
@@ -254,3 +254,35 @@ class Test_cloudtrail_bucket_requires_mfa_delete:
assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] 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
@@ -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." == f"Single region trail {trail_name_eu} is not logging in the last 24h or not configured to deliver logs."
) )
assert report.resource_tags == [] 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
@@ -128,3 +128,26 @@ class Test_cloudtrail_insights_exist:
assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] 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
@@ -137,3 +137,32 @@ class Test_cloudtrail_kms_encryption_enabled:
assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -155,3 +155,31 @@ class Test_cloudtrail_log_file_validation_enabled:
assert report.resource_arn == trail_eu["TrailARN"] assert report.resource_arn == trail_eu["TrailARN"]
assert report.resource_tags == [] assert report.resource_tags == []
assert report.region == AWS_REGION_EU_WEST_1 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
@@ -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_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -289,3 +289,40 @@ class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible:
) )
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -289,3 +289,31 @@ class Test_cloudtrail_multi_region_enabled:
assert report.resource_id == trail_name_us assert report.resource_id == trail_name_us
assert report.resource_arn == trail_us["TrailARN"] assert report.resource_arn == trail_us["TrailARN"]
assert report.resource_tags == [] 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
@@ -273,3 +273,29 @@ class Test_cloudtrail_multi_region_enabled_logging_management_events:
result[0].status_extended result[0].status_extended
== "No trail found with multi-region enabled and logging management events." == "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
@@ -391,3 +391,33 @@ class Test_cloudtrail_s3_dataevents_read_enabled:
assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -389,3 +389,33 @@ class Test_cloudtrail_s3_dataevents_write_enabled:
assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_arn == trail_us["TrailARN"]
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -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" == 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 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
@@ -99,3 +99,39 @@ class Test_cloudwatch_cross_account_sharing_disabled:
== "CloudWatch has allowed cross-account sharing." == "CloudWatch has allowed cross-account sharing."
) )
assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole" 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
@@ -138,3 +138,39 @@ class Test_cloudwatch_log_group_kms_encryption_enabled:
== "Log Group test does have AWS KMS key test_kms_id associated." == "Log Group test does have AWS KMS key test_kms_id associated."
) )
assert result[0].resource_id == "test" 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
@@ -154,3 +154,39 @@ class Test_cloudwatch_log_group_no_secrets_in_logs:
"Potential secrets found in log group", result[0].status_extended "Potential secrets found in log group", result[0].status_extended
) )
assert result[0].resource_id == "test" 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
@@ -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" == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:test"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -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" == 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 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 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): def test_fms_admin_with_non_compliant_policies(self):
fms_client = mock.MagicMock fms_client = mock.MagicMock
fms_client.audited_account = AWS_ACCOUNT_NUMBER fms_client.audited_account = AWS_ACCOUNT_NUMBER
@@ -145,3 +145,25 @@ class Test_iam_password_policy_expires_passwords_within_90_days_or_less:
"Password expiration is set lower than 90 days", "Password expiration is set lower than 90 days",
result[0].status_extended, 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
@@ -85,3 +85,25 @@ class Test_iam_password_policy_lowercase:
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -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" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -92,3 +92,25 @@ class Test_iam_password_policy_number:
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -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" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -92,3 +92,25 @@ class Test_iam_password_policy_symbol:
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -89,3 +89,25 @@ class Test_iam_password_policy_uppercase:
== f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy"
) )
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -264,3 +264,26 @@ class Test_iam_role_administratoraccess_policy:
check = iam_role_administratoraccess_policy() check = iam_role_administratoraccess_policy()
result = check.execute() result = check.execute()
assert len(result) == 0 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
@@ -85,3 +85,25 @@ class Test_iam_securityaudit_role_created:
assert result[0].resource_id == "SecurityAudit" assert result[0].resource_id == "SecurityAudit"
assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit" assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit"
assert result[0].region == "us-east-1" 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
@@ -95,3 +95,25 @@ class Test_iam_support_role_created:
result[0].resource_arn result[0].resource_arn
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" == "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
@@ -80,3 +80,31 @@ class Test_resourceexplorer2_indexes_found:
assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert result[0].resource_arn == INDEX_ARN assert result[0].resource_arn == INDEX_ARN
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -164,3 +164,30 @@ class Test_ssmincidents_enabled_with_plans:
assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert result[0].resource_arn == REPLICATION_SET_ARN assert result[0].resource_arn == REPLICATION_SET_ARN
assert result[0].region == AWS_REGION_US_EAST_1 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
@@ -144,3 +144,30 @@ class Test_trustedadvisor_errors_and_warnings:
check = trustedadvisor_errors_and_warnings() check = trustedadvisor_errors_and_warnings()
result = check.execute() result = check.execute()
assert len(result) == 0 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
@@ -86,3 +86,30 @@ class Test_trustedadvisor_premium_support_plan_subscribed:
result[0].resource_arn result[0].resource_arn
== f"arn:aws:trusted-advisor:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account" == 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