From 5fa9e071c71d22c6c415d9ee195833451682d860 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Fri, 6 Sep 2019 11:35:51 +0100 Subject: [PATCH] Add check that PDF file can be opened and is not malformed This checks that the PDF file is not malformed in some way (e.g. by missing the EOF marker). We check this by trying to get the page count of the letter which will be needed to display the preview of the letter. --- app/main/views/uploads.py | 28 ++++++++++++++++++++----- tests/app/main/views/test_uploads.py | 14 +++++++++++++ tests/test_pdf_files/no_eof_marker.pdf | Bin 0 -> 14046 bytes 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/test_pdf_files/no_eof_marker.pdf diff --git a/app/main/views/uploads.py b/app/main/views/uploads.py index 052329766..6a2315a90 100644 --- a/app/main/views/uploads.py +++ b/app/main/views/uploads.py @@ -1,7 +1,16 @@ import uuid from io import BytesIO -from flask import flash, redirect, render_template, request, url_for +from flask import ( + current_app, + flash, + redirect, + render_template, + request, + url_for, +) +from notifications_utils.pdf import pdf_page_count +from PyPDF2.utils import PdfReadError from app import current_service from app.extensions import antivirus_client @@ -28,12 +37,16 @@ def upload_letter(service_id): virus_free = antivirus_client.scan(BytesIO(pdf_file_bytes)) if not virus_free: - flash('Your file has failed the virus check', 'dangerous') - return render_template('views/uploads/choose-file.html', form=form), 400 + return invalid_upload_error('Your file has failed the virus check') if len(pdf_file_bytes) > MAX_FILE_UPLOAD_SIZE: - flash('Your file must be smaller than 2MB', 'dangerous') - return render_template('views/uploads/choose-file.html', form=form), 400 + return invalid_upload_error('Your file must be smaller than 2MB') + + try: + pdf_page_count(BytesIO(pdf_file_bytes)) + except PdfReadError: + current_app.logger.info('Invalid PDF uploaded for service_id: {}'.format(service_id)) + return invalid_upload_error('Your file must be a valid PDF') upload_id = uuid.uuid4() @@ -49,6 +62,11 @@ def upload_letter(service_id): return render_template('views/uploads/choose-file.html', form=form) +def invalid_upload_error(message): + flash(message, 'dangerous') + return render_template('views/uploads/choose-file.html', form=PDFUploadForm()), 400 + + @main.route("/services//preview-letter/") @user_has_permissions('send_messages') def uploaded_letter_preview(service_id, file_id): diff --git a/tests/app/main/views/test_uploads.py b/tests/app/main/views/test_uploads.py index 530130e74..d8daf1398 100644 --- a/tests/app/main/views/test_uploads.py +++ b/tests/app/main/views/test_uploads.py @@ -90,6 +90,20 @@ def test_post_choose_upload_file_when_file_is_too_big(mocker, client_request): assert normalize_spaces(page.select('.banner-dangerous')[0].text) == 'Your file must be smaller than 2MB' +def test_post_choose_upload_file_when_file_is_malformed(mocker, client_request): + mocker.patch('app.main.views.uploads.antivirus_client.scan', return_value=True) + + with open('tests/test_pdf_files/no_eof_marker.pdf', 'rb') as file: + page = client_request.post( + 'main.upload_letter', + service_id=SERVICE_ONE_ID, + _data={'file': file}, + _expected_status=400 + ) + assert page.find('h1').text == 'Upload a letter' + assert normalize_spaces(page.select('.banner-dangerous')[0].text) == 'Your file must be a valid PDF' + + def test_uploaded_letter_preview(client_request): page = client_request.get( 'main.uploaded_letter_preview', diff --git a/tests/test_pdf_files/no_eof_marker.pdf b/tests/test_pdf_files/no_eof_marker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..857ae1fbfbbfb6b0bd0addbcf8401cec6dbdadc8 GIT binary patch literal 14046 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 literal 0 HcmV?d00001