Keep order in request body to ensure auth signing works. (#3024)

* Keep order in request body to ensure auth signing works.

* Lint.

* More OrderedDict to ensure data parameter order.

* Lint.

* Improve CF test assertions.

* Fix syntax error.

* Cleanup CF test.
This commit is contained in:
Steve Pulec 2020-05-24 02:51:45 -05:00 committed by GitHub
parent 80b64f9b3f
commit 59c71760ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 13 deletions

View File

@ -16,7 +16,7 @@ from moto.core.exceptions import DryRunClientError
from jinja2 import Environment, DictLoader, TemplateNotFound from jinja2 import Environment, DictLoader, TemplateNotFound
import six import six
from six.moves.urllib.parse import parse_qs, urlparse from six.moves.urllib.parse import parse_qs, parse_qsl, urlparse
import xmltodict import xmltodict
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
@ -30,7 +30,7 @@ log = logging.getLogger(__name__)
def _decode_dict(d): def _decode_dict(d):
decoded = {} decoded = OrderedDict()
for key, value in d.items(): for key, value in d.items():
if isinstance(key, six.binary_type): if isinstance(key, six.binary_type):
newkey = key.decode("utf-8") newkey = key.decode("utf-8")
@ -199,7 +199,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
return cls()._dispatch(*args, **kwargs) return cls()._dispatch(*args, **kwargs)
def setup_class(self, request, full_url, headers): def setup_class(self, request, full_url, headers):
querystring = {} querystring = OrderedDict()
if hasattr(request, "body"): if hasattr(request, "body"):
# Boto # Boto
self.body = request.body self.body = request.body
@ -211,7 +211,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
# definition for back-compatibility # definition for back-compatibility
self.body = request.data self.body = request.data
querystring = {} querystring = OrderedDict()
for key, value in request.form.items(): for key, value in request.form.items():
querystring[key] = [value] querystring[key] = [value]
@ -240,7 +240,14 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
querystring[key] = [value] querystring[key] = [value]
elif self.body: elif self.body:
try: try:
querystring.update(parse_qs(raw_body, keep_blank_values=True)) querystring.update(
OrderedDict(
(key, [value])
for key, value in parse_qsl(
raw_body, keep_blank_values=True
)
)
)
except UnicodeEncodeError: except UnicodeEncodeError:
pass # ignore encoding errors, as the body may not contain a legitimate querystring pass # ignore encoding errors, as the body may not contain a legitimate querystring
if not querystring: if not querystring:

View File

@ -62,10 +62,9 @@ def test_boto3_json_invalid_missing_resource():
cf_conn.validate_template(TemplateBody=dummy_bad_template_json) cf_conn.validate_template(TemplateBody=dummy_bad_template_json)
assert False assert False
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
assert ( str(e).should.contain(
str(e) "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack"
== "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack" " with id Missing top level"
" with id Missing top level item Resources to file module does not exist"
) )
assert True assert True
@ -103,9 +102,8 @@ def test_boto3_yaml_invalid_missing_resource():
cf_conn.validate_template(TemplateBody=yaml_bad_template) cf_conn.validate_template(TemplateBody=yaml_bad_template)
assert False assert False
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
assert ( str(e).should.contain(
str(e) "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack"
== "An error occurred (ValidationError) when calling the ValidateTemplate operation: Stack" " with id Missing top level"
" with id Missing top level item Resources to file module does not exist"
) )
assert True assert True