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(waf): add new check waf_regional_rule_with_conditions #5411

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": "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