in src/cfnlint/rules/resources/properties/Properties.py [0:0]
def propertycheck(self, text, proptype, parenttype, resourcename, path, root):
"""Check individual properties"""
parameternames = self.parameternames
matches = []
if root:
specs = self.resourcetypes
resourcetype = parenttype
else:
specs = self.propertytypes
resourcetype = str.format('{0}.{1}', parenttype, proptype)
# Handle tags
if resourcetype not in specs:
if proptype in specs:
resourcetype = proptype
else:
resourcetype = str.format('{0}.{1}', parenttype, proptype)
else:
resourcetype = str.format('{0}.{1}', parenttype, proptype)
resourcespec = specs[resourcetype].get('Properties', {})
if not resourcespec:
if specs[resourcetype].get('Type') == 'List':
if isinstance(text, list):
property_type = specs[resourcetype].get('ItemType')
for index, item in enumerate(text):
matches.extend(
self.propertycheck(
item, property_type, parenttype, resourcename,
path[:] + [index], root))
return matches
supports_additional_properties = specs[resourcetype].get('AdditionalProperties', False)
if text == 'AWS::NoValue':
return matches
if not isinstance(text, dict):
if not self.check_exceptions(parenttype, proptype, text):
message = 'Expecting an object at %s' % ('/'.join(map(str, path)))
matches.append(RuleMatch(path, message))
return matches
# You can put in functions directly in place of objects as long as that is
# the only thing there (conditions, select) could all (possibly)
# return objects. FindInMap cannot directly return an object.
len_of_text = len(text)
#pylint: disable=too-many-nested-blocks
for prop in text:
proppath = path[:]
proppath.append(prop)
if prop not in resourcespec:
if prop in cfnlint.helpers.CONDITION_FUNCTIONS and len_of_text == 1:
cond_values = self.cfn.get_condition_values(text[prop])
for cond_value in cond_values:
if isinstance(cond_value['Value'], dict):
matches.extend(self.propertycheck(
cond_value['Value'], proptype, parenttype, resourcename,
proppath + cond_value['Path'], root))
elif isinstance(cond_value['Value'], list):
for index, item in enumerate(cond_value['Value']):
matches.extend(
self.propertycheck(
item, proptype, parenttype, resourcename,
proppath + cond_value['Path'] + [index], root)
)
elif text.is_function_returning_object():
self.logger.debug('Ran into function "%s". Skipping remaining checks', prop)
elif len(text) == 1 and prop in 'Ref' and text.get(prop) == 'AWS::NoValue':
pass
elif not supports_additional_properties:
message = 'Invalid Property %s' % ('/'.join(map(str, proppath)))
matches.append(RuleMatch(proppath, message))
else:
if 'Type' in resourcespec[prop]:
if resourcespec[prop]['Type'] == 'List':
if 'PrimitiveItemType' not in resourcespec[prop]:
if isinstance(text[prop], list):
for index, item in enumerate(text[prop]):
arrproppath = proppath[:]
arrproppath.append(index)
matches.extend(self.propertycheck(
item, resourcespec[prop]['ItemType'],
parenttype, resourcename, arrproppath, False))
elif (isinstance(text[prop], dict)):
# A list can be be specific as a Conditional
matches.extend(
self.check_list_for_condition(
text, prop, parenttype, resourcename, resourcespec[prop], proppath)
)
else:
message = 'Property {0} should be of type List for resource {1}'
matches.append(
RuleMatch(
proppath,
message.format(prop, resourcename)))
else:
if isinstance(text[prop], list):
primtype = resourcespec[prop]['PrimitiveItemType']
for index, item in enumerate(text[prop]):
arrproppath = proppath[:]
arrproppath.append(index)
matches.extend(self.primitivetypecheck(
item, primtype, arrproppath))
elif isinstance(text[prop], dict):
if 'Ref' in text[prop]:
ref = text[prop]['Ref']
if ref == 'AWS::NotificationARNs':
continue
if ref in parameternames:
param_type = self.cfn.template['Parameters'][ref]['Type']
if param_type:
if 'List<' not in param_type and '<List' not in param_type and not param_type == 'CommaDelimitedList':
message = 'Property {0} should be of type List or Parameter should ' \
'be a list for resource {1}'
matches.append(
RuleMatch(
proppath,
message.format(prop, resourcename)))
else:
message = 'Property {0} should be of type List for resource {1}'
matches.append(
RuleMatch(
proppath,
message.format(prop, resourcename)))
else:
if len(text[prop]) == 1:
for k in text[prop].keys():
def_intrinsic_type = self.intrinsictypes.get(k, {})
if def_intrinsic_type:
if len(def_intrinsic_type.get('ReturnTypes')) == 1 and def_intrinsic_type.get('ReturnTypes')[0] == 'Singular':
message = 'Property {0} is using {1} when a List is needed for resource {2}'
matches.append(
RuleMatch(
proppath,
message.format(prop, k, resourcename)))
else:
message = 'Property {0} should be of type List for resource {1}'
matches.append(
RuleMatch(
proppath,
message.format(prop, resourcename)))
else:
if resourcespec[prop]['Type'] not in ['Map']:
matches.extend(self.propertycheck(
text[prop], resourcespec[prop]['Type'], parenttype,
resourcename, proppath, False))
elif 'PrimitiveType' in resourcespec[prop]:
primtype = resourcespec[prop]['PrimitiveType']
matches.extend(self.primitivetypecheck(text[prop], primtype, proppath))
return matches