From 4fc571379211d6679a2f3a024e1bf7b18fd4dc7d Mon Sep 17 00:00:00 2001 From: Bianca Severino Date: Wed, 31 Mar 2021 13:14:36 -0400 Subject: [PATCH] fix: validate the media type of uploaded IDV images --- .../verify_student/tests/test_views.py | 18 +++++++++++++- lms/djangoapps/verify_student/views.py | 24 ++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 5bf61eb518..ea361dff50 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -1230,7 +1230,7 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): """ USERNAME = "test_user" PASSWORD = "test_password" - IMAGE_DATA = "abcd,1234" + IMAGE_DATA = "data:image/png;base64,1234" FULL_NAME = "Ḟüḷḷ Ṅäṁë" EXPERIMENT_NAME = "test-experiment" @@ -1338,6 +1338,22 @@ class TestSubmitPhotosForVerification(MockS3BotoMixin, TestVerificationBase): response = self._submit_photos(expected_status_code=400, **params) assert response.content.decode('utf-8') == 'Image data is not valid.' + @ddt.data( + ('data:image/png;base64,1234', 200), + ('data:image/jpeg;base64,1234', 200), + ('data:image/webp;base64,1234', 200), + ('data:application/pdf;base64,1234', 400), + ('data:text/html;base64,1234', 400), + ('invalid_image_data', 400), + ) + @ddt.unpack + def test_validate_media_type(self, image_data, status_code): + params = { + 'face_image': image_data, + 'photo_id_image': image_data, + } + self._submit_photos(expected_status_code=status_code, **params) + def test_invalid_name(self): response = self._submit_photos( face_image=self.IMAGE_DATA, diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index cf45ccd5e6..4e0b9a0dca 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -852,7 +852,7 @@ class SubmitPhotosView(View): # Retrieve the image data # Validation ensures that we'll have a face image, but we may not have # a photo ID image if this is a re-verification. - face_image, photo_id_image, response = self._decode_image_data( + face_image, photo_id_image, response = self._validate_and_decode_image_data( request, params["face_image"], params.get("photo_id_image") ) @@ -957,9 +957,9 @@ class SubmitPhotosView(View): ) return HttpResponseBadRequest(msg) - def _decode_image_data(self, request, face_data, photo_id_data=None): + def _validate_and_decode_image_data(self, request, face_data, photo_id_data=None): """ - Decode image data sent with the request. + Validate and decode image data sent with the request. Arguments: face_data (str): base64-encoded face image data. @@ -971,6 +971,24 @@ class SubmitPhotosView(View): tuple of (str, str, HttpResponse) """ + for image_data in [face_data, photo_id_data]: + # Validate that the media type is image + if image_data and not image_data.startswith('data:image'): + msg = _("Image data is in an unsupported format.") + data_type = image_data.split(',')[0] + if data_type: + log.error( + "Image data for user_id={user_id} was uploaded in an unsupported " + "format: {data_type}".format(user_id=request.user.id, data_type=data_type) + ) + else: + log.error( + "Image data type for user_id={user_id} could not be identified.".format( + user_id=request.user.id + ) + ) + return None, None, HttpResponseBadRequest(msg) + try: # Decode face image data (used for both an initial and re-verification) face_image = decode_image_data(face_data)