Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cloudwatch): add new check cloudwatch_alarm_actions_enabled #5416

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "cloudwatch_alarm_actions_enabled",
"CheckTitle": "Check if CloudWatch alarms have actions enabled",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "cloudwatch",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:cloudwatch:region:account-id:alarm/alarm-name",
"Severity": "high",
"ResourceType": "AwsCloudWatchAlarm",
"Description": "Alarm actions automatically alert you when a monitored metric is outside the defined threshold. If the alarm action is deactivated, no actions are run when the alarm changes state, and you won't be alerted to changes in monitored metrics. We recommend activating CloudWatch alarm actions to help you quickly respond to security and operational issues.",
"Risk": "Without active alarm actions, you may not be alerted to security or operational issues, potentially leading to delayed responses and increased risk.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html#alarms-and-actions",
"Remediation": {
"Code": {
"CLI": "aws cloudwatch enable-alarm-actions --alarm-names <alarm-name>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/cloudwatch-controls.html#cloudwatch-17",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure that all CloudWatch alarms have at least one action configured. This can include sending notifications to SNS topics, invoking Lambda functions, or triggering other AWS services.",
"Url": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/CloudWatch/cloudwatch-alarm-action-activated.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudwatch.cloudwatch_client import (
cloudwatch_client,
)


class cloudwatch_alarm_actions_enabled(Check):
def execute(self):
findings = []
for metric_alarm in cloudwatch_client.metric_alarms:
report = Check_Report_AWS(self.metadata())
report.region = metric_alarm.region
report.resource_id = metric_alarm.name
report.resource_arn = metric_alarm.arn
report.resource_tags = metric_alarm.tags
report.status = "PASS"
report.status_extended = (
f"CloudWatch metric alarm {metric_alarm.name} has actions enabled."
)
if not metric_alarm.actions_enabled:
report.status = "FAIL"
report.status_extended = f"CloudWatch metric alarm {metric_alarm.name} does not have actions enabled."
findings.append(report)
return findings
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def _describe_alarms(self, regional_client):
metric=metric_name,
name_space=namespace,
region=regional_client.region,
actions_enabled=alarm["ActionsEnabled"],
)
)
except ClientError as error:
Expand Down Expand Up @@ -247,6 +248,7 @@ class MetricAlarm(BaseModel):
name_space: Optional[str]
region: str
tags: Optional[list] = []
actions_enabled: bool = False


class LogGroup(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from unittest import mock

from boto3 import client
from moto import mock_aws

from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider


class Test_cloudwatch_alarm_actions_enabled:
@mock_aws
def test_no_cloudwatch_alarms(self):
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1)
cloudwatch_client.metric_alarms = []

from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
CloudWatch,
)

aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client",
new=CloudWatch(aws_provider),
):

from prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (
cloudwatch_alarm_actions_enabled,
)

check = cloudwatch_alarm_actions_enabled()
result = check.execute()

assert len(result) == 0

@mock_aws
def test_cloudwatch_alarms_actions_enabled(self):
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1)
cloudwatch_client.put_metric_alarm(
AlarmName="test_alarm",
AlarmDescription="Test alarm",
ActionsEnabled=True,
AlarmActions=["arn:aws:sns:us-east-1:123456789012:my-sns-topic"],
EvaluationPeriods=1,
ComparisonOperator="GreaterThanThreshold",
)

from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
CloudWatch,
)

aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client",
new=CloudWatch(aws_provider),
):

from prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (
cloudwatch_alarm_actions_enabled,
)

check = cloudwatch_alarm_actions_enabled()
result = check.execute()

assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "CloudWatch metric alarm test_alarm has actions enabled."
)
assert result[0].resource_id == "test_alarm"
assert (
result[0].resource_arn
== "arn:aws:cloudwatch:us-east-1:123456789012:alarm:test_alarm"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []

@mock_aws
def test_cloudwatch_alarms_actions_disabled(self):
cloudwatch_client = client("cloudwatch", region_name=AWS_REGION_US_EAST_1)
cloudwatch_client.put_metric_alarm(
AlarmName="test_alarm",
AlarmDescription="Test alarm",
ActionsEnabled=False,
AlarmActions=["arn:aws:sns:us-east-1:123456789012:my-sns-topic"],
EvaluationPeriods=1,
ComparisonOperator="GreaterThanThreshold",
)

from prowler.providers.aws.services.cloudwatch.cloudwatch_service import (
CloudWatch,
)

aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled.cloudwatch_client",
new=CloudWatch(aws_provider),
):

from prowler.providers.aws.services.cloudwatch.cloudwatch_alarm_actions_enabled.cloudwatch_alarm_actions_enabled import (
cloudwatch_alarm_actions_enabled,
)

check = cloudwatch_alarm_actions_enabled()
result = check.execute()

assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "CloudWatch metric alarm test_alarm does not have actions enabled."
)
assert result[0].resource_id == "test_alarm"
assert (
result[0].resource_arn
== "arn:aws:cloudwatch:us-east-1:123456789012:alarm:test_alarm"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def test_describe_alarms(self):
Threshold=2,
Unit="Seconds",
Tags=[{"Key": "key-1", "Value": "value-1"}],
ActionsEnabled=True,
)
aws_provider = set_mocked_aws_provider(
expected_checks=["cloudwatch_log_group_no_secrets_in_logs"]
Expand All @@ -133,6 +134,7 @@ def test_describe_alarms(self):
assert cloudwatch.metric_alarms[0].tags == [
{"Key": "key-1", "Value": "value-1"}
]
assert cloudwatch.metric_alarms[0].actions_enabled

# Test Logs Filters
@mock_aws
Expand Down