-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(waf): add new check
waf_regional_rule_with_conditions
(#5411)
Co-authored-by: Sergio <[email protected]>
- Loading branch information
1 parent
6e3c008
commit 3c0f360
Showing
4 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
32 changes: 32 additions & 0 deletions
32
...ces/waf/waf_regional_rule_with_conditions/waf_regional_rule_with_conditions.metadata.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": "" | ||
} |
27 changes: 27 additions & 0 deletions
27
...s/aws/services/waf/waf_regional_rule_with_conditions/waf_regional_rule_with_conditions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
167 changes: 167 additions & 0 deletions
167
.../services/waf/waf_regional_rule_with_conditions/waf_regional_rule_with_conditions_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |