From d4638380bc454ccc7bf90cfd6a7c78c6342c7f26 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 25 Sep 2023 11:16:20 +0200 Subject: [PATCH] Add simple test, localize error messages --- filer/admin/clipboardadmin.py | 32 +++++++++++++++++++++----------- tests/test_admin.py | 23 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/filer/admin/clipboardadmin.py b/filer/admin/clipboardadmin.py index 82a17d229..1d3c12f17 100644 --- a/filer/admin/clipboardadmin.py +++ b/filer/admin/clipboardadmin.py @@ -141,25 +141,35 @@ def ajax_upload(request, folder_id=None): # the image gets attached to a folder and saved. We also # send the error msg in the JSON and also post the message # so that they know what is wrong with the image they uploaded - from django.contrib.messages import ERROR, add_message - pixels: int = max(1, file_obj.height) * max(1, file_obj.width) - + from django.contrib.messages import ERROR, WARNING, add_message + height: int = max(1, file_obj.height) + width: int = max(1, file_obj.width) + pixels: int = width * height + aspect: float = file_obj.width / file_obj.height + res_x: int = int((FILER_MAX_IMAGE_PIXELS * aspect) ** 0.5) + res_y: int = int(res_x / aspect) if pixels > 2 * FILER_MAX_IMAGE_PIXELS: - msg = ( - f"Image size ({pixels} pixels) exceeds limit of {2 * FILER_MAX_IMAGE_PIXELS} " - "pixels, could be decompression bomb DOS attack." - ) - + msg = _( + "Image size (%(pixels)d pixels) exceeds limit of %(max_pixels)d " + "pixels by a factor of two or more. Resize image to (%(width)d, " + "%(height)d) resolution or lower." + ).format(pixels=pixels, max_pixels=FILER_MAX_IMAGE_PIXELS, + width=res_x, height=res_y) message = str(msg) add_message(request, ERROR, message) return JsonResponse({'error': message}) if pixels > FILER_MAX_IMAGE_PIXELS: - msg = f"Image size ({pixels} pixels) exceeds limit of {FILER_MAX_IMAGE_PIXELS} pixels, could be decompression bomb DOS attack." + msg = _( + "Image size (%(pixels)d pixels) exceeds limit of %(max_pixels)d " + "pixels. Consider resizing image to (%(width)d, %(height)d) resolution " + "or lower." + ).format(pixels=pixels, max_pixels=FILER_MAX_IMAGE_PIXELS, + width=res_x, height=res_y) message = str(msg) - add_message(request, ERROR, message) - return JsonResponse({'error': message}) + add_message(request, WARNING, message) + # return JsonResponse({'warning': message}) file_obj.folder = folder file_obj.save() else: diff --git a/tests/test_admin.py b/tests/test_admin.py index 017c9fc89..3d39763cf 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -1,3 +1,4 @@ +import json import os import django @@ -475,6 +476,28 @@ def test_filer_ajax_upload_file(self): self.assertEqual(stored_image.original_filename, self.image_name) self.assertEqual(stored_image.mime_type, 'image/jpeg') + def test_filer_ajax_decompression_bomb(self): + from filer.admin import clipboardadmin + DEFAULT_MAX_IMAGE_PIXELS = clipboardadmin.FILER_MAX_IMAGE_PIXELS + clipboardadmin.FILER_MAX_IMAGE_PIXELS = 800 * 200 + self.assertEqual(Image.objects.count(), 0) + folder = Folder.objects.create(name='foo') + with open(self.filename, 'rb') as fh: + file_obj = django.core.files.File(fh) + url = reverse( + 'admin:filer-ajax_upload', + kwargs={'folder_id': folder.pk} + ) + '?filename=%s' % self.image_name + response = self.client.post( + url, + data=file_obj.read(), + content_type='image/jpeg', + **{'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'} + ) + self.assertEqual(Image.objects.count(), 0) + self.assertIn("error", json.loads(response.content.decode("utf-8"))) + clipboardadmin.FILER_MAX_IMAGE_PIXELS = DEFAULT_MAX_IMAGE_PIXELS + def test_filer_ajax_upload_file_using_content_type(self): self.assertEqual(Image.objects.count(), 0) folder = Folder.objects.create(name='foo')