JSON size violation gives a bad error with nested templates
This patch propose a normalized error message for both template validation and JSON validation. Also Add template validation in api template parser to prevent huge template (besides nested stacks) pass to engine. Change-Id: If62621d7a6f44100a158974f0fc9c3ddf58d8d99 Depends-On: I981d477c96345e68baa5a3d96c6671ad242dbb63 Closes-Bug: 1499379
This commit is contained in:
parent
bb894cde1c
commit
fc1c513d35
@ -96,6 +96,8 @@ class InstantiationData(object):
|
||||
adopt_data = self.data[rpc_api.PARAM_ADOPT_STACK_DATA]
|
||||
try:
|
||||
adopt_data = template_format.simple_parse(adopt_data)
|
||||
template_format.validate_template_limit(
|
||||
six.text_type(adopt_data['template']))
|
||||
return adopt_data['template']
|
||||
except (ValueError, KeyError) as ex:
|
||||
err_reason = _('Invalid adopt data: %s') % ex
|
||||
@ -103,7 +105,10 @@ class InstantiationData(object):
|
||||
elif self.PARAM_TEMPLATE in self.data:
|
||||
template_data = self.data[self.PARAM_TEMPLATE]
|
||||
if isinstance(template_data, dict):
|
||||
template_format.validate_template_limit(six.text_type(
|
||||
template_data))
|
||||
return template_data
|
||||
|
||||
elif self.PARAM_TEMPLATE_URL in self.data:
|
||||
url = self.data[self.PARAM_TEMPLATE_URL]
|
||||
LOG.debug('TemplateUrl %s' % url)
|
||||
|
@ -22,8 +22,6 @@ import yaml
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
|
||||
cfg.CONF.import_opt('max_template_size', 'heat.common.config')
|
||||
|
||||
if hasattr(yaml, 'CSafeLoader'):
|
||||
yaml_loader = yaml.CSafeLoader
|
||||
else:
|
||||
@ -39,6 +37,7 @@ def _construct_yaml_str(self, node):
|
||||
# Override the default string handling function
|
||||
# to always return unicode objects
|
||||
return self.construct_scalar(node)
|
||||
|
||||
yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str)
|
||||
# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type
|
||||
# datetime.data which causes problems in API layer when being processed by
|
||||
@ -75,16 +74,31 @@ def simple_parse(tmpl_str):
|
||||
return tpl
|
||||
|
||||
|
||||
def validate_template_limit(contain_str):
|
||||
"""Validate limit for the template.
|
||||
|
||||
Check if the contain exceeds allowed size range.
|
||||
"""
|
||||
|
||||
if len(contain_str) > cfg.CONF.max_template_size:
|
||||
msg = _("Template size (%(actual_len)s bytes) exceeds maximum "
|
||||
"allowed size (%(limit)s bytes)."
|
||||
) % {'actual_len': len(contain_str),
|
||||
'limit': cfg.CONF.max_template_size}
|
||||
raise exception.RequestLimitExceeded(message=msg)
|
||||
|
||||
|
||||
def parse(tmpl_str):
|
||||
"""Takes a string and returns a dict containing the parsed structure.
|
||||
|
||||
This includes determination of whether the string is using the
|
||||
JSON or YAML format.
|
||||
"""
|
||||
if len(tmpl_str) > cfg.CONF.max_template_size:
|
||||
msg = (_('Template exceeds maximum allowed size (%s bytes)') %
|
||||
cfg.CONF.max_template_size)
|
||||
raise exception.RequestLimitExceeded(message=msg)
|
||||
|
||||
# TODO(ricolin): Move this validation to api side.
|
||||
# Validate nested stack template.
|
||||
validate_template_limit(six.text_type(tmpl_str))
|
||||
|
||||
tpl = simple_parse(tmpl_str)
|
||||
# Looking for supported version keys in the loaded template
|
||||
if not ('HeatTemplateFormatVersion' in tpl
|
||||
|
@ -130,6 +130,20 @@ blarg: wibble
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, data.template)
|
||||
|
||||
def test_template_exceeds_max_template_size(self):
|
||||
cfg.CONF.set_override('max_template_size', 10)
|
||||
template = json.dumps(['a'] * cfg.CONF.max_template_size)
|
||||
body = {'template': template}
|
||||
data = stacks.InstantiationData(body)
|
||||
error = self.assertRaises(heat_exc.RequestLimitExceeded,
|
||||
data.template)
|
||||
|
||||
msg = ('Request limit exceeded: Template size (%(actual_len)s '
|
||||
'bytes) exceeds maximum allowed size (%(limit)s bytes).') % {
|
||||
'actual_len': len(str(template)),
|
||||
'limit': cfg.CONF.max_template_size}
|
||||
self.assertEqual(msg, six.text_type(error))
|
||||
|
||||
def test_parameters(self):
|
||||
params = {'foo': 'bar', 'blarg': 'wibble'}
|
||||
body = {'parameters': params,
|
||||
|
@ -97,7 +97,7 @@ class YamlMinimalTest(common.HeatTestCase):
|
||||
|
||||
def test_long_yaml(self):
|
||||
template = {'HeatTemplateFormatVersion': '2012-12-12'}
|
||||
config.cfg.CONF.set_override('max_template_size', 1024)
|
||||
config.cfg.CONF.set_override('max_template_size', 10)
|
||||
template['Resources'] = ['a'] * int(
|
||||
config.cfg.CONF.max_template_size / 3)
|
||||
limit = config.cfg.CONF.max_template_size
|
||||
@ -105,8 +105,10 @@ class YamlMinimalTest(common.HeatTestCase):
|
||||
self.assertTrue(len(long_yaml) > limit)
|
||||
ex = self.assertRaises(exception.RequestLimitExceeded,
|
||||
template_format.parse, long_yaml)
|
||||
msg = ('Request limit exceeded: Template exceeds maximum allowed size '
|
||||
'(1024 bytes)')
|
||||
msg = ('Request limit exceeded: Template size (%(actual_len)s '
|
||||
'bytes) exceeds maximum allowed size (%(limit)s bytes).') % {
|
||||
'actual_len': len(str(long_yaml)),
|
||||
'limit': config.cfg.CONF.max_template_size}
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_parse_no_version_format(self):
|
||||
|
Loading…
Reference in New Issue
Block a user