Fixed #23397 -- Stripped whitespace from base64 during chunking

This insures the actual base64 content has a length a multiple of 4.
Also added a test case for the failure.
This commit is contained in:
Jason Hobbs 2014-09-02 12:23:51 -05:00 committed by Claude Paroz
parent 22bfc45146
commit e1424b2370
2 changed files with 17 additions and 8 deletions

View File

@ -206,14 +206,19 @@ class MultiPartParser(object):
for chunk in field_stream: for chunk in field_stream:
if transfer_encoding == 'base64': if transfer_encoding == 'base64':
# We only special-case base64 transfer encoding # We only special-case base64 transfer encoding
# We should always read base64 streams by multiple of 4 # We should always decode base64 chunks by multiple of 4,
over_bytes = len(chunk) % 4 # ignoring whitespace.
if over_bytes:
over_chunk = field_stream.read(4 - over_bytes) stripped_chunk = b"".join(chunk.split())
chunk += over_chunk
remaining = len(stripped_chunk) % 4
while remaining != 0:
over_chunk = field_stream.read(4 - remaining)
stripped_chunk += b"".join(over_chunk.split())
remaining = len(stripped_chunk) % 4
try: try:
chunk = base64.b64decode(chunk) chunk = base64.b64decode(stripped_chunk)
except Exception as e: except Exception as e:
# Since this is only a chunk, any error is an unfixable error. # Since this is only a chunk, any error is an unfixable error.
msg = "Could not decode base64 data: %r" % e msg = "Could not decode base64 data: %r" % e

View File

@ -77,14 +77,14 @@ class FileUploadTests(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def _test_base64_upload(self, content): def _test_base64_upload(self, content, encode=base64.b64encode):
payload = client.FakePayload("\r\n".join([ payload = client.FakePayload("\r\n".join([
'--' + client.BOUNDARY, '--' + client.BOUNDARY,
'Content-Disposition: form-data; name="file"; filename="test.txt"', 'Content-Disposition: form-data; name="file"; filename="test.txt"',
'Content-Type: application/octet-stream', 'Content-Type: application/octet-stream',
'Content-Transfer-Encoding: base64', 'Content-Transfer-Encoding: base64',
''])) '']))
payload.write(b"\r\n" + base64.b64encode(force_bytes(content)) + b"\r\n") payload.write(b"\r\n" + encode(force_bytes(content)) + b"\r\n")
payload.write('--' + client.BOUNDARY + '--\r\n') payload.write('--' + client.BOUNDARY + '--\r\n')
r = { r = {
'CONTENT_LENGTH': len(payload), 'CONTENT_LENGTH': len(payload),
@ -104,6 +104,10 @@ class FileUploadTests(TestCase):
def test_big_base64_upload(self): def test_big_base64_upload(self):
self._test_base64_upload("Big data" * 68000) # > 512Kb self._test_base64_upload("Big data" * 68000) # > 512Kb
def test_big_base64_newlines_upload(self):
self._test_base64_upload(
"Big data" * 68000, encode=base64.encodestring)
def test_unicode_file_name(self): def test_unicode_file_name(self):
tdir = sys_tempfile.mkdtemp() tdir = sys_tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tdir, True) self.addCleanup(shutil.rmtree, tdir, True)