Skip to content

Commit 0a24d74

Browse files
authored
Added new taggable test (#70)
1 parent 4d9b110 commit 0a24d74

File tree

8 files changed

+182
-3
lines changed

8 files changed

+182
-3
lines changed

docs/BASIC_LINTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
| | `TAG012` | `"Resource MUST provide 'permissions' if 'tagging.taggable' is true"` |
5252
| | `TAG013` | `"'tagProperty' MUST specify property defined in the schema"` |
5353
| | `TAG014` | `"'tagProperty' MUST NOT be a part of 'writeOnlyProperties'"` |
54+
| | `TAG016` | `"'tagging.taggable' MUST be true when Taging Property is defined in the schema"` |
5455

5556
#### Permissions
5657
| Rule Name | Check Id | Message |

src/rpdk/guard_rail/core/runner.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def __exec_rules__(schema: Dict):
7373
@logdebug
7474
def __exec__(rules: str):
7575
guard_result = cfn_guard_rs.run_checks(schema, rules)
76+
tag_path = schema.get("TaggingPath")
7677

7778
def __render_output(evaluation_result: object):
7879
def __add_item__(rule_name: str, mapping: Mapping, result: Any):
@@ -88,9 +89,13 @@ def __add_item__(rule_name: str, mapping: Mapping, result: Any):
8889
try:
8990
if check.message:
9091
_message_dict = literal_eval(check.message.strip())
92+
_check_id = _message_dict["check_id"]
9193
_path = check.path
94+
if _check_id == "TAG016" and tag_path:
95+
_path = tag_path
96+
9297
rule_result = GuardRuleResult(
93-
check_id=_message_dict["check_id"],
98+
check_id=_check_id,
9499
message=_message_dict["message"],
95100
path=_path,
96101
)

src/rpdk/guard_rail/rule_library/tags/schema-linter-core-tagging-rules.guard

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,15 @@ rule ensure_property_tags_exists_v2 when tagging exists {
154154
}
155155
>>
156156
}
157+
158+
when tagging.taggable == false {
159+
TaggingPath !exists
160+
<<
161+
{
162+
"result": "NON_COMPLIANT",
163+
"check_id": "TAG016",
164+
"message": "`tagging.taggable` MUST be true when Taging Property is defined in the schema"
165+
}
166+
>>
167+
}
157168
}

src/rpdk/guard_rail/utils/schema_utils.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Module to handle schema manipulations."""
22
from copy import deepcopy
3-
from typing import Any, Dict, Sequence, Set, Tuple
3+
from typing import Any, Dict, List, Sequence, Set, Tuple
44

55
from jsonschema import RefResolver
66

@@ -143,5 +143,15 @@ def add_paths_to_schema(schema: Dict):
143143
Returns:
144144
Dict: schema with added paths
145145
"""
146-
schema["paths"] = _fetch_all_paths(schema)
146+
paths = _fetch_all_paths(schema)
147+
schema["paths"] = paths
148+
_add_tag_property(paths, schema)
147149
return schema
150+
151+
152+
def _add_tag_property(paths: List[str], schema: Dict):
153+
for path in paths:
154+
property_name = path.split("/")[-1]
155+
if "Tag" in property_name:
156+
schema["TaggingPath"] = path
157+
return
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"properties": {
3+
"StageDescription": {
4+
"$ref": "#/definitions/StageDescription"
5+
}
6+
},
7+
"definitions": {
8+
"StageDescription": {
9+
"type": "object",
10+
"additionalProperties": false,
11+
"properties": {
12+
"Tags": {
13+
"type": "array",
14+
"uniqueItems": false,
15+
"insertionOrder": false,
16+
"items": {
17+
"$ref": "#/definitions/Tag"
18+
}
19+
}
20+
}
21+
},
22+
"Tag": {
23+
"type": "object",
24+
"additionalProperties": false,
25+
"properties": {
26+
"Key": {
27+
"type": "string"
28+
},
29+
"Value": {
30+
"type": "string"
31+
}
32+
},
33+
"required": [
34+
"Value",
35+
"Key"
36+
]
37+
}
38+
},
39+
"tagging": {
40+
"taggable": false
41+
}
42+
}

