feat(waf): add new check waf_regional_rule_with_conditions (#5411)

Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
Hugo Pereira Brito
2024-10-16 18:41:57 +02:00
committed by GitHub
parent 6e3c008a89
commit 3c0f360244
4 changed files with 226 additions and 0 deletions
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "waf_regional_rule_with_conditions",
"CheckTitle": "AWS WAF Classic Regional Rules Should Have at Least One Condition.",
"CheckType": [
"Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls"
],
"ServiceName": "waf",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:waf-regional:region:account-id:rule/rule-id",
"Severity": "medium",
"ResourceType": "AwsWafRegionalRule",
"Description": "Ensure that every AWS WAF Classic Regional Rule contains at least one condition.",
"Risk": "An AWS WAF Classic Regional rule without any conditions cannot inspect or filter traffic, potentially allowing malicious requests to pass unchecked.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/waf-regional-rule-not-empty.html",
"Remediation": {
"Code": {
"CLI": "aws waf-regional update-rule --rule-id <your-rule-id> --change-token <your-change-token> --updates '[{\"Action\":\"INSERT\",\"Predicate\":{\"Negated\":false,\"Type\":\"IPMatch\",\"DataId\":\"<your-ipset-id>\"}}]' --region <your-region>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/waf-controls.html#waf-2",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure that every AWS WAF Classic Regional rule has at least one condition to properly inspect and manage web traffic.",
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/classic-web-acl-rules-editing.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,27 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.waf.wafregional_client import wafregional_client
class waf_regional_rule_with_conditions(Check):
def execute(self):
findings = []
for rule in wafregional_client.rules.values():
report = Check_Report_AWS(self.metadata())
report.region = rule.region
report.resource_id = rule.id
report.resource_arn = rule.arn
report.resource_tags = rule.tags
report.status = "FAIL"
report.status_extended = (
f"AWS WAF Regional Rule {rule.name} does not have any conditions."
)
if rule.predicates:
report.status = "PASS"
report.status_extended = (
f"AWS WAF Regional Rule {rule.name} has at least one condition."
)
findings.append(report)
return findings
@@ -0,0 +1,167 @@
from unittest import mock
from unittest.mock import patch
import botocore
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
RULE_ID = "test-rule-id"
# Original botocore _make_api_call function
orig = botocore.client.BaseClient._make_api_call
# Mocked botocore _make_api_call function
def mock_make_api_call_compliant_rule(self, operation_name, kwarg):
if operation_name == "ListRules":
return {
"Rules": [
{
"RuleId": RULE_ID,
"Name": RULE_ID,
},
]
}
if operation_name == "GetRule":
return {
"Rule": {
"RuleId": RULE_ID,
"Predicates": [
{
"Negated": False,
"Type": "IPMatch",
"DataId": "IPSetId",
},
],
}
}
return orig(self, operation_name, kwarg)
def mock_make_api_call_non_compliant_rule(self, operation_name, kwarg):
if operation_name == "ListRules":
return {
"Rules": [
{
"RuleId": RULE_ID,
"Name": RULE_ID,
},
]
}
if operation_name == "GetRule":
return {
"Rule": {
"RuleId": RULE_ID,
"Predicates": [],
}
}
return orig(self, operation_name, kwarg)
class Test_waf_regional_rule_with_conditions:
@mock_aws
def test_no_rules(self):
from prowler.providers.aws.services.waf.waf_service import WAFRegional
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,
):
with mock.patch(
"prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions.wafregional_client",
new=WAFRegional(aws_provider),
):
# Test Check
from prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions import (
waf_regional_rule_with_conditions,
)
check = waf_regional_rule_with_conditions()
result = check.execute()
assert len(result) == 0
@patch(
"botocore.client.BaseClient._make_api_call",
new=mock_make_api_call_compliant_rule,
)
@mock_aws
def test_waf_rules_with_condition(self):
from prowler.providers.aws.services.waf.waf_service import WAFRegional
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,
):
with mock.patch(
"prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions.wafregional_client",
new=WAFRegional(aws_provider),
):
# Test Check
from prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions import (
waf_regional_rule_with_conditions,
)
check = waf_regional_rule_with_conditions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"AWS WAF Regional Rule {RULE_ID} has at least one condition."
)
assert result[0].resource_id == RULE_ID
assert (
result[0].resource_arn
== f"arn:aws:waf-regional:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:rule/{RULE_ID}"
)
assert result[0].region == AWS_REGION_US_EAST_1
@patch(
"botocore.client.BaseClient._make_api_call",
new=mock_make_api_call_non_compliant_rule,
)
@mock_aws
def test_waf_rules_without_condition(self):
from prowler.providers.aws.services.waf.waf_service import WAFRegional
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,
):
with mock.patch(
"prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions.wafregional_client",
new=WAFRegional(aws_provider),
):
# Test Check
from prowler.providers.aws.services.waf.waf_regional_rule_with_conditions.waf_regional_rule_with_conditions import (
waf_regional_rule_with_conditions,
)
check = waf_regional_rule_with_conditions()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"AWS WAF Regional Rule {RULE_ID} does not have any conditions."
)
assert result[0].resource_id == RULE_ID
assert (
result[0].resource_arn
== f"arn:aws:waf-regional:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:rule/{RULE_ID}"
)
assert result[0].region == AWS_REGION_US_EAST_1