Skip to content

Commit 9d11634

Browse files
authored
Merge pull request #432 from ynput/417-validate-thumbnails-upon-uploading
Fix: Handle invalid thumbnails
2 parents 48165eb + d58180e commit 9d11634

File tree

2 files changed

+47
-36
lines changed

2 files changed

+47
-36
lines changed

api/thumbnails/thumbnails.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
NotFoundException,
2727
)
2828
from ayon_server.files import Storages
29+
from ayon_server.helpers.mimetypes import guess_mime_type
2930
from ayon_server.helpers.thumbnails import (
3031
ThumbnailProcessNoop,
3132
get_fake_thumbnail,
@@ -83,12 +84,17 @@ async def store_thumbnail(
8384
MAX_THUMBNAIL_WIDTH = 600
8485
MAX_THUMBNAIL_HEIGHT = 600
8586

87+
if guess_mime_type(payload) != mime:
88+
raise BadRequestException("Mime type does not match the payload")
89+
8690
try:
8791
thumbnail = await process_thumbnail(
8892
payload,
8993
(MAX_THUMBNAIL_WIDTH, MAX_THUMBNAIL_HEIGHT),
9094
raise_on_noop=True,
9195
)
96+
except ValueError as e:
97+
raise BadRequestException(str(e))
9298
except ThumbnailProcessNoop:
9399
thumbnail = payload
94100
else:

ayon_server/helpers/thumbnails.py

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import io
44

55
from nxtools import logging
6-
from PIL import Image
6+
from PIL import Image, UnidentifiedImageError
77
from starlette.concurrency import run_in_threadpool
88

99

@@ -87,41 +87,46 @@ async def process_thumbnail(
8787
"""
8888

8989
def process_image():
90-
with Image.open(io.BytesIO(image_bytes)) as img:
91-
target_format = format or img.format or "JPEG"
92-
93-
# Ensure that we have valid dimensions
94-
if size == (None, None):
95-
raise ValueError("Both width and height cannot be None")
96-
97-
original_width, original_height = img.size
98-
99-
new_width, new_height = calculate_scaled_size(
100-
original_width, original_height, *size
101-
)
102-
103-
if new_width >= original_width or new_height >= original_height:
104-
# If the requested size is larger than the original image,
105-
# return the original image
106-
if raise_on_noop:
107-
raise ThumbnailProcessNoop()
108-
return image_bytes
109-
110-
logging.debug(
111-
f"Resizing image from {img.size} to {(new_width, new_height)}"
112-
)
113-
img = img.resize((new_width, new_height), Image.LANCZOS) # type: ignore
114-
img_byte_arr = io.BytesIO()
115-
116-
# Adjustments for specific formats
117-
if target_format == "JPEG":
118-
if img.mode != "RGB":
119-
img = img.convert("RGB")
120-
img.save(img_byte_arr, format=target_format, optimize=True, quality=85)
121-
else:
122-
img.save(img_byte_arr, format=target_format)
123-
124-
return img_byte_arr.getvalue()
90+
try:
91+
with Image.open(io.BytesIO(image_bytes)) as img:
92+
target_format = format or img.format or "JPEG"
93+
94+
# Ensure that we have valid dimensions
95+
if size == (None, None):
96+
raise ValueError("Both width and height cannot be None")
97+
98+
original_width, original_height = img.size
99+
100+
new_width, new_height = calculate_scaled_size(
101+
original_width, original_height, *size
102+
)
103+
104+
if new_width >= original_width or new_height >= original_height:
105+
# If the requested size is larger than the original image,
106+
# return the original image
107+
if raise_on_noop:
108+
raise ThumbnailProcessNoop()
109+
return image_bytes
110+
111+
logging.debug(
112+
f"Resizing image from {img.size} to {(new_width, new_height)}"
113+
)
114+
img = img.resize((new_width, new_height), Image.LANCZOS) # type: ignore
115+
img_byte_arr = io.BytesIO()
116+
117+
# Adjustments for specific formats
118+
if target_format == "JPEG":
119+
if img.mode != "RGB":
120+
img = img.convert("RGB")
121+
img.save(
122+
img_byte_arr, format=target_format, optimize=True, quality=85
123+
)
124+
else:
125+
img.save(img_byte_arr, format=target_format)
126+
127+
return img_byte_arr.getvalue()
128+
except UnidentifiedImageError:
129+
raise ValueError("Invalid image format")
125130

126131
# Run the blocking image processing in a separate thread
127132
normalized_bytes = await run_in_threadpool(process_image)

0 commit comments

Comments
 (0)