tests/integ/runner/test_integ_runner.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,48 @@ def test_exec_compliance_stateless_tagging_permission_specified(
480480
assert warning_result == compliance_result.warning[warning_rule]
481481

482482

483+
@pytest.mark.parametrize(
484+
"collected_schemas,collected_rules,non_compliant_rules",
485+
[
486+
(
487+
collect_schemas(
488+
schemas=[
489+
"file:/"
490+
+ str(
491+
Path(os.path.dirname(os.path.realpath(__file__))).joinpath(
492+
"../data/schema-not-taggable-with-tags.json"
493+
)
494+
)
495+
]
496+
),
497+
[],
498+
{
499+
"ensure_property_tags_exists_v2": {
500+
GuardRuleResult(
501+
check_id="TAG016",
502+
message="`tagging.taggable` MUST be true when Taging Property is defined in the schema",
503+
path="/properties/StageDescription/Tags",
504+
),
505+
}
506+
},
507+
),
508+
],
509+
)
510+
def test_exec_compliance_stateless_not_taggable_with_tags(
511+
collected_schemas, collected_rules, non_compliant_rules
512+
):
513+
"""Test exec_compliance for stateless"""
514+
payload: Stateless = Stateless(schemas=collected_schemas, rules=collected_rules)
515+
compliance_result = exec_compliance(payload)[0]
516+
517+
# Assert for non-compliant rules
518+
for non_compliant_rule, non_compliant_result in non_compliant_rules.items():
519+
assert non_compliant_rule in compliance_result.non_compliant
520+
assert (
521+
non_compliant_result == compliance_result.non_compliant[non_compliant_rule]
522+
)
523+
524+
483525
@pytest.mark.parametrize(
484526
"previous_schema, current_schema, collected_rules,non_compliant_rules,warning_rules",
485527
[

tests/unit/utils/data/schemas-for-testing/schema-launch-template.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
{
22
"definitions": {
3+
"Description": {
4+
"type": "object",
5+
"additionalProperties": false,
6+
"properties": {
7+
"Tags": {
8+
"type": "array",
9+
"uniqueItems": false,
10+
"insertionOrder": false,
11+
"items": {
12+
"$ref": "#/definitions/Tag"
13+
}
14+
}
15+
}
16+
},
17+
"Tag": {
18+
"type": "object",
19+
"additionalProperties": false,
20+
"properties": {
21+
"Key": {
22+
"type": "string"
23+
},
24+
"Value": {
25+
"type": "string"
26+
}
27+
},
28+
"required": [
29+
"Value",
30+
"Key"
31+
]
32+
},
333
"LaunchTemplateSpecification": {
434
"type": "object",
535
"additionalProperties": false,
@@ -36,6 +66,9 @@
3666
"properties": {
3767
"LaunchTemplate": {
3868
"$ref": "#/definitions/LaunchTemplateSpecification"
69+
},
70+
"Description": {
71+
"$ref": "#/definitions/Description"
3972
}
4073
}
4174
}

tests/unit/utils/test_schema_utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def test_resolve_schema(schema, result):
104104
"/properties/LaunchTemplate/LaunchTemplateName",
105105
"/properties/LaunchTemplate/LaunchTemplateId",
106106
"/properties/LaunchTemplate/Version",
107+
"/properties/Description",
108+
"/properties/Description/Tags",
109+
"/properties/Description/Tags/*/Key",
110+
"/properties/Description/Tags/*/Value",
107111
},
108112
),
109113
],
@@ -118,3 +122,34 @@ def test_add_paths_to_schema(schema, result):
118122
)
119123
schema_with_paths = add_paths_to_schema(collected_schemas_to_resolve[0])
120124
assert set(schema_with_paths["paths"]) == result
125+
126+
127+
@pytest.mark.parametrize(
128+
"schema,result",
129+
[
130+
(
131+
"data/schemas-for-testing/schema-launch-template.json",
132+
{
133+
"/properties/LaunchTemplate",
134+
"/properties/LaunchTemplate/LaunchTemplateName",
135+
"/properties/LaunchTemplate/LaunchTemplateId",
136+
"/properties/LaunchTemplate/Version",
137+
"/properties/Description",
138+
"/properties/Description/Tags",
139+
"/properties/Description/Tags/*/Key",
140+
"/properties/Description/Tags/*/Value",
141+
},
142+
)
143+
],
144+
)
145+
def test_add_tag_path(schema, result):
146+
"""Unit test to verify that schema has tag property identified"""
147+
collected_schemas_to_resolve = collect_schemas(
148+
schemas=[
149+
"file:/"
150+
+ str(Path(os.path.dirname(os.path.realpath(__file__))).joinpath(schema))
151+
]
152+
)
153+
schema_with_paths = add_paths_to_schema(collected_schemas_to_resolve[0])
154+
assert "TaggingPath" in schema_with_paths
155+
assert schema_with_paths["TaggingPath"] == "/properties/Description/Tags"

0 commit comments

Comments
 (0)