diff --git a/prowler/providers/aws/services/wafv2/wafv2_webacl_rule_logging_enabled/wafv2_webacl_rule_logging_enabled.metadata.json b/prowler/providers/aws/services/wafv2/wafv2_webacl_rule_logging_enabled/wafv2_webacl_rule_logging_enabled.metadata.json index 90cd8eaa78..642388a88c 100644 --- a/prowler/providers/aws/services/wafv2/wafv2_webacl_rule_logging_enabled/wafv2_webacl_rule_logging_enabled.metadata.json +++ b/prowler/providers/aws/services/wafv2/wafv2_webacl_rule_logging_enabled/wafv2_webacl_rule_logging_enabled.metadata.json @@ -9,7 +9,7 @@ "SubServiceName": "", "ResourceIdTemplate": "arn:partition:wafv2:region:account-id:webacl/webacl-id", "Severity": "medium", - "ResourceType": "AwsWafv2WebAcl", + "ResourceType": "AwsWafv2RuleGroup", "Description": "This control checks whether an AWS WAF rule or rule group has Amazon CloudWatch metrics enabled. The control fails if the rule or rule group doesn't have CloudWatch metrics enabled.", "Risk": "Without CloudWatch Metrics enabled on AWS WAF rules or rule groups, it's challenging to monitor traffic flow effectively. This reduces visibility into potential security threats, such as malicious activities or unusual traffic patterns.", "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/APIReference/API_UpdateRuleGroup.html", diff --git a/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/__init__.py b/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.metadata.json b/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.metadata.json new file mode 100644 index 0000000000..e3ba151f1b --- /dev/null +++ b/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "wafv2_webacl_with_rules", + "CheckTitle": "Check if AWS WAFv2 WebACL has at least one rule or rule group.", + "CheckType": [ + "Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls" + ], + "ServiceName": "wafv2", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:wafv2:region:account-id:webacl/webacl-id", + "Severity": "medium", + "ResourceType": "AwsWafv2WebAcl", + "Description": "Check if AWS WAFv2 WebACL has at least one rule or rule group associated with it.", + "Risk": "An empty AWS WAF web ACL allows all web traffic to pass without inspection or control, exposing resources to potential security threats and attacks.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/APIReference/API_Rule.html", + "Remediation": { + "Code": { + "CLI": "aws wafv2 update-web-acl --id --scope --default-action --rules ", + "NativeIaC": "https://docs.prowler.com/checks/aws/networking-policies/bc_aws_networking_64/", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/waf-controls.html#waf-10", + "Terraform": "" + }, + "Recommendation": { + "Text": "Ensure that each AWS WAF web ACL contains at least one rule or rule group to effectively manage and inspect incoming HTTP(S) web requests.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-editing.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.py b/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.py new file mode 100644 index 0000000000..dd9bbdc4b8 --- /dev/null +++ b/prowler/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules.py @@ -0,0 +1,23 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.wafv2.wafv2_client import wafv2_client + + +class wafv2_webacl_with_rules(Check): + def execute(self): + findings = [] + for web_acl in wafv2_client.web_acls.values(): + report = Check_Report_AWS(self.metadata()) + report.region = web_acl.region + report.resource_id = web_acl.id + report.resource_arn = web_acl.arn + report.resource_tags = web_acl.tags + report.status = "FAIL" + report.status_extended = f"AWS WAFv2 Web ACL {web_acl.name} does not have any rules or rule groups attached." + + if web_acl.rules or web_acl.rule_groups: + report.status = "PASS" + report.status_extended = f"AWS WAFv2 Web ACL {web_acl.name} does have rules or rule groups attached." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules_test.py b/tests/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules_test.py new file mode 100644 index 0000000000..a975d5df51 --- /dev/null +++ b/tests/providers/aws/services/wafv2/wafv2_webacl_with_rules/wafv2_webacl_with_rules_test.py @@ -0,0 +1,149 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.aws.services.wafv2.wafv2_service import Rule, WebAclv2 +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_EU_WEST_1 + +waf_id = str(uuid4()) +waf_name = "waf-example" +waf_arn = f"arn:aws:wafv2:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:regional/webacl/{waf_name}/{waf_id}" + + +class Test_wafv2_webacl_with_rules: + def test_no_web_acls(self): + wafv2_client = mock.MagicMock + wafv2_client.web_acls = {} + with mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_service.WAFv2", + new=wafv2_client, + ), mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_client.wafv2_client", + new=wafv2_client, + ): + from prowler.providers.aws.services.wafv2.wafv2_webacl_with_rules.wafv2_webacl_with_rules import ( + wafv2_webacl_with_rules, + ) + + check = wafv2_webacl_with_rules() + result = check.execute() + assert len(result) == 0 + + def test_wafv2_wb_acl_with_rule(self): + wafv2_client = mock.MagicMock + wafv2_client.enabled = True + wafv2_client.web_acls = { + waf_arn: WebAclv2( + arn=waf_arn, + name=waf_name, + id=waf_id, + albs=[], + user_pools=[], + region=AWS_REGION_EU_WEST_1, + logging_enabled=True, + tags=[{"Key": "Name", "Value": waf_name}], + rules=[Rule(name="rule1", cloudwatch_metrics_enabled=True)], + ) + } + with mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_service.WAFv2", + new=wafv2_client, + ), mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_client.wafv2_client", + new=wafv2_client, + ): + from prowler.providers.aws.services.wafv2.wafv2_webacl_with_rules.wafv2_webacl_with_rules import ( + wafv2_webacl_with_rules, + ) + + check = wafv2_webacl_with_rules() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"AWS WAFv2 Web ACL {waf_name} does have rules or rule groups attached." + ) + assert result[0].resource_id == waf_id + assert result[0].resource_arn == waf_arn + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_tags == [{"Key": "Name", "Value": waf_name}] + + def test_wafv2_wb_acl_with_rule_group(self): + wafv2_client = mock.MagicMock + wafv2_client.enabled = True + wafv2_client.web_acls = { + waf_arn: WebAclv2( + arn=waf_arn, + name=waf_name, + id=waf_id, + albs=[], + user_pools=[], + region=AWS_REGION_EU_WEST_1, + logging_enabled=True, + tags=[{"Key": "Name", "Value": waf_name}], + rule_groups=[Rule(name="rule_group1", cloudwatch_metrics_enabled=True)], + ) + } + with mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_service.WAFv2", + new=wafv2_client, + ), mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_client.wafv2_client", + new=wafv2_client, + ): + from prowler.providers.aws.services.wafv2.wafv2_webacl_with_rules.wafv2_webacl_with_rules import ( + wafv2_webacl_with_rules, + ) + + check = wafv2_webacl_with_rules() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"AWS WAFv2 Web ACL {waf_name} does have rules or rule groups attached." + ) + assert result[0].resource_id == waf_id + assert result[0].resource_arn == waf_arn + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_tags == [{"Key": "Name", "Value": waf_name}] + + def test_wafv2_wb_acl_without_rule_or_rule_group(self): + wafv2_client = mock.MagicMock + wafv2_client.web_acls = {} + wafv2_client.enabled = True + wafv2_client.web_acls = { + waf_arn: WebAclv2( + arn=waf_arn, + name=waf_name, + id=waf_id, + albs=[], + user_pools=[], + region=AWS_REGION_EU_WEST_1, + logging_enabled=False, + tags=[{"Key": "Name", "Value": waf_name}], + ) + } + with mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_service.WAFv2", + new=wafv2_client, + ), mock.patch( + "prowler.providers.aws.services.wafv2.wafv2_client.wafv2_client", + new=wafv2_client, + ): + from prowler.providers.aws.services.wafv2.wafv2_webacl_with_rules.wafv2_webacl_with_rules import ( + wafv2_webacl_with_rules, + ) + + check = wafv2_webacl_with_rules() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"AWS WAFv2 Web ACL {waf_name} does not have any rules or rule groups attached." + ) + assert result[0].resource_id == waf_id + assert result[0].resource_arn == waf_arn + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_tags == [{"Key": "Name", "Value": waf_name}]