Skip to content

Commit b55faa6

Browse files
committed
- Test file type checks
- Test file size checks
1 parent 69f0742 commit b55faa6

File tree

4 files changed

+49
-2
lines changed

4 files changed

+49
-2
lines changed

kirovy/constants/api_codes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ class FileUploadApiCodes(enum.StrEnum):
3131
3232
e.g. a temporary map does not support custom image uploads.
3333
"""
34+
TOO_LARGE = "file-too-large"

kirovy/views/base_views.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from kirovy.request import KirovyRequest
2929
from kirovy.response import KirovyResponse
3030
from kirovy.serializers import KirovySerializer, CncNetUserOwnedModelSerializer
31-
31+
from kirovy.utils import file_utils
3232

3333
_LOGGER = logging.get_logger(__name__)
3434

@@ -176,6 +176,8 @@ class FileUploadBaseView(APIView, metaclass=ABCMeta):
176176

177177
serializer_class: t.ClassVar[t.Type[CncNetUserOwnedModelSerializer]]
178178

179+
max_file_size: t.ClassVar[file_utils.ByteSized] = file_utils.ByteSized(mega=3)
180+
179181
def get_parent_object(self, request: KirovyRequest) -> GameScopedUserOwnedModel:
180182

181183
parent_object_id = request.data.get(self.file_parent_attr_name)
@@ -193,6 +195,14 @@ def get_parent_object(self, request: KirovyRequest) -> GameScopedUserOwnedModel:
193195

194196
def post(self, request: KirovyRequest, format=None) -> KirovyResponse[ui_objects.ResultResponseData]:
195197
uploaded_file: UploadedFile = request.data["file"]
198+
199+
if file_utils.ByteSized(uploaded_file.size) > self.max_file_size:
200+
raise KirovyValidationError(
201+
"File too large",
202+
code=api_codes.FileUploadApiCodes.TOO_LARGE,
203+
additional={"max_size": str(self.max_file_size)},
204+
)
205+
196206
parent_object = self.get_parent_object(request)
197207
self.extra_verification(request, uploaded_file, parent_object)
198208

kirovy/views/cnc_map_views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
CncMap,
2424
CncMapFile,
2525
)
26-
from kirovy.models.cnc_game import GameScopedUserOwnedModel
2726
from kirovy.models.cnc_map import CncMapImageFile
2827
from kirovy.objects import ui_objects
2928
from kirovy.request import KirovyRequest
@@ -341,6 +340,8 @@ def extra_serializer_data(
341340
height = image.height
342341
width = image.width
343342
except (DecompressionBombError, UnidentifiedImageError) as e:
343+
# This should be in verification, but we need the width and height,
344+
# and loading the image twice is inefficient.
344345
_LOGGER.warning(
345346
"user-attempted-bad-image-upload",
346347
{"user_id": request.user.id, "username": request.user.username, "e": str(e)},

tests/test_views/test_map_upload.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,38 @@ def test_map_image_upload__map_is_banned(create_cnc_map, file_map_image, client_
222222
assert response.status_code == status.HTTP_403_FORBIDDEN
223223

224224
assert cnc_map.cncmapimagefile_set.select_related().count() == original_image_count
225+
226+
227+
def test_map_image_upload__not_an_image(create_cnc_map, file_map_desert, client_user):
228+
"""Test that map image uploads fail for non-image files."""
229+
cnc_map = create_cnc_map(user_id=client_user.kirovy_user.id)
230+
original_image_count = cnc_map.cncmapimagefile_set.select_related().count()
231+
response = client_user.post(
232+
"/maps/img/",
233+
{"file": file_map_desert, "cnc_map_id": str(cnc_map.id)},
234+
format="multipart",
235+
content_type=None,
236+
)
237+
238+
assert response.status_code == status.HTTP_400_BAD_REQUEST
239+
assert response.data["code"] == UploadApiCodes.FILE_EXTENSION_NOT_SUPPORTED
240+
assert cnc_map.cncmapimagefile_set.select_related().count() == original_image_count
241+
242+
243+
def test_map_image_upload__too_large(create_cnc_map, file_binary, client_user):
244+
"""Test that map image uploads fail for files that are too large.
245+
246+
This test works because the file size check happens before the extension check
247+
"""
248+
cnc_map = create_cnc_map(user_id=client_user.kirovy_user.id)
249+
original_image_count = cnc_map.cncmapimagefile_set.select_related().count()
250+
response = client_user.post(
251+
"/maps/img/",
252+
{"file": file_binary, "cnc_map_id": str(cnc_map.id)},
253+
format="multipart",
254+
content_type=None,
255+
)
256+
257+
assert response.status_code == status.HTTP_400_BAD_REQUEST
258+
assert response.data["code"] == FileUploadApiCodes.TOO_LARGE
259+
assert cnc_map.cncmapimagefile_set.select_related().count() == original_image_count

0 commit comments

Comments
 (0)