From f43414379965ca2ded279e884fea2070206c7743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Mon, 14 Dec 2015 17:09:07 +0100 Subject: [PATCH] form: fix uploaded file mime types with server-side detection (#9315) --- tests/test_form_pages.py | 38 ++++++++++++++++++++++++++++++++++++++ wcs/qommon/form.py | 32 +++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index bf01718..fc79f22 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -1248,6 +1248,44 @@ def test_form_file_field_submit(pub): assert resp.content_type == 'text/plain' assert resp.body == 'foobar' +def test_form_file_field_submit_wrong_mimetype(pub): + formdef = create_formdef() + formdef.fields = [fields.FileField(id='0', label='file')] + formdef.store() + formdef.data_class().wipe() + + upload = Upload('test.txt', 'foobar', 'application/force-download') + + resp = get_app(pub).get('/test/') + resp.forms[0]['f0$file'] = upload + resp = resp.forms[0].submit('submit') + assert 'Check values then click submit.' in resp.body + resp = resp.forms[0].submit('submit') + assert resp.status_int == 302 + resp = resp.follow() + assert 'The form has been recorded' in resp.body + resp = resp.click('test.txt') + assert resp.location.endswith('/test.txt') + resp = resp.follow() + assert resp.content_type == 'text/plain' + assert resp.body == 'foobar' + + upload = Upload('test.pdf', '%PDF-1.4 ...', 'application/force-download') + + resp = get_app(pub).get('/test/') + resp.forms[0]['f0$file'] = upload + resp = resp.forms[0].submit('submit') + assert 'Check values then click submit.' in resp.body + resp = resp.forms[0].submit('submit') + assert resp.status_int == 302 + resp = resp.follow() + assert 'The form has been recorded' in resp.body + resp = resp.click('test.pdf') + assert resp.location.endswith('/test.pdf') + resp = resp.follow() + assert resp.content_type == 'application/pdf' + assert resp.body == '%PDF-1.4 ...' + def test_formdata_attachment_download(pub): create_user(pub) wf = Workflow(name='status') diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py index 6c5355a..0af0776 100644 --- a/wcs/qommon/form.py +++ b/wcs/qommon/form.py @@ -630,6 +630,26 @@ class FileWithPreviewWidget(CompositeWidget): # there's no file, the other checks are irrelevant. return + # Don't trust the browser supplied MIME type, update the Upload object + # with a MIME type created with magic (or based on the extension if the + # module is missing). + # + # This also helps people uploading PDF files that were downloaded from + # sites setting a wrong MIME type (like application/force-download) for + # various reasons. + if magic: + magic_object = magic.open(magic.MIME) + magic_object.load() + filetype = magic_object.file(self.value.fp.name).split(';')[0] + magic_object.close() + else: + filetype, encoding = mimetypes.guess_type(self.value.base_filename) + + if not filetype: + filetype = 'application/octet-stream' + + self.value.content_type = filetype + if self.max_file_size: # validate file size file_size = os.path.getsize(self.value.fp.name) @@ -642,21 +662,11 @@ class FileWithPreviewWidget(CompositeWidget): for file_type in self.file_type: accepted_file_types.extend(file_type.split(',')) - if magic: - magic_object = magic.open(magic.MIME) - magic_object.load() - filetype = magic_object.file(self.value.fp.name).split(';')[0] - magic_object.close() - else: - filetype, encoding = mimetypes.guess_type(self.value.base_filename) - if not filetype: - filetype = 'application/octet-stream' - valid_file_type = False for accepted_file_type in accepted_file_types: # fnmatch is used to handle generic mimetypes, like # image/* - if fnmatch.fnmatch(filetype, accepted_file_type): + if fnmatch.fnmatch(self.value.content_type, accepted_file_type): valid_file_type = True break if not valid_file_type: -- 2.6.4