From 35c8ea5e3f5a21a7a290ad414838e0cb762fad87 Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Mon, 29 Apr 2024 17:42:44 +0200 Subject: [PATCH] fix(aws): not show findings when AccessDenieds (#3803) --- docs/developer-guide/services.md | 2 + prowler/__main__.py | 6 +- prowler/lib/outputs/summary_table.py | 13 +- prowler/providers/aws/aws_provider.py | 31 ++- ...ails_to_security_billing_and_operations.py | 40 ++-- .../aws/services/account/account_service.py | 55 +++-- .../aws/services/backup/backup_service.py | 11 +- .../backup_vaults_encrypted.py | 34 +-- .../backup_vaults_exist.py | 27 +-- .../cloudtrail_bucket_requires_mfa_delete.py | 45 ++-- .../cloudtrail_cloudwatch_logging_enabled.py | 59 ++--- .../cloudtrail_insights_exist.py | 29 ++- .../cloudtrail_kms_encryption_enabled.py | 39 ++-- .../cloudtrail_log_file_validation_enabled.py | 37 ++- ...l_logs_s3_bucket_access_logging_enabled.py | 59 ++--- ...gs_s3_bucket_is_not_publicly_accessible.py | 73 +++--- .../cloudtrail_multi_region_enabled.py | 59 +++-- ...egion_enabled_logging_management_events.py | 85 +++---- .../cloudtrail_s3_dataevents_read_enabled.py | 89 ++++---- .../cloudtrail_s3_dataevents_write_enabled.py | 89 ++++---- .../services/cloudtrail/cloudtrail_service.py | 25 +- ...hanges_to_network_acls_alarm_configured.py | 35 +-- ...es_to_network_gateways_alarm_configured.py | 35 +-- ...o_network_route_tables_alarm_configured.py | 35 +-- ...dwatch_changes_to_vpcs_alarm_configured.py | 35 +-- ...oudwatch_cross_account_sharing_disabled.py | 29 +-- ...dwatch_log_group_kms_encryption_enabled.py | 29 ++- ...cloudwatch_log_group_no_secrets_in_logs.py | 146 ++++++------ ..._retention_policy_specific_days_enabled.py | 37 +-- ...ws_config_configuration_changes_enabled.py | 35 +-- ...loudtrail_configuration_changes_enabled.py | 35 +-- ...g_metric_filter_authentication_failures.py | 35 +-- ...metric_filter_aws_organizations_changes.py | 35 +-- ...isable_or_scheduled_deletion_of_kms_cmk.py | 35 +-- ...ric_filter_for_s3_bucket_policy_changes.py | 35 +-- ...dwatch_log_metric_filter_policy_changes.py | 35 +-- ...cloudwatch_log_metric_filter_root_usage.py | 35 +-- ...og_metric_filter_security_group_changes.py | 35 +-- ...h_log_metric_filter_sign_in_without_mfa.py | 35 +-- ...og_metric_filter_unauthorized_api_calls.py | 35 +-- .../services/cloudwatch/cloudwatch_service.py | 61 ++++- .../services/cloudwatch/lib/metric_filters.py | 7 +- .../providers/aws/services/fms/fms_service.py | 5 + ...xpires_passwords_within_90_days_or_less.py | 14 +- .../iam_password_policy_lowercase.py | 14 +- .../iam_password_policy_minimum_length_14.py | 14 +- .../iam_password_policy_number.py | 14 +- .../iam_password_policy_reuse_24.py | 14 +- .../iam_password_policy_symbol.py | 14 +- .../iam_password_policy_uppercase.py | 14 +- .../iam_role_administratoraccess_policy.py | 35 ++- ...ole_cross_account_readonlyaccess_policy.py | 122 +++++----- ...ross_service_confused_deputy_prevention.py | 61 ++--- .../iam_securityaudit_role_created.py | 25 +- .../providers/aws/services/iam/iam_service.py | 213 +++++++++++------- .../iam_support_role_created.py | 27 +-- .../resourceexplorer2_indexes_found.py | 27 +-- .../resourceexplorer2_service.py | 14 ++ .../ssmincidents_enabled_with_plans.py | 33 +-- .../ssmincidents/ssmincidents_service.py | 11 + .../trustedadvisor_errors_and_warnings.py | 47 ++-- ...advisor_premium_support_plan_subscribed.py | 7 +- .../trustedadvisor/trustedadvisor_service.py | 14 +- ...to_security_billing_and_operations_test.py | 26 +++ .../backup_vaults_exist_test.py | 25 ++ ...udtrail_bucket_requires_mfa_delete_test.py | 32 +++ ...udtrail_cloudwatch_logging_enabled_test.py | 31 +++ .../cloudtrail_insights_exist_test.py | 23 ++ .../cloudtrail_kms_encryption_enabled_test.py | 29 +++ ...dtrail_log_file_validation_enabled_test.py | 28 +++ ...s_s3_bucket_access_logging_enabled_test.py | 37 +++ ..._bucket_is_not_publicly_accessible_test.py | 37 +++ .../cloudtrail_multi_region_enabled_test.py | 28 +++ ..._enabled_logging_management_events_test.py | 26 +++ ...udtrail_s3_dataevents_read_enabled_test.py | 30 +++ ...dtrail_s3_dataevents_write_enabled_test.py | 30 +++ ...s_to_network_acls_alarm_configured_test.py | 48 ++++ ...tch_cross_account_sharing_disabled_test.py | 36 +++ ...h_log_group_kms_encryption_enabled_test.py | 36 +++ ...watch_log_group_no_secrets_in_logs_test.py | 36 +++ ...ntion_policy_specific_days_enabled_test.py | 44 ++++ ...nfig_configuration_changes_enabled_test.py | 50 ++++ .../fms_policy_compliant_test.py | 18 ++ ...s_passwords_within_90_days_or_less_test.py | 22 ++ .../iam_password_policy_lowercase_test.py | 22 ++ ..._password_policy_minimum_length_14_test.py | 22 ++ .../iam_password_policy_number_test.py | 22 ++ .../iam_password_policy_reuse_24_test.py | 22 ++ .../iam_password_policy_symbol_test.py | 22 ++ .../iam_password_policy_uppercase_test.py | 22 ++ ...am_role_administratoraccess_policy_test.py | 23 ++ .../iam_securityaudit_role_created_test.py | 22 ++ .../iam_support_role_created_test.py | 22 ++ .../resourceexplorer2_indexes_found_test.py | 28 +++ .../ssmincidents_enabled_with_plans_test.py | 27 +++ ...trustedadvisor_errors_and_warnings_test.py | 27 +++ ...or_premium_support_plan_subscribed_test.py | 27 +++ 97 files changed, 2333 insertions(+), 1127 deletions(-) diff --git a/docs/developer-guide/services.md b/docs/developer-guide/services.md index cf4c4bc19a..f7175fc0b2 100644 --- a/docs/developer-guide/services.md +++ b/docs/developer-guide/services.md @@ -178,6 +178,8 @@ class (ServiceParentClass): f"{.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 diff --git a/prowler/__main__.py b/prowler/__main__.py index c641cddee9..b0ed547d0a 100644 --- a/prowler/__main__.py +++ b/prowler/__main__.py @@ -345,8 +345,10 @@ def prowler(): global_provider, global_provider.output_options, ) - # Only display compliance table if there are findings and it is a default execution - if findings and default_execution: + # Only display compliance table if there are findings (not all MANUAL) and it is a default execution + if ( + findings and not all(finding.status == "MANUAL" for finding in findings) + ) and default_execution: compliance_overview = False if not compliance_framework: compliance_framework = get_available_compliance_frameworks(provider) diff --git a/prowler/lib/outputs/summary_table.py b/prowler/lib/outputs/summary_table.py index b73741c963..9a4428bd85 100644 --- a/prowler/lib/outputs/summary_table.py +++ b/prowler/lib/outputs/summary_table.py @@ -40,11 +40,13 @@ def display_summary_table( entity_type = "Context" audited_entities = provider.identity.context - if findings: + # Check if there are findings and that they are not all MANUAL + if findings and not all(finding.status == "MANUAL" for finding in findings): current = { "Service": "", "Provider": "", "Total": 0, + "Pass": 0, "Critical": 0, "High": 0, "Medium": 0, @@ -70,9 +72,9 @@ def display_summary_table( ): add_service_to_table(findings_table, current) - current["Total"] = current["Muted"] = current["Critical"] = current[ - "High" - ] = current["Medium"] = current["Low"] = 0 + current["Total"] = current["Pass"] = current["Muted"] = current[ + "Critical" + ] = current["High"] = current["Medium"] = current["Low"] = 0 current["Service"] = finding.check_metadata.ServiceName current["Provider"] = finding.check_metadata.Provider @@ -83,6 +85,7 @@ def display_summary_table( current["Muted"] += 1 if finding.status == "PASS": pass_count += 1 + current["Pass"] += 1 elif finding.status == "FAIL": fail_count += 1 if finding.check_metadata.Severity == "critical": @@ -155,7 +158,7 @@ def add_service_to_table(findings_table, current): ) current["Status"] = f"{Fore.RED}FAIL ({total_fails}){Style.RESET_ALL}" else: - current["Status"] = f"{Fore.GREEN}PASS ({current['Total']}){Style.RESET_ALL}" + current["Status"] = f"{Fore.GREEN}PASS ({current['Pass']}){Style.RESET_ALL}" findings_table["Provider"].append(current["Provider"]) findings_table["Service"].append(current["Service"]) diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index d5392051c1..54c62fa8b4 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -789,18 +789,25 @@ def assume_role( def get_aws_enabled_regions(self, current_session: Session) -> set: """get_aws_enabled_regions returns a set of enabled AWS regions""" - - # EC2 Client to check enabled regions - service = "ec2" - default_region = self.get_default_region(service) - ec2_client = current_session.client(service, region_name=default_region) - - enabled_regions = set() - # With AllRegions=False we only get the enabled regions for the account - for region in ec2_client.describe_regions(AllRegions=False).get("Regions", []): - enabled_regions.add(region.get("RegionName")) - - return enabled_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) + + enabled_regions = set() + # With AllRegions=False we only get the enabled regions for the account + for region in ec2_client.describe_regions(AllRegions=False).get( + "Regions", [] + ): + enabled_regions.add(region.get("RegionName")) + + return enabled_regions + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return set() # TODO: review this function # Maybe this should be done within the AwsProvider and not in __main__.py diff --git a/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.py b/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.py index 6616755413..1a3f799dfb 100644 --- a/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.py +++ b/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.py @@ -6,22 +6,26 @@ class account_maintain_different_contact_details_to_security_billing_and_operati Check ): def execute(self): - report = Check_Report_AWS(self.metadata()) - report.region = account_client.region - report.resource_id = account_client.audited_account - report.resource_arn = account_client.audited_account_arn + findings = [] + if account_client.contact_base: + report = Check_Report_AWS(self.metadata()) + report.region = account_client.region + report.resource_id = account_client.audited_account + report.resource_arn = account_client.audited_account_arn - if ( - len(account_client.contact_phone_numbers) - == account_client.number_of_contacts - and len(account_client.contact_names) == account_client.number_of_contacts - # This is because the primary contact has no email field - and len(account_client.contact_emails) - == account_client.number_of_contacts - 1 - ): - report.status = "PASS" - report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact." - else: - report.status = "FAIL" - report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact." - return [report] + if ( + len(account_client.contact_phone_numbers) + == account_client.number_of_contacts + and len(account_client.contact_names) + == account_client.number_of_contacts + # This is because the primary contact has no email field + and len(account_client.contact_emails) + == account_client.number_of_contacts - 1 + ): + report.status = "PASS" + report.status_extended = "SECURITY, BILLING and OPERATIONS contacts found and they are different between each other and between ROOT contact." + else: + report.status = "FAIL" + report.status_extended = "SECURITY, BILLING and OPERATIONS contacts not found or they are not different between each other and between ROOT contact." + findings.append(report) + return findings diff --git a/prowler/providers/aws/services/account/account_service.py b/prowler/providers/aws/services/account/account_service.py index c623bef658..a7a7bacb86 100644 --- a/prowler/providers/aws/services/account/account_service.py +++ b/prowler/providers/aws/services/account/account_service.py @@ -18,28 +18,29 @@ def __init__(self, provider): self.contacts_security = self.__get_alternate_contact__("SECURITY") self.contacts_operations = self.__get_alternate_contact__("OPERATIONS") - # Set of contact phone numbers - self.contact_phone_numbers = { - self.contact_base.phone_number, - self.contacts_billing.phone_number, - self.contacts_security.phone_number, - self.contacts_operations.phone_number, - } + if self.contact_base: + # Set of contact phone numbers + self.contact_phone_numbers = { + self.contact_base.phone_number, + self.contacts_billing.phone_number, + self.contacts_security.phone_number, + self.contacts_operations.phone_number, + } - # Set of contact names - self.contact_names = { - self.contact_base.name, - self.contacts_billing.name, - self.contacts_security.name, - self.contacts_operations.name, - } + # Set of contact names + self.contact_names = { + self.contact_base.name, + self.contacts_billing.name, + self.contacts_security.name, + self.contacts_operations.name, + } - # Set of contact emails - self.contact_emails = { - self.contacts_billing.email, - self.contacts_security.email, - self.contacts_operations.email, - } + # Set of contact emails + self.contact_emails = { + self.contacts_billing.email, + self.contacts_security.email, + self.contacts_operations.email, + } def __get_contact_information__(self): try: @@ -53,10 +54,16 @@ def __get_contact_information__(self): phone_number=primary_account_contact.get("PhoneNumber"), ) except Exception as error: - logger.error( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - return Contact(type="PRIMARY") + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return None + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return Contact(type="PRIMARY") def __get_alternate_contact__(self, contact_type: str): try: diff --git a/prowler/providers/aws/services/backup/backup_service.py b/prowler/providers/aws/services/backup/backup_service.py index c2ceb782b1..7462952a3f 100644 --- a/prowler/providers/aws/services/backup/backup_service.py +++ b/prowler/providers/aws/services/backup/backup_service.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import Optional +from botocore.client import ClientError from pydantic import BaseModel from prowler.lib.logger import logger @@ -37,6 +38,8 @@ def __list_backup_vaults__(self, regional_client): self.audit_resources, ) ): + if self.backup_vaults is None: + self.backup_vaults = [] self.backup_vaults.append( BackupVault( arn=configuration.get("BackupVaultArn"), @@ -55,7 +58,13 @@ def __list_backup_vaults__(self, regional_client): ), ) ) - + except ClientError as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if error.response["Error"]["Code"] == "AccessDeniedException": + if not self.backup_vaults: + self.backup_vaults = None except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" diff --git a/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.py b/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.py index 7ccc7162fb..c9f909170d 100644 --- a/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.py +++ b/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.py @@ -5,24 +5,24 @@ class backup_vaults_encrypted(Check): def execute(self): findings = [] - - for backup_vault in backup_client.backup_vaults: - # By default we assume that the result is fail - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - f"Backup Vault {backup_vault.name} is not encrypted." - ) - report.resource_arn = backup_vault.arn - report.resource_id = backup_vault.name - report.region = backup_vault.region - # if it is encrypted we only change the status and the status extended - if backup_vault.encryption: - report.status = "PASS" + if backup_client.backup_vaults: + for backup_vault in backup_client.backup_vaults: + # By default we assume that the result is fail + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" report.status_extended = ( - f"Backup Vault {backup_vault.name} is encrypted." + f"Backup Vault {backup_vault.name} is not encrypted." ) - # then we store the finding - findings.append(report) + report.resource_arn = backup_vault.arn + report.resource_id = backup_vault.name + report.region = backup_vault.region + # if it is encrypted we only change the status and the status extended + if backup_vault.encryption: + report.status = "PASS" + report.status_extended = ( + f"Backup Vault {backup_vault.name} is encrypted." + ) + # then we store the finding + findings.append(report) return findings diff --git a/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.py b/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.py index 0c50e63525..c05e9c5ab1 100644 --- a/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.py +++ b/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.py @@ -5,18 +5,19 @@ class backup_vaults_exist(Check): def execute(self): findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = "No Backup Vault exist." - report.resource_arn = backup_client.backup_vault_arn_template - report.resource_id = backup_client.audited_account - report.region = backup_client.region - if backup_client.backup_vaults: - report.status = "PASS" - report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}." - report.resource_arn = backup_client.backup_vaults[0].arn - report.resource_id = backup_client.backup_vaults[0].name - report.region = backup_client.backup_vaults[0].region + if backup_client.backup_vaults is not None: + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No Backup Vault exist." + report.resource_arn = backup_client.backup_vault_arn_template + report.resource_id = backup_client.audited_account + report.region = backup_client.region + if backup_client.backup_vaults: + report.status = "PASS" + report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}." + report.resource_arn = backup_client.backup_vaults[0].arn + report.resource_id = backup_client.backup_vaults[0].name + report.region = backup_client.backup_vaults[0].region - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py index 786d9f7a62..b16117054c 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py @@ -8,28 +8,29 @@ class cloudtrail_bucket_requires_mfa_delete(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.is_logging: - trail_bucket_is_in_account = False - trail_bucket = trail.s3_bucket - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "FAIL" - report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled." - for bucket in s3_client.buckets: - if trail_bucket == bucket.name: - trail_bucket_is_in_account = True - if bucket.mfa_delete: - report.status = "PASS" - report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled." - # check if trail bucket is a cross account bucket - if not trail_bucket_is_in_account: - report.status = "MANUAL" - report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually." + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.is_logging: + trail_bucket_is_in_account = False + trail_bucket = trail.s3_bucket + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "FAIL" + report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) does not have MFA delete enabled." + for bucket in s3_client.buckets: + if trail_bucket == bucket.name: + trail_bucket_is_in_account = True + if bucket.mfa_delete: + report.status = "PASS" + report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) has MFA delete enabled." + # check if trail bucket is a cross account bucket + if not trail_bucket_is_in_account: + report.status = "MANUAL" + report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py index 94c7381c9e..6d502d0884 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py @@ -11,37 +11,38 @@ class cloudtrail_cloudwatch_logging_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.name: - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - if trail.is_multiregion: - report.status_extended = ( - f"Multiregion trail {trail.name} has been logging the last 24h." - ) - else: - report.status_extended = f"Single region trail {trail.name} has been logging the last 24h." - if trail.latest_cloudwatch_delivery_time: - last_log_delivery = ( - datetime.now().replace(tzinfo=timezone.utc) - - trail.latest_cloudwatch_delivery_time - ) - if last_log_delivery > timedelta(days=maximum_time_without_logging): + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.name: + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + if trail.is_multiregion: + report.status_extended = f"Multiregion trail {trail.name} has been logging the last 24h." + else: + report.status_extended = f"Single region trail {trail.name} has been logging the last 24h." + if trail.latest_cloudwatch_delivery_time: + last_log_delivery = ( + datetime.now().replace(tzinfo=timezone.utc) + - trail.latest_cloudwatch_delivery_time + ) + if last_log_delivery > timedelta( + days=maximum_time_without_logging + ): + report.status = "FAIL" + if trail.is_multiregion: + report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h." + else: + report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h." + else: report.status = "FAIL" if trail.is_multiregion: - report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h." + report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs." else: - report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h." - else: - report.status = "FAIL" - if trail.is_multiregion: - report.status_extended = f"Multiregion trail {trail.name} is not logging in the last 24h or not configured to deliver logs." - else: - report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs." - findings.append(report) + report.status_extended = f"Single region trail {trail.name} is not logging in the last 24h or not configured to deliver logs." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py index a175c4fa1f..c24aaa2c26 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py @@ -7,19 +7,18 @@ class cloudtrail_insights_exist(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.is_logging: - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "FAIL" - report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging." - if trail.has_insight_selectors: - report.status = "PASS" - report.status_extended = ( - f"Trail {trail.name} has insight selectors and it is logging." - ) - findings.append(report) + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.is_logging: + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "FAIL" + report.status_extended = f"Trail {trail.name} does not have insight selectors and it is logging." + if trail.has_insight_selectors: + report.status = "PASS" + report.status_extended = f"Trail {trail.name} has insight selectors and it is logging." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py index 68b4b0c7d5..85fb994541 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py @@ -7,32 +7,29 @@ class cloudtrail_kms_encryption_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.name: - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "FAIL" - if trail.is_multiregion: - report.status_extended = ( - f"Multiregion trail {trail.name} has encryption disabled." - ) - else: - report.status_extended = ( - f"Single region trail {trail.name} has encryption disabled." - ) - if trail.kms_key: - report.status = "PASS" + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.name: + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "FAIL" if trail.is_multiregion: report.status_extended = ( - f"Multiregion trail {trail.name} has encryption enabled." + f"Multiregion trail {trail.name} has encryption disabled." ) else: report.status_extended = ( - f"Single region trail {trail.name} has encryption enabled." + f"Single region trail {trail.name} has encryption disabled." ) - findings.append(report) + if trail.kms_key: + report.status = "PASS" + if trail.is_multiregion: + report.status_extended = f"Multiregion trail {trail.name} has encryption enabled." + else: + report.status_extended = f"Single region trail {trail.name} has encryption enabled." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py index f7aa0fbf75..a2d6c6fee3 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py @@ -7,26 +7,25 @@ class cloudtrail_log_file_validation_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.name: - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "FAIL" - if trail.is_multiregion: - report.status_extended = ( - f"Multiregion trail {trail.name} log file validation disabled." - ) - else: - report.status_extended = f"Single region trail {trail.name} log file validation disabled." - if trail.log_file_validation_enabled: - report.status = "PASS" + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.name: + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "FAIL" if trail.is_multiregion: - report.status_extended = f"Multiregion trail {trail.name} log file validation enabled." + report.status_extended = f"Multiregion trail {trail.name} log file validation disabled." else: - report.status_extended = f"Single region trail {trail.name} log file validation enabled." - findings.append(report) + report.status_extended = f"Single region trail {trail.name} log file validation disabled." + if trail.log_file_validation_enabled: + report.status = "PASS" + if trail.is_multiregion: + report.status_extended = f"Multiregion trail {trail.name} log file validation enabled." + else: + report.status_extended = f"Single region trail {trail.name} log file validation enabled." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py index 95f0557850..eb9a9c271c 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py @@ -8,35 +8,36 @@ class cloudtrail_logs_s3_bucket_access_logging_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.name: - trail_bucket_is_in_account = False - trail_bucket = trail.s3_bucket - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "FAIL" - if trail.is_multiregion: - report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." - else: - report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." - for bucket in s3_client.buckets: - if trail_bucket == bucket.name: - trail_bucket_is_in_account = True - if bucket.logging: - report.status = "PASS" - if trail.is_multiregion: - report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." - else: - report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." - break + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.name: + trail_bucket_is_in_account = False + trail_bucket = trail.s3_bucket + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "FAIL" + if trail.is_multiregion: + report.status_extended = f"Multiregion Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." + else: + report.status_extended = f"Single region Trail {trail.name} S3 bucket access logging is not enabled for bucket {trail_bucket}." + for bucket in s3_client.buckets: + if trail_bucket == bucket.name: + trail_bucket_is_in_account = True + if bucket.logging: + report.status = "PASS" + if trail.is_multiregion: + report.status_extended = f"Multiregion trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." + else: + report.status_extended = f"Single region trail {trail.name} S3 bucket access logging is enabled for bucket {trail_bucket}." + break - # check if trail is delivering logs in a cross account bucket - if not trail_bucket_is_in_account: - report.status = "MANUAL" - report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually." - findings.append(report) + # check if trail is delivering logs in a cross account bucket + if not trail_bucket_is_in_account: + report.status = "MANUAL" + report.status_extended = f"Trail {trail.name} is delivering logs in a cross-account bucket {trail_bucket} in another account out of Prowler's permissions scope, please check it manually." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py index 258977522b..3d83a57f86 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py @@ -8,41 +8,42 @@ class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - if trail.name: - trail_bucket_is_in_account = False - trail_bucket = trail.s3_bucket - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - if trail.is_multiregion: - report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible." - else: - report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible." - for bucket in s3_client.buckets: - # Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region - # (for example due to a SCP) we are going to try access an attribute from a None type - if trail_bucket == bucket.name: - trail_bucket_is_in_account = True - if bucket.acl_grantees: - for grant in bucket.acl_grantees: - if ( - grant.URI - == "http://acs.amazonaws.com/groups/global/AllUsers" - ): - report.status = "FAIL" - if trail.is_multiregion: - report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible." - else: - report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible." - break - # check if trail bucket is a cross account bucket - if not trail_bucket_is_in_account: - report.status = "MANUAL" - report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually." - findings.append(report) + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + if trail.name: + trail_bucket_is_in_account = False + trail_bucket = trail.s3_bucket + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + if trail.is_multiregion: + report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is not publicly accessible." + else: + report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is not publicly accessible." + for bucket in s3_client.buckets: + # Here we need to ensure that acl_grantee is filled since if we don't have permissions to query the api for a concrete region + # (for example due to a SCP) we are going to try access an attribute from a None type + if trail_bucket == bucket.name: + trail_bucket_is_in_account = True + if bucket.acl_grantees: + for grant in bucket.acl_grantees: + if ( + grant.URI + == "http://acs.amazonaws.com/groups/global/AllUsers" + ): + report.status = "FAIL" + if trail.is_multiregion: + report.status_extended = f"S3 Bucket {trail_bucket} from multiregion trail {trail.name} is publicly accessible." + else: + report.status_extended = f"S3 Bucket {trail_bucket} from single region trail {trail.name} is publicly accessible." + break + # check if trail bucket is a cross account bucket + if not trail_bucket_is_in_account: + report.status = "MANUAL" + report.status_extended = f"Trail {trail.name} bucket ({trail_bucket}) is a cross-account bucket in another account out of Prowler's permissions scope, please check it manually." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py index 17646fef78..b04a5a93fc 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py @@ -7,36 +7,35 @@ class cloudtrail_multi_region_enabled(Check): def execute(self): findings = [] - for region in cloudtrail_client.regional_clients.keys(): - report = Check_Report_AWS(self.metadata()) - report.region = region - for trail in cloudtrail_client.trails.values(): - if trail.region == region or trail.is_multiregion: - if trail.is_logging: - report.status = "PASS" - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - if trail.is_multiregion: + if cloudtrail_client.trails is not None: + for region in cloudtrail_client.regional_clients.keys(): + report = Check_Report_AWS(self.metadata()) + report.region = region + for trail in cloudtrail_client.trails.values(): + if trail.region == region or trail.is_multiregion: + if trail.is_logging: + report.status = "PASS" + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + if trail.is_multiregion: + report.status_extended = f"Trail {trail.name} is multiregion and it is logging." + else: + report.status_extended = f"Trail {trail.name} is not multiregion and it is logging." + # Since there exists a logging trail in that region there is no point in checking the remaining trails + # Store the finding and exit the loop + findings.append(report) + break + else: + report.status = "FAIL" report.status_extended = ( - f"Trail {trail.name} is multiregion and it is logging." + "No CloudTrail trails enabled and logging were found." ) - else: - report.status_extended = f"Trail {trail.name} is not multiregion and it is logging." - # Since there exists a logging trail in that region there is no point in checking the remaining trails - # Store the finding and exit the loop - findings.append(report) - break - else: - report.status = "FAIL" - report.status_extended = ( - "No CloudTrail trails enabled and logging were found." - ) - report.resource_arn = ( - cloudtrail_client.__get_trail_arn_template__(region) - ) - report.resource_id = cloudtrail_client.audited_account - # If there are no trails logging it is needed to store the FAIL once all the trails have been checked - if report.status == "FAIL": - findings.append(report) + report.resource_arn = ( + cloudtrail_client.__get_trail_arn_template__(region) + ) + report.resource_id = cloudtrail_client.audited_account + # If there are no trails logging it is needed to store the FAIL once all the trails have been checked + if report.status == "FAIL": + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py index e97159d4b1..ac6b5a9db1 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py @@ -7,48 +7,49 @@ class cloudtrail_multi_region_enabled_logging_management_events(Check): def execute(self): findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No trail found with multi-region enabled and logging management events." - ) - report.region = cloudtrail_client.region - report.resource_id = cloudtrail_client.audited_account - report.resource_arn = cloudtrail_client.trail_arn_template + if cloudtrail_client.trails is not None: + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No trail found with multi-region enabled and logging management events." + report.region = cloudtrail_client.region + report.resource_id = cloudtrail_client.audited_account + report.resource_arn = cloudtrail_client.trail_arn_template - for trail in cloudtrail_client.trails.values(): - if trail.is_logging: - if trail.is_multiregion: - for event in trail.data_events: - # Classic event selectors - if not event.is_advanced: - # Check if trail has IncludeManagementEvents and ReadWriteType is All - if ( - event.event_selector["ReadWriteType"] == "All" - and event.event_selector["IncludeManagementEvents"] - ): - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled." + for trail in cloudtrail_client.trails.values(): + if trail.is_logging: + if trail.is_multiregion: + for event in trail.data_events: + # Classic event selectors + if not event.is_advanced: + # Check if trail has IncludeManagementEvents and ReadWriteType is All + if ( + event.event_selector["ReadWriteType"] == "All" + and event.event_selector["IncludeManagementEvents"] + ): + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled." - # Advanced event selectors - elif event.is_advanced: - if event.event_selector.get( - "Name" - ) == "Management events selector" and all( - [ - field["Field"] != "readOnly" - for field in event.event_selector["FieldSelectors"] - ] - ): - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled." - findings.append(report) + # Advanced event selectors + elif event.is_advanced: + if event.event_selector.get( + "Name" + ) == "Management events selector" and all( + [ + field["Field"] != "readOnly" + for field in event.event_selector[ + "FieldSelectors" + ] + ] + ): + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} is multi-region, is logging and have management events enabled." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py index e26e2a8eab..5620be612a 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py @@ -8,23 +8,41 @@ class cloudtrail_s3_dataevents_read_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - for data_event in trail.data_events: - # classic event selectors - if not data_event.is_advanced: - # Check if trail has a data event for all S3 Buckets for read - if ( - data_event.event_selector["ReadWriteType"] == "ReadOnly" - or data_event.event_selector["ReadWriteType"] == "All" - ): - for resource in data_event.event_selector["DataResources"]: - if "AWS::S3::Object" == resource["Type"] and ( - f"arn:{cloudtrail_client.audited_partition}:s3" - in resource["Values"] - or f"arn:{cloudtrail_client.audited_partition}:s3:::" - in resource["Values"] - or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" - in resource["Values"] + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + for data_event in trail.data_events: + # classic event selectors + if not data_event.is_advanced: + # Check if trail has a data event for all S3 Buckets for read + if ( + data_event.event_selector["ReadWriteType"] == "ReadOnly" + or data_event.event_selector["ReadWriteType"] == "All" + ): + for resource in data_event.event_selector["DataResources"]: + if "AWS::S3::Object" == resource["Type"] and ( + f"arn:{cloudtrail_client.audited_partition}:s3" + in resource["Values"] + or f"arn:{cloudtrail_client.audited_partition}:s3:::" + in resource["Values"] + or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" + in resource["Values"] + ): + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations." + findings.append(report) + # advanced event selectors + elif data_event.is_advanced: + for field_selector in data_event.event_selector[ + "FieldSelectors" + ]: + if ( + field_selector["Field"] == "resources.type" + and field_selector["Equals"][0] == "AWS::S3::Object" ): report = Check_Report_AWS(self.metadata()) report.region = trail.region @@ -32,31 +50,16 @@ def execute(self): report.resource_arn = trail.arn report.resource_tags = trail.tags report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations." + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations." findings.append(report) - # advanced event selectors - elif data_event.is_advanced: - for field_selector in data_event.event_selector["FieldSelectors"]: - if ( - field_selector["Field"] == "resources.type" - and field_selector["Equals"][0] == "AWS::S3::Object" - ): - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations." - findings.append(report) - if not findings and ( - s3_client.buckets or cloudtrail_client.provider.scan_unused_services - ): - report = Check_Report_AWS(self.metadata()) - report.region = cloudtrail_client.region - report.resource_arn = cloudtrail_client.trail_arn_template - report.resource_id = cloudtrail_client.audited_account - report.status = "FAIL" - report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations." - findings.append(report) + if not findings and ( + s3_client.buckets or cloudtrail_client.provider.scan_unused_services + ): + report = Check_Report_AWS(self.metadata()) + report.region = cloudtrail_client.region + report.resource_arn = cloudtrail_client.trail_arn_template + report.resource_id = cloudtrail_client.audited_account + report.status = "FAIL" + report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py index 7863eaf02b..481636235f 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py @@ -8,23 +8,41 @@ class cloudtrail_s3_dataevents_write_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails.values(): - for data_event in trail.data_events: - # Classic event selectors - if not data_event.is_advanced: - # Check if trail has a data event for all S3 Buckets for write - if ( - data_event.event_selector["ReadWriteType"] == "All" - or data_event.event_selector["ReadWriteType"] == "WriteOnly" - ): - for resource in data_event.event_selector["DataResources"]: - if "AWS::S3::Object" == resource["Type"] and ( - f"arn:{cloudtrail_client.audited_partition}:s3" - in resource["Values"] - or f"arn:{cloudtrail_client.audited_partition}:s3:::" - in resource["Values"] - or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" - in resource["Values"] + if cloudtrail_client.trails is not None: + for trail in cloudtrail_client.trails.values(): + for data_event in trail.data_events: + # Classic event selectors + if not data_event.is_advanced: + # Check if trail has a data event for all S3 Buckets for write + if ( + data_event.event_selector["ReadWriteType"] == "All" + or data_event.event_selector["ReadWriteType"] == "WriteOnly" + ): + for resource in data_event.event_selector["DataResources"]: + if "AWS::S3::Object" == resource["Type"] and ( + f"arn:{cloudtrail_client.audited_partition}:s3" + in resource["Values"] + or f"arn:{cloudtrail_client.audited_partition}:s3:::" + in resource["Values"] + or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*" + in resource["Values"] + ): + report = Check_Report_AWS(self.metadata()) + report.region = trail.region + report.resource_id = trail.name + report.resource_arn = trail.arn + report.resource_tags = trail.tags + report.status = "PASS" + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations." + findings.append(report) + # Advanced event selectors + elif data_event.is_advanced: + for field_selector in data_event.event_selector[ + "FieldSelectors" + ]: + if ( + field_selector["Field"] == "resources.type" + and field_selector["Equals"][0] == "AWS::S3::Object" ): report = Check_Report_AWS(self.metadata()) report.region = trail.region @@ -32,31 +50,16 @@ def execute(self): report.resource_arn = trail.arn report.resource_tags = trail.tags report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations." + report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations." findings.append(report) - # Advanced event selectors - elif data_event.is_advanced: - for field_selector in data_event.event_selector["FieldSelectors"]: - if ( - field_selector["Field"] == "resources.type" - and field_selector["Equals"][0] == "AWS::S3::Object" - ): - report = Check_Report_AWS(self.metadata()) - report.region = trail.region - report.resource_id = trail.name - report.resource_arn = trail.arn - report.resource_tags = trail.tags - report.status = "PASS" - report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations." - findings.append(report) - if not findings and ( - s3_client.buckets or cloudtrail_client.provider.scan_unused_services - ): - report = Check_Report_AWS(self.metadata()) - report.region = cloudtrail_client.region - report.resource_arn = cloudtrail_client.trail_arn_template - report.resource_id = cloudtrail_client.audited_account - report.status = "FAIL" - report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations." - findings.append(report) + if not findings and ( + s3_client.buckets or cloudtrail_client.provider.scan_unused_services + ): + report = Check_Report_AWS(self.metadata()) + report.region = cloudtrail_client.region + report.resource_arn = cloudtrail_client.trail_arn_template + report.resource_id = cloudtrail_client.audited_account + report.status = "FAIL" + report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py index d912c57865..ee81d82a04 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py @@ -17,10 +17,11 @@ def __init__(self, provider): self.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail" self.trails = {} self.__threading_call__(self.__get_trails__) - self.__get_trail_status__() - self.__get_insight_selectors__() - self.__get_event_selectors__() - self.__list_tags_for_resource__() + if self.trails: + self.__get_trail_status__() + self.__get_insight_selectors__() + self.__get_event_selectors__() + self.__list_tags_for_resource__() def __get_trail_arn_template__(self, region): return ( @@ -45,6 +46,8 @@ def __get_trails__(self, regional_client): kms_key_id = trail["KmsKeyId"] if "CloudWatchLogsLogGroupArn" in trail: log_group_arn = trail["CloudWatchLogsLogGroupArn"] + if self.trails is None: + self.trails = {} self.trails[trail["TrailARN"]] = Trail( name=trail["Name"], is_multiregion=trail["IsMultiRegionTrail"], @@ -61,12 +64,24 @@ def __get_trails__(self, regional_client): has_insight_selectors=trail.get("HasInsightSelectors"), ) if trails_count == 0: + if self.trails is None: + self.trails = {} self.trails[self.__get_trail_arn_template__(regional_client.region)] = ( Trail( region=regional_client.region, ) ) - + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.trails: + self.trails = None + else: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.py index 21ad6c6f7c..63905b4c2d 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.py @@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?CreateNetworkAcl.+\$\.eventName\s*=\s*.?CreateNetworkAclEntry.+\$\.eventName\s*=\s*.?DeleteNetworkAcl.+\$\.eventName\s*=\s*.?DeleteNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclAssociation.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured.py index 1c8d40994f..11de46ec26 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured.py @@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?CreateCustomerGateway.+\$\.eventName\s*=\s*.?DeleteCustomerGateway.+\$\.eventName\s*=\s*.?AttachInternetGateway.+\$\.eventName\s*=\s*.?CreateInternetGateway.+\$\.eventName\s*=\s*.?DeleteInternetGateway.+\$\.eventName\s*=\s*.?DetachInternetGateway.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.py index f602d2a7c0..3f1f2d04fb 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.py @@ -15,21 +15,24 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check): def execute(self): pattern = r"\$\.eventSource\s*=\s*.?ec2.amazonaws.com.+\$\.eventName\s*=\s*.?CreateRoute.+\$\.eventName\s*=\s*.?CreateRouteTable.+\$\.eventName\s*=\s*.?ReplaceRoute.+\$\.eventName\s*=\s*.?ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*.?DeleteRouteTable.+\$\.eventName\s*=\s*.?DeleteRoute.+\$\.eventName\s*=\s*.?DisassociateRouteTable.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured.py index 82afeb1fb5..5a56070828 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured.py @@ -15,21 +15,24 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?CreateVpc.+\$\.eventName\s*=\s*.?DeleteVpc.+\$\.eventName\s*=\s*.?ModifyVpcAttribute.+\$\.eventName\s*=\s*.?AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*.?CreateVpcPeeringConnection.+\$\.eventName\s*=\s*.?DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*.?RejectVpcPeeringConnection.+\$\.eventName\s*=\s*.?AttachClassicLinkVpc.+\$\.eventName\s*=\s*.?DetachClassicLinkVpc.+\$\.eventName\s*=\s*.?DisableVpcClassicLink.+\$\.eventName\s*=\s*.?EnableVpcClassicLink.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled.py index c41954e82b..065a9d569b 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled.py @@ -5,17 +5,20 @@ class cloudwatch_cross_account_sharing_disabled(Check): def execute(self): findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "PASS" - report.status_extended = "CloudWatch doesn't allow cross-account sharing." - report.resource_arn = iam_client.role_arn_template - report.resource_id = iam_client.audited_account - report.region = iam_client.region - for role in iam_client.roles: - if role.name == "CloudWatch-CrossAccountSharingRole": - report.resource_arn = role.arn - report.resource_id = role.name - report.status = "FAIL" - report.status_extended = "CloudWatch has allowed cross-account sharing." - findings.append(report) + if iam_client.roles is not None: + report = Check_Report_AWS(self.metadata()) + report.status = "PASS" + report.status_extended = "CloudWatch doesn't allow cross-account sharing." + report.resource_arn = iam_client.role_arn_template + report.resource_id = iam_client.audited_account + report.region = iam_client.region + for role in iam_client.roles: + if role.name == "CloudWatch-CrossAccountSharingRole": + report.resource_arn = role.arn + report.resource_id = role.name + report.status = "FAIL" + report.status_extended = ( + "CloudWatch has allowed cross-account sharing." + ) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled.py index e30ddfcae1..83b8bf30c0 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled.py @@ -5,19 +5,18 @@ class cloudwatch_log_group_kms_encryption_enabled(Check): def execute(self): findings = [] - for log_group in logs_client.log_groups: - report = Check_Report_AWS(self.metadata()) - report.region = log_group.region - report.resource_id = log_group.name - report.resource_arn = log_group.arn - report.resource_tags = log_group.tags - if log_group.kms_id: - report.status = "PASS" - report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated." - else: - report.status = "FAIL" - report.status_extended = ( - f"Log Group {log_group.name} does not have AWS KMS keys associated." - ) - findings.append(report) + if logs_client.log_groups: + for log_group in logs_client.log_groups: + report = Check_Report_AWS(self.metadata()) + report.region = log_group.region + report.resource_id = log_group.name + report.resource_arn = log_group.arn + report.resource_tags = log_group.tags + if log_group.kms_id: + report.status = "PASS" + report.status_extended = f"Log Group {log_group.name} does have AWS KMS key {log_group.kms_id} associated." + else: + report.status = "FAIL" + report.status_extended = f"Log Group {log_group.name} does not have AWS KMS keys associated." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs.py index c199d01908..8b23d20285 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs.py @@ -11,78 +11,86 @@ class cloudwatch_log_group_no_secrets_in_logs(Check): def execute(self): findings = [] - for log_group in logs_client.log_groups: - report = Check_Report_AWS(self.metadata()) - report.status = "PASS" - report.status_extended = f"No secrets found in {log_group.name} log group." - report.region = log_group.region - report.resource_id = log_group.name - report.resource_arn = log_group.arn - log_group_secrets = [] - if log_group.log_streams: - for log_stream_name in log_group.log_streams: - log_stream_secrets = {} - log_stream_data = "\n".join( - [ - dumps(event["message"]) - for event in log_group.log_streams[log_stream_name] - ] - ) - log_stream_secrets_output = detect_secrets_scan(log_stream_data) - - if log_stream_secrets_output: - for secret in log_stream_secrets_output: - flagged_event = log_group.log_streams[log_stream_name][ - secret["line_number"] - 1 - ] - cloudwatch_timestamp = ( - convert_to_cloudwatch_timestamp_format( - flagged_event["timestamp"] - ) - ) - if cloudwatch_timestamp not in log_stream_secrets.keys(): - log_stream_secrets[cloudwatch_timestamp] = SecretsDict() - - try: - log_event_data = dumps( - loads(flagged_event["message"]), indent=2 - ) - except Exception: - log_event_data = dumps( - flagged_event["message"], indent=2 - ) - if len(log_event_data.split("\n")) > 1: - # Can get more informative output if there is more than 1 line. - # Will rescan just this event to get the type of secret and the line number - event_detect_secrets_output = detect_secrets_scan( - log_event_data - ) - if event_detect_secrets_output: - for secret in event_detect_secrets_output: - log_stream_secrets[ - cloudwatch_timestamp - ].add_secret( - secret["line_number"], secret["type"] - ) - else: - log_stream_secrets[cloudwatch_timestamp].add_secret( - 1, secret["type"] - ) - if log_stream_secrets: - secrets_string = "; ".join( + if logs_client.log_groups: + for log_group in logs_client.log_groups: + report = Check_Report_AWS(self.metadata()) + report.status = "PASS" + report.status_extended = ( + f"No secrets found in {log_group.name} log group." + ) + report.region = log_group.region + report.resource_id = log_group.name + report.resource_arn = log_group.arn + log_group_secrets = [] + if log_group.log_streams: + for log_stream_name in log_group.log_streams: + log_stream_secrets = {} + log_stream_data = "\n".join( [ - f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}" - for timestamp in log_stream_secrets + dumps(event["message"]) + for event in log_group.log_streams[log_stream_name] ] ) - log_group_secrets.append( - f"in log stream {log_stream_name} {secrets_string}" - ) - if log_group_secrets: - secrets_string = "; ".join(log_group_secrets) - report.status = "FAIL" - report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}." - findings.append(report) + log_stream_secrets_output = detect_secrets_scan(log_stream_data) + + if log_stream_secrets_output: + for secret in log_stream_secrets_output: + flagged_event = log_group.log_streams[log_stream_name][ + secret["line_number"] - 1 + ] + cloudwatch_timestamp = ( + convert_to_cloudwatch_timestamp_format( + flagged_event["timestamp"] + ) + ) + if ( + cloudwatch_timestamp + not in log_stream_secrets.keys() + ): + log_stream_secrets[cloudwatch_timestamp] = ( + SecretsDict() + ) + + try: + log_event_data = dumps( + loads(flagged_event["message"]), indent=2 + ) + except Exception: + log_event_data = dumps( + flagged_event["message"], indent=2 + ) + if len(log_event_data.split("\n")) > 1: + # Can get more informative output if there is more than 1 line. + # Will rescan just this event to get the type of secret and the line number + event_detect_secrets_output = detect_secrets_scan( + log_event_data + ) + if event_detect_secrets_output: + for secret in event_detect_secrets_output: + log_stream_secrets[ + cloudwatch_timestamp + ].add_secret( + secret["line_number"], secret["type"] + ) + else: + log_stream_secrets[cloudwatch_timestamp].add_secret( + 1, secret["type"] + ) + if log_stream_secrets: + secrets_string = "; ".join( + [ + f"at {timestamp} - {log_stream_secrets[timestamp].to_string()}" + for timestamp in log_stream_secrets + ] + ) + log_group_secrets.append( + f"in log stream {log_stream_name} {secrets_string}" + ) + if log_group_secrets: + secrets_string = "; ".join(log_group_secrets) + report.status = "FAIL" + report.status_extended = f"Potential secrets found in log group {log_group.name} {secrets_string}." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled.py index 564e1f3117..342770dc8a 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled.py @@ -10,23 +10,24 @@ def execute(self): specific_retention_days = logs_client.audit_config.get( "log_group_retention_days", 365 ) - for log_group in logs_client.log_groups: - report = Check_Report_AWS(self.metadata()) - report.region = log_group.region - report.resource_id = log_group.name - report.resource_arn = log_group.arn - report.resource_tags = log_group.tags - if ( - log_group.never_expire is False - and log_group.retention_days < specific_retention_days - ): - report.status = "FAIL" - report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)." - else: - report.status = "PASS" - if log_group.never_expire is True: - report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires." + if logs_client.log_groups: + for log_group in logs_client.log_groups: + report = Check_Report_AWS(self.metadata()) + report.region = log_group.region + report.resource_id = log_group.name + report.resource_arn = log_group.arn + report.resource_tags = log_group.tags + if ( + log_group.never_expire is False + and log_group.retention_days < specific_retention_days + ): + report.status = "FAIL" + report.status_extended = f"Log Group {log_group.name} has less than {specific_retention_days} days retention period ({log_group.retention_days} days)." else: - report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days." - findings.append(report) + report.status = "PASS" + if log_group.never_expire is True: + report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it never expires." + else: + report.status_extended = f"Log Group {log_group.name} comply with {specific_retention_days} days retention period since it has {log_group.retention_days} days." + findings.append(report) return findings diff --git a/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.py b/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.py index 6cc439fea6..72079b8597 100644 --- a/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.py +++ b/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.py @@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change def execute(self): pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.py index b6c440d454..0addc30196 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled/cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled.py @@ -17,21 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change def execute(self): pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures.py index d70c602b15..fe6f538caf 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_authentication_failures(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes.py index 6a700cc3b2..d9053d3160 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_aws_organizations_changes(Check): def execute(self): pattern = r"\$\.eventSource\s*=\s*.?organizations\.amazonaws\.com.+\$\.eventName\s*=\s*.?AcceptHandshake.+\$\.eventName\s*=\s*.?AttachPolicy.+\$\.eventName\s*=\s*.?CancelHandshake.+\$\.eventName\s*=\s*.?CreateAccount.+\$\.eventName\s*=\s*.?CreateOrganization.+\$\.eventName\s*=\s*.?CreateOrganizationalUnit.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeclineHandshake.+\$\.eventName\s*=\s*.?DeleteOrganization.+\$\.eventName\s*=\s*.?DeleteOrganizationalUnit.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?EnableAllFeatures.+\$\.eventName\s*=\s*.?EnablePolicyType.+\$\.eventName\s*=\s*.?InviteAccountToOrganization.+\$\.eventName\s*=\s*.?LeaveOrganization.+\$\.eventName\s*=\s*.?DetachPolicy.+\$\.eventName\s*=\s*.?DisablePolicyType.+\$\.eventName\s*=\s*.?MoveAccount.+\$\.eventName\s*=\s*.?RemoveAccountFromOrganization.+\$\.eventName\s*=\s*.?UpdateOrganizationalUnit.+\$\.eventName\s*=\s*.?UpdatePolicy.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.py index f2bc343333..63f99e1de1 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk/cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Chec def execute(self): pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.py index b21f2cd099..46489bab68 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.py @@ -15,22 +15,25 @@ class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check): def execute(self): pattern = r"\$\.eventSource\s*=\s*.?s3.amazonaws.com.+\$\.eventName\s*=\s*.?PutBucketAcl.+\$\.eventName\s*=\s*.?PutBucketPolicy.+\$\.eventName\s*=\s*.?PutBucketCors.+\$\.eventName\s*=\s*.?PutBucketLifecycle.+\$\.eventName\s*=\s*.?PutBucketReplication.+\$\.eventName\s*=\s*.?DeleteBucketPolicy.+\$\.eventName\s*=\s*.?DeleteBucketCors.+\$\.eventName\s*=\s*.?DeleteBucketLifecycle.+\$\.eventName\s*=\s*.?DeleteBucketReplication.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.py index b5f121e33d..0b38eb7bfa 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_policy_changes(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?DeleteGroupPolicy.+\$\.eventName\s*=\s*.?DeleteRolePolicy.+\$\.eventName\s*=\s*.?DeleteUserPolicy.+\$\.eventName\s*=\s*.?PutGroupPolicy.+\$\.eventName\s*=\s*.?PutRolePolicy.+\$\.eventName\s*=\s*.?PutUserPolicy.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?CreatePolicyVersion.+\$\.eventName\s*=\s*.?DeletePolicyVersion.+\$\.eventName\s*=\s*.?AttachRolePolicy.+\$\.eventName\s*=\s*.?DetachRolePolicy.+\$\.eventName\s*=\s*.?AttachUserPolicy.+\$\.eventName\s*=\s*.?DetachUserPolicy.+\$\.eventName\s*=\s*.?AttachGroupPolicy.+\$\.eventName\s*=\s*.?DetachGroupPolicy.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_root_usage/cloudwatch_log_metric_filter_root_usage.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_root_usage/cloudwatch_log_metric_filter_root_usage.py index 027c6d02b9..9f99ad4c13 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_root_usage/cloudwatch_log_metric_filter_root_usage.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_root_usage/cloudwatch_log_metric_filter_root_usage.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_root_usage(Check): def execute(self): pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes.py index a9efd8e0a4..6240f20383 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_security_group_changes(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*.?AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*.?CreateSecurityGroup.+\$\.eventName\s*=\s*.?DeleteSecurityGroup.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.py index 42f0450c2d..f6657d125a 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_sign_in_without_mfa(Check): def execute(self): pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_unauthorized_api_calls/cloudwatch_log_metric_filter_unauthorized_api_calls.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_unauthorized_api_calls/cloudwatch_log_metric_filter_unauthorized_api_calls.py index f05133c1bd..2cd8057bfc 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_unauthorized_api_calls/cloudwatch_log_metric_filter_unauthorized_api_calls.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_unauthorized_api_calls/cloudwatch_log_metric_filter_unauthorized_api_calls.py @@ -15,21 +15,24 @@ class cloudwatch_log_metric_filter_unauthorized_api_calls(Check): def execute(self): pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?" findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = ( - "No CloudWatch log groups found with metric filters or alarms associated." - ) - report.region = logs_client.region - report.resource_id = logs_client.audited_account - report.resource_arn = logs_client.log_group_arn_template - report = check_cloudwatch_log_metric_filter( - pattern, - cloudtrail_client.trails, - logs_client.metric_filters, - cloudwatch_client.metric_alarms, - report, - ) + if ( + cloudtrail_client.trails is not None + and logs_client.metric_filters is not None + and cloudwatch_client.metric_alarms is not None + ): + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated." + report.region = logs_client.region + report.resource_id = logs_client.audited_account + report.resource_arn = logs_client.log_group_arn_template + report = check_cloudwatch_log_metric_filter( + pattern, + cloudtrail_client.trails, + logs_client.metric_filters, + cloudwatch_client.metric_alarms, + report, + ) - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py index da6fea0429..2166779e2b 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py @@ -16,7 +16,8 @@ def __init__(self, provider): super().__init__(__class__.__name__, provider) self.metric_alarms = [] self.__threading_call__(self.__describe_alarms__) - self.__list_tags_for_resource__() + if self.metric_alarms: + self.__list_tags_for_resource__() def __describe_alarms__(self, regional_client): logger.info("CloudWatch - Describing alarms...") @@ -33,6 +34,8 @@ def __describe_alarms__(self, regional_client): namespace = None if "Namespace" in alarm: namespace = alarm["Namespace"] + if self.metric_alarms is None: + self.metric_alarms = [] self.metric_alarms.append( MetricAlarm( arn=alarm["AlarmArn"], @@ -42,6 +45,17 @@ def __describe_alarms__(self, regional_client): region=regional_client.region, ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDenied": + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.metric_alarms: + self.metric_alarms = None + else: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -72,15 +86,16 @@ def __init__(self, provider): self.log_groups = [] self.__threading_call__(self.__describe_metric_filters__) self.__threading_call__(self.__describe_log_groups__) - if ( - "cloudwatch_log_group_no_secrets_in_logs" - in provider.audit_metadata.expected_checks - ): - self.events_per_log_group_threshold = ( - 1000 # The threshold for number of events to return per log group. - ) - self.__threading_call__(self.__get_log_events__) - self.__list_tags_for_resource__() + if self.log_groups: + if ( + "cloudwatch_log_group_no_secrets_in_logs" + in provider.audit_metadata.expected_checks + ): + self.events_per_log_group_threshold = ( + 1000 # The threshold for number of events to return per log group. + ) + self.__threading_call__(self.__get_log_events__) + self.__list_tags_for_resource__() def __describe_metric_filters__(self, regional_client): logger.info("CloudWatch Logs - Describing metric filters...") @@ -94,6 +109,8 @@ def __describe_metric_filters__(self, regional_client): if not self.audit_resources or ( is_resource_filtered(arn, self.audit_resources) ): + if self.metric_filters is None: + self.metric_filters = [] self.metric_filters.append( MetricFilter( arn=arn, @@ -104,6 +121,17 @@ def __describe_metric_filters__(self, regional_client): region=regional_client.region, ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.metric_filters: + self.metric_filters = [] + else: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -126,6 +154,8 @@ def __describe_log_groups__(self, regional_client): if not retention_days: never_expire = True retention_days = 9999 + if self.log_groups is None: + self.log_groups = [] self.log_groups.append( LogGroup( arn=log_group["arn"], @@ -136,6 +166,17 @@ def __describe_log_groups__(self, regional_client): region=regional_client.region, ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.log_groups: + self.log_groups = None + else: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" diff --git a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py index 1c67b1d638..a8743b6c0f 100644 --- a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py +++ b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py @@ -12,9 +12,10 @@ def check_cloudwatch_log_metric_filter( ): # 1. Iterate for CloudWatch Log Group in CloudTrail trails log_groups = [] - for trail in trails.values(): - if trail.log_group_arn: - log_groups.append(trail.log_group_arn.split(":")[6]) + if trails is not None: + for trail in trails.values(): + if trail.log_group_arn: + log_groups.append(trail.log_group_arn.split(":")[6]) # 2. Describe metric filters for previous log groups for metric_filter in metric_filters: if metric_filter.log_group in log_groups: diff --git a/prowler/providers/aws/services/fms/fms_service.py b/prowler/providers/aws/services/fms/fms_service.py index 37127644a5..51322e3244 100644 --- a/prowler/providers/aws/services/fms/fms_service.py +++ b/prowler/providers/aws/services/fms/fms_service.py @@ -52,6 +52,11 @@ def __list_policies__(self): ): # FMS is not enabled in this account self.fms_admin_account = False + else: + logger.error( + f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" + ) + self.fms_admin_account = None except Exception as error: logger.error( f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" diff --git a/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.py b/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.py index ebb79f1437..4ae79d62ce 100644 --- a/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.py +++ b/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.py @@ -5,12 +5,11 @@ class iam_password_policy_expires_passwords_within_90_days_or_less(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if password policy expiration exists if iam_client.password_policy.max_age: if iam_client.password_policy.max_age <= 90: @@ -22,9 +21,6 @@ def execute(self) -> Check_Report_AWS: else: report.status = "FAIL" report.status_extended = "Password expiration is not set." - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase.py b/prowler/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase.py index 4d7d5212e1..052db73bf5 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase.py @@ -5,12 +5,11 @@ class iam_password_policy_lowercase(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if lowercase flag is set if iam_client.password_policy.lowercase: report.status = "PASS" @@ -20,8 +19,5 @@ def execute(self) -> Check_Report_AWS: else: report.status = "FAIL" report.status_extended = "IAM password policy does not require at least one lowercase letter." - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14.py b/prowler/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14.py index 74e9443dbb..452dee6e3a 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14.py @@ -5,12 +5,11 @@ class iam_password_policy_minimum_length_14(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check password policy length if ( iam_client.password_policy.length @@ -23,8 +22,5 @@ def execute(self) -> Check_Report_AWS: else: report.status = "FAIL" report.status_extended = "IAM password policy does not require minimum length of 14 characters." - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number.py b/prowler/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number.py index 22f6f6d73a..aaa2956efc 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number.py @@ -5,12 +5,11 @@ class iam_password_policy_number(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if number flag is set if iam_client.password_policy.numbers: report.status = "PASS" @@ -22,8 +21,5 @@ def execute(self) -> Check_Report_AWS: report.status_extended = ( "IAM password policy does not require at least one number." ) - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24.py b/prowler/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24.py index e06ce4e51c..8ee47908bd 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24.py @@ -5,12 +5,11 @@ class iam_password_policy_reuse_24(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if reuse prevention flag is set if ( iam_client.password_policy.reuse_prevention @@ -25,8 +24,5 @@ def execute(self) -> Check_Report_AWS: report.status_extended = ( "IAM password policy reuse prevention is less than 24 or not set." ) - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol.py b/prowler/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol.py index d5d83cc765..2074e8e67a 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol.py @@ -5,12 +5,11 @@ class iam_password_policy_symbol(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if symbol flag is set if iam_client.password_policy.symbols: report.status = "PASS" @@ -22,8 +21,5 @@ def execute(self) -> Check_Report_AWS: report.status_extended = ( "IAM password policy does not require at least one symbol." ) - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase.py b/prowler/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase.py index 176dfabcfc..e7afbddace 100644 --- a/prowler/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase.py +++ b/prowler/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase.py @@ -5,12 +5,11 @@ class iam_password_policy_uppercase(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = iam_client.password_policy_arn_template - report.resource_id = iam_client.audited_account - # Check if password policy exists if iam_client.password_policy: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = iam_client.password_policy_arn_template + report.resource_id = iam_client.audited_account # Check if uppercase flag is set if iam_client.password_policy.uppercase: report.status = "PASS" @@ -20,8 +19,5 @@ def execute(self) -> Check_Report_AWS: else: report.status = "FAIL" report.status_extended = "IAM password policy does not require at least one uppercase letter." - else: - report.status = "FAIL" - report.status_extended = "Password policy cannot be found." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy.py b/prowler/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy.py index 7cb42cd3dc..2889f291fa 100644 --- a/prowler/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy.py +++ b/prowler/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy.py @@ -5,24 +5,23 @@ class iam_role_administratoraccess_policy(Check): def execute(self) -> Check_Report_AWS: findings = [] - for role in iam_client.roles: - if ( - not role.is_service_role - ): # Avoid service roles since they cannot be modified by the user - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = role.arn - report.resource_id = role.name - report.resource_tags = role.tags - report.status = "PASS" - report.status_extended = ( - f"IAM Role {role.name} does not have AdministratorAccess policy." - ) - for policy in role.attached_policies: - if policy["PolicyName"] == "AdministratorAccess": - report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached." - report.status = "FAIL" + if iam_client.roles: + for role in iam_client.roles: + if ( + not role.is_service_role + ): # Avoid service roles since they cannot be modified by the user + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = role.arn + report.resource_id = role.name + report.resource_tags = role.tags + report.status = "PASS" + report.status_extended = f"IAM Role {role.name} does not have AdministratorAccess policy." + for policy in role.attached_policies: + if policy["PolicyName"] == "AdministratorAccess": + report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached." + report.status = "FAIL" - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_role_cross_account_readonlyaccess_policy/iam_role_cross_account_readonlyaccess_policy.py b/prowler/providers/aws/services/iam/iam_role_cross_account_readonlyaccess_policy/iam_role_cross_account_readonlyaccess_policy.py index 605ca3350a..4f71f0a4e5 100644 --- a/prowler/providers/aws/services/iam/iam_role_cross_account_readonlyaccess_policy/iam_role_cross_account_readonlyaccess_policy.py +++ b/prowler/providers/aws/services/iam/iam_role_cross_account_readonlyaccess_policy/iam_role_cross_account_readonlyaccess_policy.py @@ -5,78 +5,82 @@ class iam_role_cross_account_readonlyaccess_policy(Check): def execute(self) -> Check_Report_AWS: findings = [] - for role in iam_client.roles: - if ( - not role.is_service_role - ): # Avoid service roles since they cannot be modified by the user - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = role.arn - report.resource_id = role.name - report.resource_tags = role.tags - report.status = "PASS" - report.status_extended = ( - f"IAM Role {role.name} does not have ReadOnlyAccess policy." - ) - for policy in role.attached_policies: - if policy["PolicyName"] == "ReadOnlyAccess": - report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account." - cross_account_access = False - if isinstance(role.assume_role_policy["Statement"], list): - for statement in role.assume_role_policy["Statement"]: - if not cross_account_access: - if ( - statement["Effect"] == "Allow" - and "AWS" in statement["Principal"] - ): - if isinstance( - statement["Principal"]["AWS"], list + if iam_client.roles: + for role in iam_client.roles: + if ( + not role.is_service_role + ): # Avoid service roles since they cannot be modified by the user + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = role.arn + report.resource_id = role.name + report.resource_tags = role.tags + report.status = "PASS" + report.status_extended = ( + f"IAM Role {role.name} does not have ReadOnlyAccess policy." + ) + for policy in role.attached_policies: + if policy["PolicyName"] == "ReadOnlyAccess": + report.status_extended = f"IAM Role {role.name} has read-only access but is not cross account." + cross_account_access = False + if isinstance(role.assume_role_policy["Statement"], list): + for statement in role.assume_role_policy["Statement"]: + if not cross_account_access: + if ( + statement["Effect"] == "Allow" + and "AWS" in statement["Principal"] ): - for aws_account in statement["Principal"][ - "AWS" - ]: + if isinstance( + statement["Principal"]["AWS"], list + ): + for aws_account in statement[ + "Principal" + ]["AWS"]: + if ( + iam_client.audited_account + not in aws_account + or "*" == aws_account + ): + cross_account_access = True + break + else: if ( iam_client.audited_account - not in aws_account - or "*" == aws_account + not in statement["Principal"]["AWS"] + or "*" + == statement["Principal"]["AWS"] ): cross_account_access = True - break - else: + else: + break + else: + statement = role.assume_role_policy["Statement"] + if ( + statement["Effect"] == "Allow" + and "AWS" in statement["Principal"] + ): + if isinstance(statement["Principal"]["AWS"], list): + for aws_account in statement["Principal"][ + "AWS" + ]: if ( iam_client.audited_account - not in statement["Principal"]["AWS"] - or "*" == statement["Principal"]["AWS"] + not in aws_account + or "*" == aws_account ): cross_account_access = True - else: - break - else: - statement = role.assume_role_policy["Statement"] - if ( - statement["Effect"] == "Allow" - and "AWS" in statement["Principal"] - ): - if isinstance(statement["Principal"]["AWS"], list): - for aws_account in statement["Principal"]["AWS"]: + break + else: if ( iam_client.audited_account - not in aws_account - or "*" == aws_account + not in statement["Principal"]["AWS"] + or "*" == statement["Principal"]["AWS"] ): cross_account_access = True - break - else: - if ( - iam_client.audited_account - not in statement["Principal"]["AWS"] - or "*" == statement["Principal"]["AWS"] - ): - cross_account_access = True - if cross_account_access: - report.status = "FAIL" - report.status_extended = f"IAM Role {role.name} gives cross account read-only access." + if cross_account_access: + report.status = "FAIL" + report.status_extended = f"IAM Role {role.name} gives cross account read-only access." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.py b/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.py index 3fd7739e25..9717101927 100644 --- a/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.py +++ b/prowler/providers/aws/services/iam/iam_role_cross_service_confused_deputy_prevention/iam_role_cross_service_confused_deputy_prevention.py @@ -8,36 +8,37 @@ class iam_role_cross_service_confused_deputy_prevention(Check): def execute(self) -> Check_Report_AWS: findings = [] - for role in iam_client.roles: - # This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed) - if role.is_service_role and "aws-service-role" not in role.arn: - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_arn = role.arn - report.resource_id = role.name - report.resource_tags = role.tags - report.status = "FAIL" - report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack." - for statement in role.assume_role_policy["Statement"]: - if ( - statement["Effect"] == "Allow" - and ( - "sts:AssumeRole" in statement["Action"] - or "sts:*" in statement["Action"] - or "*" in statement["Action"] - ) - # Need to make sure we are checking the part of the assume role policy document that provides a service access - and "Service" in statement["Principal"] - # Check to see if the appropriate condition statements have been implemented - and "Condition" in statement - and is_condition_block_restrictive( - statement["Condition"], iam_client.audited_account - ) - ): - report.status = "PASS" - report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack." - break + if iam_client.roles: + for role in iam_client.roles: + # This check should only be performed against service roles (avoid Service Linked Roles since the trust relationship cannot be changed) + if role.is_service_role and "aws-service-role" not in role.arn: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_arn = role.arn + report.resource_id = role.name + report.resource_tags = role.tags + report.status = "FAIL" + report.status_extended = f"IAM Service Role {role.name} does not prevent against a cross-service confused deputy attack." + for statement in role.assume_role_policy["Statement"]: + if ( + statement["Effect"] == "Allow" + and ( + "sts:AssumeRole" in statement["Action"] + or "sts:*" in statement["Action"] + or "*" in statement["Action"] + ) + # Need to make sure we are checking the part of the assume role policy document that provides a service access + and "Service" in statement["Principal"] + # Check to see if the appropriate condition statements have been implemented + and "Condition" in statement + and is_condition_block_restrictive( + statement["Condition"], iam_client.audited_account + ) + ): + report.status = "PASS" + report.status_extended = f"IAM Service Role {role.name} prevents against a cross-service confused deputy attack." + break - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py index 16394a30c8..8b6a2d55ed 100644 --- a/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py +++ b/prowler/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created.py @@ -5,15 +5,18 @@ class iam_securityaudit_role_created(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_id = "SecurityAudit" - report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit" - if iam_client.entities_role_attached_to_securityaudit_policy: - report.status = "PASS" - report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}." - else: - report.status = "FAIL" - report.status_extended = "SecurityAudit policy is not attached to any role." - findings.append(report) + if iam_client.entities_role_attached_to_securityaudit_policy is not None: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = "SecurityAudit" + report.resource_arn = "arn:aws:iam::aws:policy/SecurityAudit" + if iam_client.entities_role_attached_to_securityaudit_policy: + report.status = "PASS" + report.status_extended = f"SecurityAudit policy attached to role {iam_client.entities_role_attached_to_securityaudit_policy[0]['RoleName']}." + else: + report.status = "FAIL" + report.status_extended = ( + "SecurityAudit policy is not attached to any role." + ) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index edfbd1d1fe..a3b0d7a1b2 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -117,6 +117,16 @@ def __get_roles__(self): is_service_role=is_service_role(role), ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDenied": + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + roles = None + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -219,11 +229,28 @@ def __get_password_policy__(self): except ClientError as error: if error.response["Error"]["Code"] == "NoSuchEntity": - # Password policy does not exist - stored_password_policy = None + # Password policy is the IAM default + stored_password_policy = PasswordPolicy( + length=8, + symbols=False, + numbers=False, + uppercase=False, + lowercase=False, + allow_change=True, + expiration=False, + max_age=None, + reuse_prevention=None, + hard_expiry=None, + ) logger.warning( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + elif error.response["Error"]["Code"] == "AccessDenied": + # User does not have permission to get password policy + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + stored_password_policy = None else: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -395,34 +422,35 @@ def __list_attached_user_policies__(self): def __list_attached_role_policies__(self): logger.info("IAM - List Attached User Policies...") try: - for role in self.roles: - try: - attached_role_policies = [] - list_attached_role_policies_paginator = self.client.get_paginator( - "list_attached_role_policies" - ) - for page in list_attached_role_policies_paginator.paginate( - RoleName=role.name - ): - for policy in page["AttachedPolicies"]: - attached_role_policies.append(policy) - - role.attached_policies = attached_role_policies - except ClientError as error: - if error.response["Error"]["Code"] == "NoSuchEntity": - logger.warning( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + if self.roles: + for role in self.roles: + try: + attached_role_policies = [] + list_attached_role_policies_paginator = ( + self.client.get_paginator("list_attached_role_policies") ) - else: + for page in list_attached_role_policies_paginator.paginate( + RoleName=role.name + ): + for policy in page["AttachedPolicies"]: + attached_role_policies.append(policy) + + role.attached_policies = attached_role_policies + except ClientError as error: + if error.response["Error"]["Code"] == "NoSuchEntity": + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - except Exception as error: - logger.error( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -548,66 +576,67 @@ def __list_inline_group_policies__(self): def __list_inline_role_policies__(self): logger.info("IAM - List Inline Role Policies...") - for role in self.roles: - try: - inline_role_policies = [] - get_role_inline_policies_paginator = self.client.get_paginator( - "list_role_policies" - ) - for page in get_role_inline_policies_paginator.paginate( - RoleName=role.name - ): - for policy in page["PolicyNames"]: - try: - inline_role_policies.append(policy) - # Get inline policies & their policy documents here: - inline_policy = self.client.get_role_policy( - RoleName=role.name, PolicyName=policy - ) - inline_role_policy_doc = inline_policy["PolicyDocument"] - self.policies.append( - Policy( - name=policy, - arn=role.arn, - entity=role.name, - type="Inline", - attached=True, - version_id="v1", - document=inline_role_policy_doc, + if self.roles: + for role in self.roles: + try: + inline_role_policies = [] + get_role_inline_policies_paginator = self.client.get_paginator( + "list_role_policies" + ) + for page in get_role_inline_policies_paginator.paginate( + RoleName=role.name + ): + for policy in page["PolicyNames"]: + try: + inline_role_policies.append(policy) + # Get inline policies & their policy documents here: + inline_policy = self.client.get_role_policy( + RoleName=role.name, PolicyName=policy ) - ) - except ClientError as error: - if error.response["Error"]["Code"] == "NoSuchEntity": - logger.warning( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + inline_role_policy_doc = inline_policy["PolicyDocument"] + self.policies.append( + Policy( + name=policy, + arn=role.arn, + entity=role.name, + type="Inline", + attached=True, + version_id="v1", + document=inline_role_policy_doc, + ) ) - else: + except ClientError as error: + if error.response["Error"]["Code"] == "NoSuchEntity": + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - except Exception as error: - logger.error( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) + role.inline_policies = inline_role_policies - role.inline_policies = inline_role_policies + except ClientError as error: + if error.response["Error"]["Code"] == "NoSuchEntity": + logger.warning( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) - except ClientError as error: - if error.response["Error"]["Code"] == "NoSuchEntity": - logger.warning( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - else: + except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - except Exception as error: - logger.error( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - def __list_entities_role_for_policy__(self, policy_arn): logger.info("IAM - List Entities Role For Policy...") try: @@ -615,6 +644,17 @@ def __list_entities_role_for_policy__(self, policy_arn): roles = self.client.list_entities_for_policy( PolicyArn=policy_arn, EntityFilter="Role" )["PolicyRoles"] + return roles + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDenied": + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + roles = None + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -717,21 +757,24 @@ def __list_server_certificates__(self): def __list_tags_for_resource__(self): logger.info("IAM - List Tags...") try: - for role in self.roles: - try: - response = self.client.list_role_tags(RoleName=role.name)["Tags"] - role.tags = response - except ClientError as error: - if error.response["Error"]["Code"] == "NoSuchEntity": - role.tags = [] - else: + if self.roles: + for role in self.roles: + try: + response = self.client.list_role_tags(RoleName=role.name)[ + "Tags" + ] + role.tags = response + except ClientError as error: + if error.response["Error"]["Code"] == "NoSuchEntity": + role.tags = [] + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + except Exception as error: logger.error( f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - except Exception as error: - logger.error( - f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) except Exception as error: logger.error( diff --git a/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py b/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py index eba577f05b..c17bb03543 100644 --- a/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py +++ b/prowler/providers/aws/services/iam/iam_support_role_created/iam_support_role_created.py @@ -5,17 +5,18 @@ class iam_support_role_created(Check): def execute(self) -> Check_Report_AWS: findings = [] - report = Check_Report_AWS(self.metadata()) - report.region = iam_client.region - report.resource_id = "AWSSupportServiceRolePolicy" - report.resource_arn = ( - "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" - ) - if iam_client.entities_role_attached_to_support_policy: - report.status = "PASS" - report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}." - else: - report.status = "FAIL" - report.status_extended = "Support policy is not attached to any role." - findings.append(report) + if iam_client.entities_role_attached_to_support_policy is not None: + report = Check_Report_AWS(self.metadata()) + report.region = iam_client.region + report.resource_id = "AWSSupportServiceRolePolicy" + report.resource_arn = ( + "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" + ) + if iam_client.entities_role_attached_to_support_policy: + report.status = "PASS" + report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}." + else: + report.status = "FAIL" + report.status_extended = "Support policy is not attached to any role." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found.py b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found.py index 3e993a27f0..d0d4979282 100644 --- a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found.py +++ b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found.py @@ -7,18 +7,19 @@ class resourceexplorer2_indexes_found(Check): def execute(self): findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = "No Resource Explorer Indexes found." - report.region = resource_explorer_2_client.region - report.resource_arn = "NoResourceExplorer" - report.resource_id = resource_explorer_2_client.audited_account - report.resource_arn = resource_explorer_2_client.index_arn_template - if resource_explorer_2_client.indexes: - report.region = resource_explorer_2_client.indexes[0].region - report.resource_arn = resource_explorer_2_client.indexes[0].arn - report.status = "PASS" - report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}." - findings.append(report) + if resource_explorer_2_client.indexes is not None: + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No Resource Explorer Indexes found." + report.region = resource_explorer_2_client.region + report.resource_arn = "NoResourceExplorer" + report.resource_id = resource_explorer_2_client.audited_account + report.resource_arn = resource_explorer_2_client.index_arn_template + if resource_explorer_2_client.indexes: + report.region = resource_explorer_2_client.indexes[0].region + report.resource_arn = resource_explorer_2_client.indexes[0].arn + report.status = "PASS" + report.status_extended = f"Resource Explorer Indexes found: {len(resource_explorer_2_client.indexes)}." + findings.append(report) return findings diff --git a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py index b717017651..31894e69fc 100644 --- a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py +++ b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py @@ -1,3 +1,4 @@ +from botocore.client import ClientError from pydantic import BaseModel from prowler.lib.logger import logger @@ -23,6 +24,8 @@ def __list_indexes__(self, regional_client): if not self.audit_resources or ( is_resource_filtered(index["Arn"], self.audit_resources) ): + if self.indexes is None: + self.indexes = [] self.indexes.append( Indexes( arn=index["Arn"], @@ -30,6 +33,17 @@ def __list_indexes__(self, regional_client): type=index["Type"], ) ) + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.indexes: + self.indexes = None + else: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" diff --git a/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.py b/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.py index 912e1a31f8..2c47294647 100644 --- a/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.py +++ b/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.py @@ -7,22 +7,23 @@ class ssmincidents_enabled_with_plans(Check): def execute(self): findings = [] - report = Check_Report_AWS(self.metadata()) - report.status = "FAIL" - report.status_extended = "No SSM Incidents replication set exists." - report.resource_arn = ssmincidents_client.replication_set_arn_template - report.resource_id = ssmincidents_client.audited_account - report.region = ssmincidents_client.region - if ssmincidents_client.replication_set: - report.resource_arn = ssmincidents_client.replication_set[0].arn - report.resource_tags = [] # Not supported for replication sets - report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE." - if ssmincidents_client.replication_set[0].status == "ACTIVE": - report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist." - if ssmincidents_client.response_plans: - report.status = "PASS" - report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans." + if ssmincidents_client.replication_set is not None: + report = Check_Report_AWS(self.metadata()) + report.status = "FAIL" + report.status_extended = "No SSM Incidents replication set exists." + report.resource_arn = ssmincidents_client.replication_set_arn_template + report.resource_id = ssmincidents_client.audited_account + report.region = ssmincidents_client.region + if ssmincidents_client.replication_set: + report.resource_arn = ssmincidents_client.replication_set[0].arn + report.resource_tags = [] # Not supported for replication sets + report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} exists but not ACTIVE." + if ssmincidents_client.replication_set[0].status == "ACTIVE": + report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE but no response plans exist." + if ssmincidents_client.response_plans: + report.status = "PASS" + report.status_extended = f"SSM Incidents replication set {ssmincidents_client.replication_set[0].arn} is ACTIVE and has response plans." - findings.append(report) + findings.append(report) return findings diff --git a/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py b/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py index ff99f9eb53..53b2510f01 100644 --- a/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py +++ b/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py @@ -45,6 +45,17 @@ def __list_replication_sets__(self): arn=replication_set, ) ] + except ClientError as error: + if error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + if not self.replication_set: + self.replication_set = None + else: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: logger.error( f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py index 0bae8f9728..ec873b5f25 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings.py @@ -7,28 +7,29 @@ class trustedadvisor_errors_and_warnings(Check): def execute(self): findings = [] - if trustedadvisor_client.premium_support.enabled: - if trustedadvisor_client.checks: - for check in trustedadvisor_client.checks: - if ( - check.status != "not_available" - ): # avoid not_available checks since there are no resources that apply - report = Check_Report_AWS(self.metadata()) - report.region = check.region - report.resource_id = check.id - report.resource_arn = check.arn - report.status = "FAIL" - report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}." - if check.status == "ok": - report.status = "PASS" - findings.append(report) - else: - report = Check_Report_AWS(self.metadata()) - report.status = "MANUAL" - report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service." - report.resource_id = trustedadvisor_client.audited_account - report.resource_arn = trustedadvisor_client.account_arn_template - report.region = trustedadvisor_client.region - findings.append(report) + if trustedadvisor_client.premium_support: + if trustedadvisor_client.premium_support.enabled: + if trustedadvisor_client.checks: + for check in trustedadvisor_client.checks: + if ( + check.status != "not_available" + ): # avoid not_available checks since there are no resources that apply + report = Check_Report_AWS(self.metadata()) + report.region = check.region + report.resource_id = check.id + report.resource_arn = check.arn + report.status = "FAIL" + report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}." + if check.status == "ok": + report.status = "PASS" + findings.append(report) + else: + report = Check_Report_AWS(self.metadata()) + report.status = "MANUAL" + report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service." + report.resource_id = trustedadvisor_client.audited_account + report.resource_arn = trustedadvisor_client.account_arn_template + report.region = trustedadvisor_client.region + findings.append(report) return findings diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed.py index 8d045cf43a..aed03a57c0 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed.py @@ -7,7 +7,12 @@ class trustedadvisor_premium_support_plan_subscribed(Check): def execute(self): findings = [] - if trustedadvisor_client.audit_config.get("verify_premium_support_plans", True): + if ( + trustedadvisor_client.premium_support + and trustedadvisor_client.audit_config.get( + "verify_premium_support_plans", True + ) + ): report = Check_Report_AWS(self.metadata()) report.status = "FAIL" report.status_extended = ( diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py index 8a20c4b374..457a189fac 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py @@ -22,7 +22,6 @@ def __init__(self, provider): support_region = "us-east-1" else: support_region = "us-gov-west-1" - self.client = self.session.client(self.service, region_name=support_region) self.client.region = support_region self.__describe_services__() @@ -106,6 +105,19 @@ def __describe_services__(self): f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" f" {error}" ) + elif error.response["Error"]["Code"] == "AccessDeniedException": + logger.error( + f"{self.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) + self.premium_support = None + else: + logger.error( + f"{self.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) except Exception as error: logger.error( diff --git a/tests/providers/aws/services/account/account_maintain_different_contact_details_to_security_billing_and_operations/account_maintain_different_contact_details_to_security_billing_and_operations_test.py b/tests/providers/aws/services/account/account_maintain_different_contact_details_to_security_billing_and_operations/account_maintain_different_contact_details_to_security_billing_and_operations_test.py index 898d6785a1..d2aac9c79a 100644 --- a/tests/providers/aws/services/account/account_maintain_different_contact_details_to_security_billing_and_operations/account_maintain_different_contact_details_to_security_billing_and_operations_test.py +++ b/tests/providers/aws/services/account/account_maintain_different_contact_details_to_security_billing_and_operations/account_maintain_different_contact_details_to_security_billing_and_operations_test.py @@ -101,3 +101,29 @@ def test_contacts_diffent(self): assert result[0].region == AWS_REGION assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == AWS_ACCOUNT_ARN + + def test_access_denied(self): + account_client = mock.MagicMock + account_client.region = AWS_REGION + account_client.audited_account = AWS_ACCOUNT_NUMBER + account_client.audited_account_arn = AWS_ACCOUNT_ARN + account_client.contact_base = None + + with mock.patch( + "prowler.providers.aws.services.account.account_service.Account", + new=account_client, + ), mock.patch( + "prowler.providers.aws.services.account.account_client.account_client", + new=account_client, + ): + # Test Check + from prowler.providers.aws.services.account.account_maintain_different_contact_details_to_security_billing_and_operations.account_maintain_different_contact_details_to_security_billing_and_operations import ( + account_maintain_different_contact_details_to_security_billing_and_operations, + ) + + check = ( + account_maintain_different_contact_details_to_security_billing_and_operations() + ) + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist_test.py b/tests/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist_test.py index 857ae0d9c5..c8da157443 100644 --- a/tests/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist_test.py +++ b/tests/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist_test.py @@ -85,3 +85,28 @@ def test_one_backup_vault(self): assert result[0].resource_id == "MyBackupVault" assert result[0].resource_arn == backup_vault_arn assert result[0].region == AWS_REGION + + def test_access_denied(self): + backup_client = mock.MagicMock + backup_client.audited_account = AWS_ACCOUNT_NUMBER + backup_client.audited_account_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root" + backup_client.region = AWS_REGION + backup_client.audited_partition = "aws" + backup_client.backup_vault_arn_template = f"arn:{backup_client.audited_partition}:backup:{backup_client.region}:{backup_client.audited_account}:backup-vault" + backup_client.__get_backup_vault_arn_template__ = mock.MagicMock( + return_value=backup_client.backup_vault_arn_template + ) + backup_client.backup_vaults = None + with mock.patch( + "prowler.providers.aws.services.backup.backup_service.Backup", + new=backup_client, + ): + # Test Check + from prowler.providers.aws.services.backup.backup_vaults_exist.backup_vaults_exist import ( + backup_vaults_exist, + ) + + check = backup_vaults_exist() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete_test.py index 84fd3c4ecf..3bcd8f9a86 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete_test.py @@ -254,3 +254,35 @@ def test_trails_with_mfa_bucket_cross(self): assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] + + @mock_aws + @patch( + "botocore.client.BaseClient._make_api_call", + new=mock_make_api_call_getbucketversioning_mfadelete_enabled, + ) + def test_access_denied(self): + aws_provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_client, mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete.s3_client", + new=S3(aws_provider), + ) as s3_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_bucket_requires_mfa_delete.cloudtrail_bucket_requires_mfa_delete import ( + cloudtrail_bucket_requires_mfa_delete, + ) + + cloudtrail_client.trails = None + s3_client.buckets = [] + + check = cloudtrail_bucket_requires_mfa_delete() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py index 93a3eacf04..2b7372ea88 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py @@ -303,3 +303,34 @@ def test_trails_sending_and_not_sending_logs(self): == f"Single region trail {trail_name_eu} is not logging in the last 24h or not configured to deliver logs." ) assert report.resource_tags == [] + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ), + ): + with mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled.cloudtrail_client", + new=Cloudtrail( + set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ) + ), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_cloudwatch_logging_enabled.cloudtrail_cloudwatch_logging_enabled import ( + cloudtrail_cloudwatch_logging_enabled, + ) + + service_client.trails = None + + check = cloudtrail_cloudwatch_logging_enabled() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist_test.py index a74e621c44..cf6a47198c 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist_test.py @@ -128,3 +128,26 @@ def test_trails_with_insight_selector(self): assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] + + @mock_aws + def test_access_denied(self): + aws_provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ) + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_insights_exist.cloudtrail_insights_exist import ( + cloudtrail_insights_exist, + ) + + service_client.trails = None + check = cloudtrail_insights_exist() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled_test.py index 5afd6f62d6..fe650f9f97 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled_test.py @@ -137,3 +137,32 @@ def test_trail_kms(self): assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ), + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled.cloudtrail_client", + new=Cloudtrail( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_kms_encryption_enabled.cloudtrail_kms_encryption_enabled import ( + cloudtrail_kms_encryption_enabled, + ) + + service_client.trails = None + check = cloudtrail_kms_encryption_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled_test.py index 077f942014..7ef85b6b9b 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled_test.py @@ -155,3 +155,31 @@ def test_various_trails_with_and_without_logging_validation(self): assert report.resource_arn == trail_eu["TrailARN"] assert report.resource_tags == [] assert report.region == AWS_REGION_EU_WEST_1 + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ), + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled.cloudtrail_client", + new=Cloudtrail( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_log_file_validation_enabled.cloudtrail_log_file_validation_enabled import ( + cloudtrail_log_file_validation_enabled, + ) + + service_client.trails = None + + check = cloudtrail_log_file_validation_enabled() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled_test.py index 538208416b..58d57e7fd5 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled_test.py @@ -226,3 +226,40 @@ def test_bucket_cross_account(self): assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ), + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_client", + new=Cloudtrail( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as cloudtrail_client, mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled.s3_client", + new=S3( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as s3_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_access_logging_enabled.cloudtrail_logs_s3_bucket_access_logging_enabled import ( + cloudtrail_logs_s3_bucket_access_logging_enabled, + ) + + cloudtrail_client.trails = None + s3_client.buckets = [] + + check = cloudtrail_logs_s3_bucket_access_logging_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible_test.py index 081772eeb2..6bb1f87c64 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible_test.py @@ -289,3 +289,40 @@ def test_trail_bucket_cross_account(self): ) assert result[0].resource_tags == [] assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ), + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_client", + new=Cloudtrail( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as cloudtrail_client, mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.s3_client", + new=S3( + set_mocked_aws_provider([AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1]) + ), + ) as s3_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible import ( + cloudtrail_logs_s3_bucket_is_not_publicly_accessible, + ) + + cloudtrail_client.trails = None + s3_client.buckets = [] + + check = cloudtrail_logs_s3_bucket_is_not_publicly_accessible() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled_test.py index abbc851c68..b1a33eba37 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled_test.py @@ -289,3 +289,31 @@ def test_trail_multiregion_logging_and_single_region_not_logging(self): assert report.resource_id == trail_name_us assert report.resource_arn == trail_us["TrailARN"] assert report.resource_tags == [] + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled import ( + cloudtrail_multi_region_enabled, + ) + + service_client.trails = None + check = cloudtrail_multi_region_enabled() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events_test.py index a0a67f6c53..b3b7ba31b8 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events_test.py @@ -273,3 +273,29 @@ def test_non_compliant_trail_classic_event_selector(self): result[0].status_extended == "No trail found with multi-region enabled and logging management events." ) + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_client: + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled_logging_management_events.cloudtrail_multi_region_enabled_logging_management_events import ( + cloudtrail_multi_region_enabled_logging_management_events, + ) + + cloudtrail_client.trails = None + check = cloudtrail_multi_region_enabled_logging_management_events() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled_test.py index 648df19ce8..e834bf90ae 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled_test.py @@ -391,3 +391,33 @@ def test_trail_with_s3_three_colons(self): assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + aws_provider = set_mocked_aws_provider() + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_client, mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled.s3_client", + new=S3(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_read_enabled.cloudtrail_s3_dataevents_read_enabled import ( + cloudtrail_s3_dataevents_read_enabled, + ) + + cloudtrail_client.trails = None + check = cloudtrail_s3_dataevents_read_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled_test.py index a144be6b61..4baab6d603 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled_test.py @@ -389,3 +389,33 @@ def test_trail_with_s3_three_colons(self): assert result[0].resource_arn == trail_us["TrailARN"] assert result[0].resource_tags == [] assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + aws_provider = set_mocked_aws_provider() + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_service, mock.patch( + "prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled.s3_client", + new=S3(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.cloudtrail.cloudtrail_s3_dataevents_write_enabled.cloudtrail_s3_dataevents_write_enabled import ( + cloudtrail_s3_dataevents_write_enabled, + ) + + cloudtrail_service.trails = None + check = cloudtrail_s3_dataevents_write_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured_test.py index 8b45c232fc..274891b5fd 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured_test.py @@ -561,3 +561,51 @@ def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_with_newlines(sel == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.cloudwatch.cloudwatch_service import ( + CloudWatch, + Logs, + ) + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1] + ) + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.logs_client", + new=Logs(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_acls_alarm_configured.cloudwatch_changes_to_network_acls_alarm_configured import ( + cloudwatch_changes_to_network_acls_alarm_configured, + ) + + cloudtrail_client.trails = None + check = cloudwatch_changes_to_network_acls_alarm_configured() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled_test.py index c37bd7930d..4613c6e8ca 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_cross_account_sharing_disabled/cloudwatch_cross_account_sharing_disabled_test.py @@ -99,3 +99,39 @@ def test_cloudwatch_log_group_with_cross_account_role(self): == "CloudWatch has allowed cross-account sharing." ) assert result[0].resource_id == "CloudWatch-CrossAccountSharingRole" + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled.iam_client", + new=IAM(aws_provider), + ) as iam_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_cross_account_sharing_disabled.cloudwatch_cross_account_sharing_disabled import ( + cloudwatch_cross_account_sharing_disabled, + ) + + iam_client.roles = None + check = cloudwatch_cross_account_sharing_disabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled_test.py index 06246d4283..2f0a21121a 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_kms_encryption_enabled/cloudwatch_log_group_kms_encryption_enabled_test.py @@ -138,3 +138,39 @@ def test_cloudwatch_log_group_with_kms_key(self): == "Log Group test does have AWS KMS key test_kms_id associated." ) assert result[0].resource_id == "test" + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled.logs_client", + new=Logs(aws_provider), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_kms_encryption_enabled.cloudwatch_log_group_kms_encryption_enabled import ( + cloudwatch_log_group_kms_encryption_enabled, + ) + + service_client.log_groups = None + check = cloudwatch_log_group_kms_encryption_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs_test.py index b253c97d29..f9e0f7e880 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_no_secrets_in_logs/cloudwatch_log_group_no_secrets_in_logs_test.py @@ -154,3 +154,39 @@ def test_cloudwatch_log_group_with_secrets(self): "Potential secrets found in log group", result[0].status_extended ) assert result[0].resource_id == "test" + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs.logs_client", + new=Logs(aws_provider), + ) as logs_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_no_secrets_in_logs.cloudwatch_log_group_no_secrets_in_logs import ( + cloudwatch_log_group_no_secrets_in_logs, + ) + + logs_client.log_groups = None + check = cloudwatch_log_group_no_secrets_in_logs() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled_test.py index cd17a773fb..c08810c686 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_group_retention_policy_specific_days_enabled/cloudwatch_log_group_retention_policy_specific_days_enabled_test.py @@ -207,3 +207,47 @@ def test_cloudwatch_log_group_with_no_compliant_retention_days(self): == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:test" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + # Generate Logs Client + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + # Request Logs group + logs_client.create_log_group( + logGroupName="test", + ) + logs_client.put_retention_policy(logGroupName="test", retentionInDays=7) + from prowler.providers.aws.services.cloudwatch.cloudwatch_service import Logs + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + aws_provider._audit_config = {"log_group_retention_days": 365} + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled.logs_client", + new=Logs(aws_provider), + ) as service_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_group_retention_policy_specific_days_enabled.cloudwatch_log_group_retention_policy_specific_days_enabled import ( + cloudwatch_log_group_retention_policy_specific_days_enabled, + ) + + service_client.log_groups = None + check = cloudwatch_log_group_retention_policy_specific_days_enabled() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/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_test.py b/tests/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_test.py index 9374aca3bf..fccdac5970 100644 --- a/tests/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_test.py +++ b/tests/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_test.py @@ -575,3 +575,53 @@ def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_with_newlines(sel == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:metric-filter/test-filter" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_access_denied(self): + from prowler.providers.aws.services.cloudtrail.cloudtrail_service import ( + Cloudtrail, + ) + from prowler.providers.aws.services.cloudwatch.cloudwatch_service import ( + CloudWatch, + Logs, + ) + + aws_provider = set_mocked_aws_provider( + [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1] + ) + + from prowler.providers.common.models import Audit_Metadata + + aws_provider.audit_metadata = Audit_Metadata( + services_scanned=0, + # We need to set this check to call __describe_log_groups__ + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.logs_client", + new=Logs(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_client", + new=CloudWatch(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudtrail_client", + new=Cloudtrail(aws_provider), + ) as cloudtrail_client: + # Test Check + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled.cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled import ( + cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled, + ) + + check = ( + cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled() + ) + cloudtrail_client.trails = None + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant_test.py b/tests/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant_test.py index 91e76daa92..0c6fa8606a 100644 --- a/tests/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant_test.py +++ b/tests/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant_test.py @@ -26,6 +26,24 @@ def test_fms_not_admin(self): assert len(result) == 0 + def test_access_denied(self): + fms_client = mock.MagicMock + fms_client.region = AWS_REGION_US_EAST_1 + fms_client.fms_admin_account = None + with mock.patch( + "prowler.providers.aws.services.fms.fms_service.FMS", + new=fms_client, + ): + # Test Check + from prowler.providers.aws.services.fms.fms_policy_compliant.fms_policy_compliant import ( + fms_policy_compliant, + ) + + check = fms_policy_compliant() + result = check.execute() + + assert len(result) == 0 + def test_fms_admin_with_non_compliant_policies(self): fms_client = mock.MagicMock fms_client.audited_account = AWS_ACCOUNT_NUMBER diff --git a/tests/providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less_test.py b/tests/providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less_test.py index b382fa4eef..42825ab4d4 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_expires_passwords_within_90_days_or_less/iam_password_policy_expires_passwords_within_90_days_or_less_test.py @@ -145,3 +145,25 @@ def test_password_expiration_just_90(self): "Password expiration is set lower than 90 days", result[0].status_extended, ) + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less import ( + iam_password_policy_expires_passwords_within_90_days_or_less, + ) + + service_client.password_policy = None + check = iam_password_policy_expires_passwords_within_90_days_or_less() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase_test.py b/tests/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase_test.py index 425a01ebeb..6851cad1e5 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_lowercase/iam_password_policy_lowercase_test.py @@ -85,3 +85,25 @@ def test_iam_password_policy_lowercase_flag(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_lowercase.iam_password_policy_lowercase import ( + iam_password_policy_lowercase, + ) + + service_client.password_policy = None + check = iam_password_policy_lowercase() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14_test.py b/tests/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14_test.py index 6683910bc8..2682971e63 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_minimum_length_14/iam_password_policy_minimum_length_14_test.py @@ -129,3 +129,25 @@ def test_iam_password_policy_minimum_length_less_14(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_minimum_length_14.iam_password_policy_minimum_length_14 import ( + iam_password_policy_minimum_length_14, + ) + + service_client.password_policy = None + check = iam_password_policy_minimum_length_14() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number_test.py b/tests/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number_test.py index 962f1a5d9e..ed00ac4ec4 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_number/iam_password_policy_number_test.py @@ -92,3 +92,25 @@ def test_iam_password_policy_number_flag(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_number.iam_password_policy_number import ( + iam_password_policy_number, + ) + + service_client.password_policy = None + check = iam_password_policy_number() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24_test.py b/tests/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24_test.py index 5c4a81f12b..21d1b8b721 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_reuse_24/iam_password_policy_reuse_24_test.py @@ -89,3 +89,25 @@ def test_iam_password_policy_reuse_prevention_less_24(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_reuse_24.iam_password_policy_reuse_24 import ( + iam_password_policy_reuse_24, + ) + + service_client.password_policy = None + check = iam_password_policy_reuse_24() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol_test.py b/tests/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol_test.py index 3cdd7caa7b..31940d8634 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_symbol/iam_password_policy_symbol_test.py @@ -92,3 +92,25 @@ def test_iam_password_policy_symbol_flag(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_symbol.iam_password_policy_symbol import ( + iam_password_policy_symbol, + ) + + service_client.password_policy = None + check = iam_password_policy_symbol() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase_test.py b/tests/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase_test.py index 1ddba2d635..eda638374f 100644 --- a/tests/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase_test.py +++ b/tests/providers/aws/services/iam/iam_password_policy_uppercase/iam_password_policy_uppercase_test.py @@ -89,3 +89,25 @@ def test_iam_password_policy_uppercase_flag(self): == f"arn:aws:iam:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:password-policy" ) assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + from prowler.providers.aws.services.iam.iam_service import IAM + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_password_policy_uppercase.iam_password_policy_uppercase import ( + iam_password_policy_uppercase, + ) + + service_client.password_policy = None + check = iam_password_policy_uppercase() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy_test.py b/tests/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy_test.py index f248252be6..39edb0d5d0 100644 --- a/tests/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy_test.py +++ b/tests/providers/aws/services/iam/iam_role_administratoraccess_policy/iam_role_administratoraccess_policy_test.py @@ -264,3 +264,26 @@ def test_only_aws_service_linked_roles(self): check = iam_role_administratoraccess_policy() result = check.execute() assert len(result) == 0 + + @mock_aws(config={"iam": {"load_aws_managed_policies": True}}) + def test_access_denied(self): + iam_client = mock.MagicMock + iam_client.roles = None + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client", + new=iam_client, + ): + # Test Check + from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import ( + iam_role_administratoraccess_policy, + ) + + check = iam_role_administratoraccess_policy() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py b/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py index 7301d91c72..68ddf2d907 100644 --- a/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py +++ b/tests/providers/aws/services/iam/iam_securityaudit_role_created/iam_securityaudit_role_created_test.py @@ -85,3 +85,25 @@ def test_no_securityaudit_role_created(self): assert result[0].resource_id == "SecurityAudit" assert result[0].resource_arn == "arn:aws:iam::aws:policy/SecurityAudit" assert result[0].region == "us-east-1" + + @mock_aws(config={"iam": {"load_aws_managed_policies": True}}) + def test_access_denied(self): + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + from prowler.providers.aws.services.iam.iam_service import IAM + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_securityaudit_role_created.iam_securityaudit_role_created import ( + iam_securityaudit_role_created, + ) + + service_client.entities_role_attached_to_securityaudit_policy = None + + check = iam_securityaudit_role_created() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/iam/iam_support_role_created/iam_support_role_created_test.py b/tests/providers/aws/services/iam/iam_support_role_created/iam_support_role_created_test.py index 00eab94d5b..e545ae94ef 100644 --- a/tests/providers/aws/services/iam/iam_support_role_created/iam_support_role_created_test.py +++ b/tests/providers/aws/services/iam/iam_support_role_created/iam_support_role_created_test.py @@ -95,3 +95,25 @@ def test_no_support_role_created(self): result[0].resource_arn == "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy" ) + + @mock_aws(config={"iam": {"load_aws_managed_policies": True}}) + def test_access_denied(self): + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + from prowler.providers.aws.services.iam.iam_service import IAM + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created.iam_client", + new=IAM(aws_provider), + ) as service_client: + from prowler.providers.aws.services.iam.iam_support_role_created.iam_support_role_created import ( + iam_support_role_created, + ) + + service_client.entities_role_attached_to_support_policy = None + + check = iam_support_role_created() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found_test.py b/tests/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found_test.py index 51e5df9ca3..15db19e1a1 100644 --- a/tests/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found_test.py +++ b/tests/providers/aws/services/resourceexplorer2/resourceexplorer2_indexes_found/resourceexplorer2_indexes_found_test.py @@ -80,3 +80,31 @@ def test_one_index_found(self): assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == INDEX_ARN assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + resourceexplorer2_client = mock.MagicMock + resourceexplorer2_client.indexes = None + resourceexplorer2_client.audited_account = AWS_ACCOUNT_NUMBER + resourceexplorer2_client.audited_account_arn = ( + f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root" + ) + resourceexplorer2_client.audited_partition = "aws" + resourceexplorer2_client.region = AWS_REGION_US_EAST_1 + resourceexplorer2_client.index_arn_template = f"arn:{resourceexplorer2_client.audited_partition}:resource-explorer:{resourceexplorer2_client.region}:{resourceexplorer2_client.audited_account}:index" + resourceexplorer2_client.__get_index_arn_template__ = mock.MagicMock( + return_value=resourceexplorer2_client.index_arn_template + ) + with mock.patch( + "prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_service.ResourceExplorer2", + new=resourceexplorer2_client, + ): + # Test Check + from prowler.providers.aws.services.resourceexplorer2.resourceexplorer2_indexes_found.resourceexplorer2_indexes_found import ( + resourceexplorer2_indexes_found, + ) + + check = resourceexplorer2_indexes_found() + result = check.execute() + + # Assertions + assert len(result) == 0 diff --git a/tests/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans_test.py b/tests/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans_test.py index 186ee07e99..7393fbffb6 100644 --- a/tests/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans_test.py +++ b/tests/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans_test.py @@ -164,3 +164,30 @@ def test_ssmincidents_replicationset_active_with_plans(self): assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == REPLICATION_SET_ARN assert result[0].region == AWS_REGION_US_EAST_1 + + def test_access_denied(self): + ssmincidents_client = mock.MagicMock + ssmincidents_client.audited_account = AWS_ACCOUNT_NUMBER + ssmincidents_client.audited_partition = "aws" + ssmincidents_client.audited_account_arn = ( + f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root" + ) + ssmincidents_client.region = AWS_REGION_US_EAST_1 + ssmincidents_client.replication_set_arn_template = f"arn:{ssmincidents_client.audited_partition}:ssm-incidents:{ssmincidents_client.region}:{ssmincidents_client.audited_account}:replication-set" + ssmincidents_client.__get_replication_set_arn_template__ = mock.MagicMock( + return_value=ssmincidents_client.replication_set_arn_template + ) + ssmincidents_client.replication_set = None + with mock.patch( + "prowler.providers.aws.services.ssmincidents.ssmincidents_service.SSMIncidents", + new=ssmincidents_client, + ): + # Test Check + from prowler.providers.aws.services.ssmincidents.ssmincidents_enabled_with_plans.ssmincidents_enabled_with_plans import ( + ssmincidents_enabled_with_plans, + ) + + check = ssmincidents_enabled_with_plans() + result = check.execute() + + assert len(result) == 0 diff --git a/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py b/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py index 3b4d35320d..6ec0488174 100644 --- a/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py +++ b/tests/providers/aws/services/trustedadvisor/trustedadvisor_errors_and_warnings/trustedadvisor_errors_and_warnings_test.py @@ -144,3 +144,30 @@ def test_trustedadvisor_not_available_check(self): check = trustedadvisor_errors_and_warnings() result = check.execute() assert len(result) == 0 + + def test_access_denied(self): + trustedadvisor_client = mock.MagicMock + trustedadvisor_client.checks = [] + trustedadvisor_client.premium_support = None + trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER + trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN + trustedadvisor_client.checks.append( + Check( + id=CHECK_NAME, + name=CHECK_NAME, + arn=CHECK_ARN, + region=AWS_REGION_US_EAST_1, + status="not_available", + ) + ) + with mock.patch( + "prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor", + trustedadvisor_client, + ): + from prowler.providers.aws.services.trustedadvisor.trustedadvisor_errors_and_warnings.trustedadvisor_errors_and_warnings import ( + trustedadvisor_errors_and_warnings, + ) + + check = trustedadvisor_errors_and_warnings() + result = check.execute() + assert len(result) == 0 diff --git a/tests/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed_test.py b/tests/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed_test.py index 8a7b4e1f05..94019bdf55 100644 --- a/tests/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed_test.py +++ b/tests/providers/aws/services/trustedadvisor/trustedadvisor_premium_support_plan_subscribed/trustedadvisor_premium_support_plan_subscribed_test.py @@ -86,3 +86,30 @@ def test_premium_support_susbcribed(self): result[0].resource_arn == f"arn:aws:trusted-advisor:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account" ) + + def test_access_denied(self): + trustedadvisor_client = mock.MagicMock + trustedadvisor_client.checks = [] + trustedadvisor_client.premium_support = None + trustedadvisor_client.audited_account = AWS_ACCOUNT_NUMBER + trustedadvisor_client.audited_account_arn = AWS_ACCOUNT_ARN + trustedadvisor_client.audited_partition = "aws" + trustedadvisor_client.region = AWS_REGION_US_EAST_1 + + # Set verify_premium_support_plans config + trustedadvisor_client.audit_config = {"verify_premium_support_plans": True} + trustedadvisor_client.account_arn_template = f"arn:{trustedadvisor_client.audited_partition}:trusted-advisor:{trustedadvisor_client.region}:{trustedadvisor_client.audited_account}:account" + trustedadvisor_client.__get_account_arn_template__ = mock.MagicMock( + return_value=trustedadvisor_client.account_arn_template + ) + with mock.patch( + "prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor", + trustedadvisor_client, + ): + from prowler.providers.aws.services.trustedadvisor.trustedadvisor_premium_support_plan_subscribed.trustedadvisor_premium_support_plan_subscribed import ( + trustedadvisor_premium_support_plan_subscribed, + ) + + check = trustedadvisor_premium_support_plan_subscribed() + result = check.execute() + assert len(result) == 0