Skip to content

Commit e4f7c13

Browse files
authored
Fix issues with mapping values of 0 and AWS::NoValue (#3410)
* Fix issues with mapping values of 0 and AWS::NoValue * Error when no mapping and no default value in findinmap
1 parent df99e5b commit e4f7c13

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

src/cfnlint/jsonschema/_resolvers_cfn.py

+18-9
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def find_in_map(validator: Validator, instance: Any) -> ResolutionResult:
3939
if len(instance) not in [3, 4]:
4040
return
4141

42-
default_value = None
42+
default_value_found = None
4343
if len(instance) == 4:
4444
options = instance[3]
4545
if validator.is_type(options, "object"):
@@ -52,7 +52,16 @@ def find_in_map(validator: Validator, instance: Any) -> ResolutionResult:
5252
)
5353
),
5454
), None
55-
default_value = value
55+
default_value_found = True
56+
57+
if not default_value_found and not validator.context.mappings:
58+
yield None, validator, ValidationError(
59+
(
60+
f"{instance[0]!r} is not one of "
61+
f"{list(validator.context.mappings.keys())!r}"
62+
),
63+
path=deque([0]),
64+
)
5665

5766
if (
5867
validator.is_type(instance[0], "string")
@@ -63,8 +72,8 @@ def find_in_map(validator: Validator, instance: Any) -> ResolutionResult:
6372
and validator.is_type(instance[2], "string")
6473
):
6574
map = validator.context.mappings.get(instance[0])
66-
if not map:
67-
if not default_value:
75+
if map is None:
76+
if not default_value_found:
6877
yield None, validator, ValidationError(
6978
(
7079
f"{instance[0]!r} is not one of "
@@ -75,8 +84,8 @@ def find_in_map(validator: Validator, instance: Any) -> ResolutionResult:
7584
return
7685

7786
top_key = map.keys.get(instance[1])
78-
if not top_key:
79-
if not default_value:
87+
if top_key is None:
88+
if not default_value_found:
8089
yield None, validator, ValidationError(
8190
(
8291
f"{instance[1]!r} is not one of "
@@ -88,9 +97,9 @@ def find_in_map(validator: Validator, instance: Any) -> ResolutionResult:
8897
return
8998

9099
value = top_key.keys.get(instance[2])
91-
if not value:
92-
if not default_value:
93-
yield default_value, validator, ValidationError(
100+
if value is None:
101+
if not default_value_found:
102+
yield value, validator, ValidationError(
94103
(
95104
f"{instance[2]!r} is not one of "
96105
f"{list(top_key.keys.keys())!r} for mapping "

test/unit/module/jsonschema/test_resolvers_cfn.py

+52-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ def test_resolvers_ref(name, instance, response):
182182
],
183183
)
184184
def test_invalid_functions(name, instance, response):
185-
_resolve(name, instance, response)
185+
context = Context()
186+
context.mappings["foo"] = Map({"first": {"second": "bar"}})
187+
188+
_resolve(name, instance, response, context=context)
186189

187190

188191
@pytest.mark.parametrize(
@@ -221,6 +224,14 @@ def test_invalid_functions(name, instance, response):
221224
{"Fn::FindInMap": ["foo", "bar", "value", {"DefaultValue": "default"}]},
222225
[("default", deque([4, "DefaultValue"]), None)],
223226
),
227+
(
228+
"Valid FindInMap with a default value",
229+
{"Fn::FindInMap": ["foo", "first", "second", {"DefaultValue": "default"}]},
230+
[
231+
("default", deque([4, "DefaultValue"]), None),
232+
("bar", deque([2]), None),
233+
],
234+
),
224235
(
225236
"Valid FindInMap with a bad mapping",
226237
{"Fn::FindInMap": ["bar", "first", "second"]},
@@ -240,6 +251,18 @@ def test_invalid_functions(name, instance, response):
240251
{"Fn::FindInMap": ["bar", "first", "second", {"DefaultValue": "default"}]},
241252
[("default", deque([4, "DefaultValue"]), None)],
242253
),
254+
(
255+
"Valid FindInMap with a bad mapping and aws no value",
256+
{
257+
"Fn::FindInMap": [
258+
"bar",
259+
"first",
260+
"second",
261+
{"DefaultValue": {"Ref": "AWS::NoValue"}},
262+
]
263+
},
264+
[],
265+
),
243266
(
244267
"Valid FindInMap with a bad top key",
245268
{"Fn::FindInMap": ["foo", "second", "first"]},
@@ -298,3 +321,31 @@ def test_valid_functions(name, instance, response):
298321
context.mappings["foo"] = Map({"first": {"second": "bar"}})
299322

300323
_resolve(name, instance, response, context=context)
324+
325+
326+
@pytest.mark.parametrize(
327+
"name,instance,response",
328+
[
329+
(
330+
"Invalid FindInMap with no mappings",
331+
{"Fn::FindInMap": [{"Ref": "MyParameter"}, "B", "C"]},
332+
[
333+
(
334+
None,
335+
deque([]),
336+
ValidationError(
337+
("{'Ref': 'MyParameter'} is not one of []"),
338+
path=deque(["Fn::FindInMap", 0]),
339+
),
340+
)
341+
],
342+
),
343+
(
344+
"Invalid FindInMap with no mappings and default value",
345+
{"Fn::FindInMap": ["A", "B", "C", {"DefaultValue": "default"}]},
346+
[("default", deque([4, "DefaultValue"]), None)],
347+
),
348+
],
349+
)
350+
def test_no_mapping(name, instance, response):
351+
_resolve(name, instance, response)

0 commit comments

Comments
 (0)