From e085e14247d66c789dbb1109fe6fc0e753be9aaf Mon Sep 17 00:00:00 2001 From: sahil-sols <168274064+sahil-sols@users.noreply.github.com> Date: Wed, 10 Jun 2026 22:19:06 +0530 Subject: [PATCH] fix(aws): order-independent CloudWatch metric filter pattern checks (#11345) Co-authored-by: Sahil Pugalia Co-authored-by: Cursor Co-authored-by: Lydia Vilchez --- prowler/CHANGELOG.md | 1 + ...etwork_acls_alarm_configured.metadata.json | 3 +- ...hanges_to_network_acls_alarm_configured.py | 12 +- ...es_to_network_gateways_alarm_configured.py | 12 +- ...oute_tables_alarm_configured.metadata.json | 2 +- ...o_network_route_tables_alarm_configured.py | 14 +- ...dwatch_changes_to_vpcs_alarm_configured.py | 17 +- ...ws_config_configuration_changes_enabled.py | 11 +- ...loudtrail_configuration_changes_enabled.py | 11 +- ...g_metric_filter_authentication_failures.py | 6 +- ...metric_filter_aws_organizations_changes.py | 28 ++- ...isable_or_scheduled_deletion_of_kms_cmk.py | 6 +- ...for_s3_bucket_policy_changes.metadata.json | 3 +- ...ric_filter_for_s3_bucket_policy_changes.py | 16 +- ...metric_filter_policy_changes.metadata.json | 1 - ...dwatch_log_metric_filter_policy_changes.py | 22 ++- ...og_metric_filter_security_group_changes.py | 12 +- ...c_filter_sign_in_without_mfa.metadata.json | 1 - ...h_log_metric_filter_sign_in_without_mfa.py | 6 +- .../services/cloudwatch/lib/metric_filters.py | 39 ++++ ...s_to_network_acls_alarm_configured_test.py | 182 ++++++++++++++++++ ..._network_gateways_alarm_configured_test.py | 92 +++++++++ ...work_route_tables_alarm_configured_test.py | 182 ++++++++++++++++++ ...h_changes_to_vpcs_alarm_configured_test.py | 182 ++++++++++++++++++ ...nfig_configuration_changes_enabled_test.py | 94 +++++++++ ...rail_configuration_changes_enabled_test.py | 94 +++++++++ ...ric_filter_authentication_failures_test.py | 92 +++++++++ ...c_filter_aws_organizations_changes_test.py | 92 +++++++++ ...e_or_scheduled_deletion_of_kms_cmk_test.py | 94 +++++++++ ...ilter_for_s3_bucket_policy_changes_test.py | 92 +++++++++ ...h_log_metric_filter_policy_changes_test.py | 92 +++++++++ ...tric_filter_security_group_changes_test.py | 92 +++++++++ ..._metric_filter_sign_in_without_mfa_test.py | 92 +++++++++ .../cloudwatch/cloudwatch_service_test.py | 14 ++ 34 files changed, 1689 insertions(+), 20 deletions(-) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 619401bf32..cd6db78f37 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `entra_users_mfa_capable` no longer flags pre-provisioned users with future `employeeHireDate`; future-hire date comparisons now tolerate naive datetimes [(#11511)](https://github.com/prowler-cloud/prowler/pull/11511) - M365 Admin Center group enumeration now follows Microsoft Graph pagination so group-scoped checks include groups beyond the first page [(#11510)](https://github.com/prowler-cloud/prowler/pull/11510) - GCP `kms_key_rotation_enabled` check now only verifies that automatic key rotation is enabled (any interval) instead of enforcing a 90-day period, resolving the mismatch between the check and its documentation; the CIS, Prowler ThreatScore, and CCC requirements that mandate a 90-day maximum were remapped to the new `kms_key_rotation_max_90_days` check [(#11516)](https://github.com/prowler-cloud/prowler/pull/11516) +- AWS CloudWatch log metric filter checks now validate `filterPattern` clauses regardless of order [(#11345)](https://github.com/prowler-cloud/prowler/pull/11345) --- diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.metadata.json index 20ed5f0adb..9070c7b0c7 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.metadata.json +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured/cloudwatch_changes_to_network_acls_alarm_configured.metadata.json @@ -17,9 +17,8 @@ "RelatedUrl": "", "AdditionalURLs": [ "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html", - "https://www.clouddefense.ai/compliance-rules/cis-v130/monitoring/cis-v130-4-11", "https://support.icompaas.com/support/solutions/articles/62000084031-ensure-a-log-metric-filter-and-alarm-exist-for-changes-to-network-access-control-lists-nacl-", - "https://trendmicro.com/trendaivisiononecloudriskmanagement/knowledge-base/aws/CloudWatchLogs/network-acl-changes-alarm.html", + "https://www.trendmicro.com/trendaivisiononecloudriskmanagement/knowledge-base/aws/CloudWatchLogs/network-acl-changes-alarm.html", "https://support.icompaas.com/support/solutions/articles/62000233134-4-11-ensure-network-access-control-list-nacl-changes-are-monitored-manual-" ], "Remediation": { 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 20d68a0121..68f45a8d27 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,16 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_names=[ + "CreateNetworkAcl", + "CreateNetworkAclEntry", + "DeleteNetworkAcl", + "DeleteNetworkAclEntry", + "ReplaceNetworkAclEntry", + "ReplaceNetworkAclAssociation", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 5f6eda3973..f7bf8e1d22 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,16 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_names=[ + "CreateCustomerGateway", + "DeleteCustomerGateway", + "AttachInternetGateway", + "CreateInternetGateway", + "DeleteInternetGateway", + "DetachInternetGateway", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.metadata.json index 82490a63da..89949cfbd6 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.metadata.json +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured.metadata.json @@ -37,5 +37,5 @@ ], "DependsOn": [], "RelatedTo": [], - "Notes": "" + "Notes": "Logging and Monitoring" } 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 f8fcc8eacb..460765cb2f 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,18 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_source="ec2.amazonaws.com", + event_names=[ + "CreateRoute", + "CreateRouteTable", + "ReplaceRoute", + "ReplaceRouteTableAssociation", + "DeleteRouteTable", + "DeleteRoute", + "DisassociateRouteTable", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 d7606647c4..be4fb0859d 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,21 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_names=[ + "CreateVpc", + "DeleteVpc", + "ModifyVpcAttribute", + "AcceptVpcPeeringConnection", + "CreateVpcPeeringConnection", + "DeleteVpcPeeringConnection", + "RejectVpcPeeringConnection", + "AttachClassicLinkVpc", + "DetachClassicLinkVpc", + "DisableVpcClassicLink", + "EnableVpcClassicLink", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 11bf08d99d..49bf9a03a3 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -15,7 +16,15 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change Check ): def execute(self): - pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?" + pattern = build_metric_filter_pattern( + event_source="config.amazonaws.com", + event_names=[ + "StopConfigurationRecorder", + "DeleteDeliveryChannel", + "PutDeliveryChannel", + "PutConfigurationRecorder", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 eb272ecfb1..e9567315f4 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -15,7 +16,15 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change Check ): def execute(self): - pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?" + pattern = build_metric_filter_pattern( + event_names=[ + "CreateTrail", + "UpdateTrail", + "DeleteTrail", + "StartLogging", + "StopLogging", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 c217cbbaf6..1b2e5173bb 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,10 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client class cloudwatch_log_metric_filter_authentication_failures(Check): def execute(self): - pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?" + pattern = build_metric_filter_pattern( + event_names=["ConsoleLogin"], + extra_clauses=[("errorMessage", "=", "Failed authentication")], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 af7ec82119..9976a885bb 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,32 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_source="organizations.amazonaws.com", + event_names=[ + "AcceptHandshake", + "AttachPolicy", + "CancelHandshake", + "CreateAccount", + "CreateOrganization", + "CreateOrganizationalUnit", + "CreatePolicy", + "DeclineHandshake", + "DeleteOrganization", + "DeleteOrganizationalUnit", + "DeletePolicy", + "EnableAllFeatures", + "EnablePolicyType", + "InviteAccountToOrganization", + "LeaveOrganization", + "DetachPolicy", + "DisablePolicyType", + "MoveAccount", + "RemoveAccountFromOrganization", + "UpdateOrganizationalUnit", + "UpdatePolicy", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 4cfed985f7..20d1d62a5a 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,10 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Check): def execute(self): - pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?" + pattern = build_metric_filter_pattern( + event_source="kms.amazonaws.com", + event_names=["DisableKey", "ScheduleKeyDeletion"], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.metadata.json index 2d77c42704..6292de6071 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.metadata.json +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.metadata.json @@ -17,8 +17,7 @@ "RelatedUrl": "", "AdditionalURLs": [ "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html", - "https://support.icompaas.com/support/solutions/articles/62000086674-ensure-a-log-metric-filter-and-alarm-exist-for-s3-bucket-policy-changes", - "https://www.tenable.com/audits/items/CIS_Amazon_Web_Services_Foundations_v5.0.0_L1.audit:8101350d6907e07863ac6748689b3e12" + "https://support.icompaas.com/support/solutions/articles/62000086674-ensure-a-log-metric-filter-and-alarm-exist-for-s3-bucket-policy-changes" ], "Remediation": { "Code": { 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 45d09b3528..d5fe1ef994 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,20 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_source="s3.amazonaws.com", + event_names=[ + "PutBucketAcl", + "PutBucketPolicy", + "PutBucketCors", + "PutBucketLifecycle", + "PutBucketReplication", + "DeleteBucketPolicy", + "DeleteBucketCors", + "DeleteBucketLifecycle", + "DeleteBucketReplication", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.metadata.json index 75cba1ee62..b1c36be2d8 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.metadata.json +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes.metadata.json @@ -17,7 +17,6 @@ "RelatedUrl": "", "AdditionalURLs": [ "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html", - "https://www.clouddefense.ai/compliance-rules/cis-v140/monitoring/cis-v140-4-4", "https://www.intelligentdiscovery.io/controls/cloudwatch/cloudwatch-alarm-iam-policy-change" ], "Remediation": { 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 f5efd04dde..4347a9b3aa 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,26 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_names=[ + "DeleteGroupPolicy", + "DeleteRolePolicy", + "DeleteUserPolicy", + "PutGroupPolicy", + "PutRolePolicy", + "PutUserPolicy", + "CreatePolicy", + "DeletePolicy", + "CreatePolicyVersion", + "DeletePolicyVersion", + "AttachRolePolicy", + "DetachRolePolicy", + "AttachUserPolicy", + "DetachUserPolicy", + "AttachGroupPolicy", + "DetachGroupPolicy", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( 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 a7972e420c..3557632904 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,16 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client 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.?" + pattern = build_metric_filter_pattern( + event_names=[ + "AuthorizeSecurityGroupIngress", + "AuthorizeSecurityGroupEgress", + "RevokeSecurityGroupIngress", + "RevokeSecurityGroupEgress", + "CreateSecurityGroup", + "DeleteSecurityGroup", + ], + ) findings = [] report = check_cloudwatch_log_metric_filter( diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.metadata.json b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.metadata.json index 20058c1ed8..76ffd832ff 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.metadata.json +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa.metadata.json @@ -21,7 +21,6 @@ "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html", "https://www.trendmicro.com/trendaivisiononecloudriskmanagement/knowledge-base/aws/CloudWatchLogs/console-sign-in-without-mfa.html", "https://www.tenable.com/audits/items/CIS_Amazon_Web_Services_Foundations_v3.0.0_L1.audit:1957056ee174cc38502d5f5f1864333b", - "https://www.clouddefense.ai/compliance-rules/gdpr/data-protection/log-metric-filter-console-login-mfa", "https://www.intelligentdiscovery.io/controls/cloudwatch/cloudwatch-alarm-no-mfa", "https://support.icompaas.com/support/solutions/articles/62000083605-ensure-a-log-metric-filter-and-alarm-exist-for-management-console-sign-in-without-mfa" ], 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 8437600646..07475a6185 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 @@ -6,6 +6,7 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_client import ( cloudwatch_client, ) from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, check_cloudwatch_log_metric_filter, ) from prowler.providers.aws.services.cloudwatch.logs_client import logs_client @@ -13,7 +14,10 @@ from prowler.providers.aws.services.cloudwatch.logs_client import logs_client class cloudwatch_log_metric_filter_sign_in_without_mfa(Check): def execute(self): - pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?" + pattern = build_metric_filter_pattern( + event_names=["ConsoleLogin"], + extra_clauses=[("additionalEventData.MFAUsed", "!=", "Yes")], + ) findings = [] report = check_cloudwatch_log_metric_filter( diff --git a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py index 84d70b4083..e5d104b840 100644 --- a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py +++ b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py @@ -3,6 +3,45 @@ import re from prowler.lib.check.models import Check_Report_AWS +def build_metric_filter_pattern( + *, + event_names: list[str] | None = None, + event_source: str | None = None, + extra_clauses: list[tuple[str, str, str]] | None = None, +) -> str: + """Build a regex pattern to match a CloudWatch Logs filterPattern string. + + All clauses must be present for the pattern to match, regardless of the + order in which AWS stores them. Event names are matched exactly, so a + short name like ``CreateRoute`` will not be satisfied by a longer one + like ``CreateRouteTable``. + + Pass the result directly to ``check_cloudwatch_log_metric_filter``. + + Args: + event_names: AWS API action names to require (``$.eventName``). + event_source: optional service principal to require (``$.eventSource``), + e.g. ``"ec2.amazonaws.com"``. + extra_clauses: additional conditions as ``(field, operator, value)`` + tuples, where ``operator`` is ``"="`` or ``"!="``. Example: + ``("additionalEventData.MFAUsed", "!=", "Yes")``. + + Returns: + A regex string for use with ``re.search(..., flags=re.DOTALL)``. + """ + parts: list[str] = [] + if event_source is not None: + parts.append(rf"(?=.*\$\.eventSource\s*=\s*.?{re.escape(event_source)})") + for name in event_names or []: + parts.append(rf"(?=.*\$\.eventName\s*=\s*.?{re.escape(name)}\b)") + for field, operator, value in extra_clauses or []: + if operator not in ("=", "!="): + raise ValueError(f"unsupported operator {operator!r}; expected '=' or '!='") + op = r"\s*!=\s*" if operator == "!=" else r"\s*=\s*" + parts.append(rf"(?=.*\$\.{re.escape(field)}{op}.?{re.escape(value)})") + return "".join(parts) + + def check_cloudwatch_log_metric_filter( metric_filter_pattern: str, trails: list, 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 66c2099843..928ff2b47c 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 @@ -674,3 +674,185 @@ class Test_cloudwatch_changes_to_network_acls_alarm_configured: result = check.execute() assert len(result) == 0 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = ReplaceNetworkAclAssociation) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = CreateNetworkAcl) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.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), + ), + ): + 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, + ) + + check = cloudwatch_changes_to_network_acls_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_substring_only_no_match(self): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.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), + ), + ): + 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, + ) + + check = cloudwatch_changes_to_network_acls_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "No CloudWatch log groups found with metric filters or alarms associated." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured_test.py index afe0f7d3ce..50c8663437 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured/cloudwatch_changes_to_network_gateways_alarm_configured_test.py @@ -616,3 +616,95 @@ class Test_cloudwatch_changes_to_network_gateways_alarm_configured: ) assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_tags == [{}] + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = DetachInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = CreateCustomerGateway) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_gateways_alarm_configured.cloudwatch_changes_to_network_gateways_alarm_configured import ( + cloudwatch_changes_to_network_gateways_alarm_configured, + ) + + check = cloudwatch_changes_to_network_gateways_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured_test.py index 7ec6e32c56..929793eb34 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured/cloudwatch_changes_to_network_route_tables_alarm_configured_test.py @@ -596,3 +596,185 @@ class Test_cloudwatch_changes_to_network_route_tables_alarm_configured: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = ec2.amazonaws.com) && ($.eventName = DisassociateRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DeleteRouteTable) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = ReplaceRoute) || ($.eventName = CreateRouteTable) || ($.eventName = CreateRoute) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import ( + cloudwatch_changes_to_network_route_tables_alarm_configured, + ) + + check = cloudwatch_changes_to_network_route_tables_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_substring_only_no_match(self): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = ec2.amazonaws.com) && ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DisassociateRouteTable) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_network_route_tables_alarm_configured.cloudwatch_changes_to_network_route_tables_alarm_configured import ( + cloudwatch_changes_to_network_route_tables_alarm_configured, + ) + + check = cloudwatch_changes_to_network_route_tables_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "No CloudWatch log groups found with metric filters or alarms associated." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured_test.py index 31f27030ff..57d33cb3fe 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_changes_to_vpcs_alarm_configured/cloudwatch_changes_to_vpcs_alarm_configured_test.py @@ -596,3 +596,185 @@ class Test_cloudwatch_changes_to_vpcs_alarm_configured: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = EnableVpcClassicLink) || ($.eventName = DisableVpcClassicLink) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = ModifyVpcAttribute) || ($.eventName = DeleteVpc) || ($.eventName = CreateVpc) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import ( + cloudwatch_changes_to_vpcs_alarm_configured, + ) + + check = cloudwatch_changes_to_vpcs_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_substring_only_no_match(self): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_changes_to_vpcs_alarm_configured.cloudwatch_changes_to_vpcs_alarm_configured import ( + cloudwatch_changes_to_vpcs_alarm_configured, + ) + + check = cloudwatch_changes_to_vpcs_alarm_configured() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "No CloudWatch log groups found with metric filters or alarms associated." + ) 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 30982553b2..62ca14d4ba 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 @@ -665,3 +665,97 @@ class Test_cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_c result = check.execute() assert len(result) == 0 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = config.amazonaws.com) && (($.eventName = PutConfigurationRecorder) || ($.eventName = PutDeliveryChannel) || ($.eventName = DeleteDeliveryChannel) || ($.eventName = StopConfigurationRecorder)) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.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), + ), + ): + 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() + ) + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/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_test.py b/tests/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_test.py index fcb98fda3e..3b555bf05b 100644 --- a/tests/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_test.py +++ b/tests/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_test.py @@ -610,3 +610,97 @@ class Test_cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_c == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = StopLogging) || ($.eventName = StartLogging) || ($.eventName = DeleteTrail) || ($.eventName = UpdateTrail) || ($.eventName = CreateTrail) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "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.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "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.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "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.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from 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 import ( + cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled, + ) + + check = ( + cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled() + ) + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures_test.py index 66b9d8116b..201be76f6d 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_authentication_failures/cloudwatch_log_metric_filter_authentication_failures_test.py @@ -596,3 +596,95 @@ class Test_cloudwatch_log_metric_filter_authentication_failures: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.errorMessage = Failed authentication) && ($.eventName = ConsoleLogin) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_authentication_failures.cloudwatch_log_metric_filter_authentication_failures import ( + cloudwatch_log_metric_filter_authentication_failures, + ) + + check = cloudwatch_log_metric_filter_authentication_failures() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes_test.py index 3aaf475cbe..534c50c906 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_aws_organizations_changes/cloudwatch_log_metric_filter_aws_organizations_changes_test.py @@ -596,3 +596,95 @@ class Test_cloudwatch_log_metric_filter_aws_organizations_changes: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = organizations.amazonaws.com) && ($.eventName = UpdatePolicy) || ($.eventName = UpdateOrganizationalUnit) || ($.eventName = RemoveAccountFromOrganization) || ($.eventName = MoveAccount) || ($.eventName = DisablePolicyType) || ($.eventName = DetachPolicy) || ($.eventName = LeaveOrganization) || ($.eventName = InviteAccountToOrganization) || ($.eventName = EnablePolicyType) || ($.eventName = EnableAllFeatures) || ($.eventName = DeletePolicy) || ($.eventName = DeleteOrganizationalUnit) || ($.eventName = DeleteOrganization) || ($.eventName = DeclineHandshake) || ($.eventName = CreatePolicy) || ($.eventName = CreateOrganizationalUnit) || ($.eventName = CreateOrganization) || ($.eventName = CreateAccount) || ($.eventName = CancelHandshake) || ($.eventName = AttachPolicy) || ($.eventName = AcceptHandshake) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_aws_organizations_changes.cloudwatch_log_metric_filter_aws_organizations_changes.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_aws_organizations_changes.cloudwatch_log_metric_filter_aws_organizations_changes.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_aws_organizations_changes.cloudwatch_log_metric_filter_aws_organizations_changes.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_aws_organizations_changes.cloudwatch_log_metric_filter_aws_organizations_changes import ( + cloudwatch_log_metric_filter_aws_organizations_changes, + ) + + check = cloudwatch_log_metric_filter_aws_organizations_changes() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/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_test.py b/tests/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_test.py index 7eb1cae331..f13263cf9f 100644 --- a/tests/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_test.py +++ b/tests/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_test.py @@ -610,3 +610,97 @@ class Test_cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = kms.amazonaws.com) && (($.eventName = ScheduleKeyDeletion) || ($.eventName = DisableKey)) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "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.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "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.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "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.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from 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 import ( + cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk, + ) + + check = ( + cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk() + ) + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes_test.py index 7d2d631f11..aa88f5164d 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes/cloudwatch_log_metric_filter_for_s3_bucket_policy_changes_test.py @@ -596,3 +596,95 @@ class Test_cloudwatch_log_metric_filter_for_s3_bucket_policy_changes: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventSource = s3.amazonaws.com) && (($.eventName = DeleteBucketReplication) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketPolicy) || ($.eventName = PutBucketReplication) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketAcl)) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes.cloudwatch_log_metric_filter_for_s3_bucket_policy_changes import ( + cloudwatch_log_metric_filter_for_s3_bucket_policy_changes, + ) + + check = cloudwatch_log_metric_filter_for_s3_bucket_policy_changes() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes_test.py index 06e36688d6..fd3e86d313 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_policy_changes/cloudwatch_log_metric_filter_policy_changes_test.py @@ -596,3 +596,95 @@ class Test_cloudwatch_log_metric_filter_unauthorized_api_calls: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = DetachGroupPolicy) || ($.eventName = AttachGroupPolicy) || ($.eventName = DetachUserPolicy) || ($.eventName = AttachUserPolicy) || ($.eventName = DetachRolePolicy) || ($.eventName = AttachRolePolicy) || ($.eventName = DeletePolicyVersion) || ($.eventName = CreatePolicyVersion) || ($.eventName = DeletePolicy) || ($.eventName = CreatePolicy) || ($.eventName = PutUserPolicy) || ($.eventName = PutRolePolicy) || ($.eventName = PutGroupPolicy) || ($.eventName = DeleteUserPolicy) || ($.eventName = DeleteRolePolicy) || ($.eventName = DeleteGroupPolicy) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_policy_changes.cloudwatch_log_metric_filter_policy_changes import ( + cloudwatch_log_metric_filter_policy_changes, + ) + + check = cloudwatch_log_metric_filter_policy_changes() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes_test.py index ede85e8e6e..35dee7b516 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_security_group_changes/cloudwatch_log_metric_filter_security_group_changes_test.py @@ -599,3 +599,95 @@ class Test_cloudwatch_log_metric_filter_unauthorized_api_calls: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.eventName = DeleteSecurityGroup) || ($.eventName = CreateSecurityGroup) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = AuthorizeSecurityGroupIngress) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_security_group_changes.cloudwatch_log_metric_filter_security_group_changes import ( + cloudwatch_log_metric_filter_security_group_changes, + ) + + check = cloudwatch_log_metric_filter_security_group_changes() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa_test.py index df67472cdd..6944860d75 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_log_metric_filter_sign_in_without_mfa/cloudwatch_log_metric_filter_sign_in_without_mfa_test.py @@ -596,3 +596,95 @@ class Test_cloudwatch_log_metric_filter_sign_in_without_mfa: == f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*" ) assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_cloudwatch_trail_with_log_group_with_metric_and_alarm_reversed_clauses( + self, + ): + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION_US_EAST_1) + cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1) + logs_client = client("logs", region_name=AWS_REGION_US_EAST_1) + s3_client = client("s3", region_name=AWS_REGION_US_EAST_1) + s3_client.create_bucket(Bucket="test") + logs_client.create_log_group(logGroupName="/log-group/test") + cloudtrail_client.create_trail( + Name="test_trail", + S3BucketName="test", + CloudWatchLogsLogGroupArn=f"arn:aws:logs:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:log-group:/log-group/test:*", + ) + logs_client.put_metric_filter( + logGroupName="/log-group/test", + filterName="test-filter", + filterPattern="{ ($.additionalEventData.MFAUsed != Yes) && ($.eventName = ConsoleLogin) }", + metricTransformations=[ + { + "metricName": "my-metric", + "metricNamespace": "my-namespace", + "metricValue": "$.value", + } + ], + ) + cloudwatch_client.put_metric_alarm( + AlarmName="test-alarm", + MetricName="my-metric", + Namespace="my-namespace", + Period=10, + EvaluationPeriods=5, + Statistic="Average", + Threshold=2, + ComparisonOperator="GreaterThanThreshold", + ActionsEnabled=True, + ) + + 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, + expected_checks=["cloudwatch_log_group_no_secrets_in_logs"], + completed_checks=0, + audit_progress=0, + ) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.logs_client", + new=Logs(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_client", + new=CloudWatch(aws_provider), + ), + mock.patch( + "prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudtrail_client", + new=Cloudtrail(aws_provider), + ), + ): + from prowler.providers.aws.services.cloudwatch.cloudwatch_log_metric_filter_sign_in_without_mfa.cloudwatch_log_metric_filter_sign_in_without_mfa import ( + cloudwatch_log_metric_filter_sign_in_without_mfa, + ) + + check = cloudwatch_log_metric_filter_sign_in_without_mfa() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudWatch log group /log-group/test found with metric filter test-filter and alarms set." + ) diff --git a/tests/providers/aws/services/cloudwatch/cloudwatch_service_test.py b/tests/providers/aws/services/cloudwatch/cloudwatch_service_test.py index 8ae7ed980f..33d4bde7d7 100644 --- a/tests/providers/aws/services/cloudwatch/cloudwatch_service_test.py +++ b/tests/providers/aws/services/cloudwatch/cloudwatch_service_test.py @@ -1,3 +1,4 @@ +import pytest from boto3 import client from moto import mock_aws @@ -5,6 +6,9 @@ from prowler.providers.aws.services.cloudwatch.cloudwatch_service import ( CloudWatch, Logs, ) +from prowler.providers.aws.services.cloudwatch.lib.metric_filters import ( + build_metric_filter_pattern, +) from tests.providers.aws.utils import ( AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1, @@ -216,3 +220,13 @@ class Test_CloudWatch_Service: assert logs.log_groups[arn].kms_id == "test_kms_id" assert logs.log_groups[arn].region == AWS_REGION_US_EAST_1 assert logs.log_groups[arn].tags == [{}] + + +class Test_build_metric_filter_pattern: + @pytest.mark.parametrize("bad_operator", ["==", "~=", "<", "<>", ">=", ""]) + def test_rejects_unsupported_operator(self, bad_operator): + with pytest.raises(ValueError, match="unsupported operator"): + build_metric_filter_pattern( + event_names=["ConsoleLogin"], + extra_clauses=[("errorMessage", bad_operator, "Failed authentication")], + )