Fix(uri): More robust when querystring contains malformed percent sign (%).

Parse malformed percent sign as a plain "%" like [Google
Search](https://www.google.com/?q=%%20%25) and other sites have done.
This commit is contained in:
Philip_Tzou
2015-09-05 17:01:11 -07:00
parent dcbde2512c
commit 5b6502cd07
2 changed files with 22 additions and 4 deletions

View File

@@ -178,9 +178,12 @@ if six.PY2:
tokens = decoded_uri.split('%') tokens = decoded_uri.split('%')
decoded_uri = tokens[0] decoded_uri = tokens[0]
for token in tokens[1:]: for token in tokens[1:]:
char, byte = _HEX_TO_BYTE[token[:2]] token_partial = token[:2]
decoded_uri += char + token[2:] if token_partial in _HEX_TO_BYTE:
char, byte = _HEX_TO_BYTE[token_partial]
else:
char, byte = '%', 0
decoded_uri += char + (token[2:] if byte else token)
only_ascii = only_ascii and (byte <= 127) only_ascii = only_ascii and (byte <= 127)
# PERF(kgriffs): Only spend the time to do this if there # PERF(kgriffs): Only spend the time to do this if there
@@ -235,7 +238,12 @@ else:
tokens = decoded_uri.split(b'%') tokens = decoded_uri.split(b'%')
decoded_uri = tokens[0] decoded_uri = tokens[0]
for token in tokens[1:]: for token in tokens[1:]:
decoded_uri += _HEX_TO_BYTE[token[:2]] + token[2:] token_partial = token[:2]
if token_partial in _HEX_TO_BYTE:
decoded_uri += _HEX_TO_BYTE[token_partial] + token[2:]
else:
# malformed percentage like "x=%" or "y=%+"
decoded_uri += b'%' + token
# Convert back to str # Convert back to str
return decoded_uri.decode('utf-8', 'replace') return decoded_uri.decode('utf-8', 'replace')

View File

@@ -64,6 +64,16 @@ class _TestQueryParams(testing.TestBase):
self.assertEqual(req.get_param_as_list('id', int), [23, 42]) self.assertEqual(req.get_param_as_list('id', int), [23, 42])
self.assertEqual(req.get_param('q'), u'\u8c46 \u74e3') self.assertEqual(req.get_param('q'), u'\u8c46 \u74e3')
def test_bad_percentage(self):
query_string = 'x=%%20%+%&y=peregrine&z=%a%z%zz%1%20e'
self.simulate_request('/', query_string=query_string)
self.assertEqual(self.srmock.status, falcon.HTTP_200)
req = self.resource.req
self.assertEqual(req.get_param('x'), '% % %')
self.assertEqual(req.get_param('y'), 'peregrine')
self.assertEqual(req.get_param('z'), '%a%z%zz%1 e')
def test_allowed_names(self): def test_allowed_names(self):
query_string = ('p=0&p1=23&2p=foo&some-thing=that&blank=&' query_string = ('p=0&p1=23&2p=foo&some-thing=that&blank=&'
'some_thing=x&-bogus=foo&more.things=blah&' 'some_thing=x&-bogus=foo&more.things=blah&'