Skip to content

Commit

Permalink
feat(waf): add new check waf_regional_rule_with_conditions (#5411)
Browse files Browse the repository at this point in the history
Co-authored-by: Sergio <[email protected]>
  • Loading branch information
HugoPBrito and sergargar authored Oct 16, 2024
1 parent 6e3c008 commit 3c0f360
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -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": ""
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 3c0f360

Please sign in to comment.