Skip to content

Commit 8740131

Browse files
authored
feat: add rule E3063 to validate GuardDuty Detector property exclusivity (#4364)
* feat: add rule E3063 to validate GuardDuty Detector property exclusivity
1 parent 596a08b commit 8740131

3 files changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from typing import Any
9+
10+
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
11+
from cfnlint.rules.jsonschema.CfnLintJsonSchema import CfnLintJsonSchema
12+
13+
14+
class DetectorExclusiveProperties(CfnLintJsonSchema):
15+
id = "E3063"
16+
shortdesc = "Validate GuardDuty Detector property exclusivity"
17+
description = (
18+
"The request failed because both DataSources and Features were provided. "
19+
"You can provide only one; it is recommended to use Features."
20+
)
21+
source_url = "https://docs.aws.amazon.com/pt_br/guardduty/latest/ug/guardduty-features-activation-model.html"
22+
tags = ["resources", "guardduty"]
23+
24+
def __init__(self) -> None:
25+
super().__init__(
26+
keywords=[
27+
"Resources/AWS::GuardDuty::Detector/Properties",
28+
],
29+
all_matches=True,
30+
)
31+
32+
def validate(
33+
self, validator: Validator, _: Any, instance: Any, schema: dict[str, Any]
34+
) -> ValidationResult:
35+
if "DataSources" in instance and "Features" in instance:
36+
yield ValidationError(
37+
"Both 'DataSources' and 'Features' were provided. "
38+
"You can provide only one; it is recommended to use 'Features'.",
39+
rule=self,
40+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
import pytest
7+
8+
from cfnlint.rules.resources.guardduty.DetectorExclusiveProperties import (
9+
DetectorExclusiveProperties,
10+
)
11+
12+
13+
@pytest.fixture(scope="module")
14+
def rule():
15+
rule = DetectorExclusiveProperties()
16+
yield rule
17+
18+
19+
@pytest.mark.parametrize(
20+
"instance,expected_count,expected_message",
21+
[
22+
# Valid cases - should not trigger the rule
23+
(
24+
{
25+
"Enable": True,
26+
"Features": [
27+
{"Name": "S3_DATA_EVENTS", "Status": "ENABLED"},
28+
{"Name": "RUNTIME_MONITORING", "Status": "ENABLED"},
29+
],
30+
},
31+
0,
32+
None,
33+
),
34+
(
35+
{"Enable": True, "DataSources": {"S3Logs": {"Enable": True}}},
36+
0,
37+
None,
38+
),
39+
(
40+
{
41+
"Enable": True,
42+
}, # Neither provided (schema validation handles missing properties)
43+
0,
44+
None,
45+
),
46+
(
47+
[],
48+
0,
49+
None,
50+
),
51+
# Invalid cases - should trigger the rule
52+
(
53+
{
54+
"Enable": True,
55+
"DataSources": {"S3Logs": {"Enable": True}},
56+
"Features": [
57+
{"Name": "S3_DATA_EVENTS", "Status": "ENABLED"},
58+
{"Name": "EBS_MALWARE_PROTECTION", "Status": "ENABLED"},
59+
],
60+
},
61+
1,
62+
(
63+
"Both 'DataSources' and 'Features' were provided. "
64+
"You can provide only one; it is recommended to use 'Features'."
65+
),
66+
),
67+
],
68+
)
69+
def test_validate(instance, expected_count, expected_message, rule, validator):
70+
errs = list(rule.validate(validator, "", instance, {}))
71+
72+
assert len(errs) == expected_count, (
73+
f"Expected {expected_count} errors got {len(errs)}"
74+
)
75+
if expected_message:
76+
assert errs[0].message == expected_message

0 commit comments

Comments
 (0)