Unverified Commit 91dcf663 authored by Thom Wiggers's avatar Thom Wiggers 📐
Browse files

Validate paths in generate_thumbnail

parent 85ca8ea2
...@@ -36,17 +36,36 @@ def private_thumbnails(request, size_fit, path): ...@@ -36,17 +36,36 @@ def private_thumbnails(request, size_fit, path):
def generate_thumbnail(request, size_fit, path, thumbpath): def generate_thumbnail(request, size_fit, path, thumbpath):
"""The thumbnails are generated with this route. Because the """
Generate thumbnail and redirect user to new location
The thumbnails are generated with this route. Because the
thumbnails will be generated in parallel, it will not block thumbnails will be generated in parallel, it will not block
page load when many thumbnails need to be generated. page load when many thumbnails need to be generated.
After it is done, the user is redirected to the new location After it is done, the user is redirected to the new location
of the thumbnail.""" of the thumbnail."""
thumbpath = urlunquote(thumbpath) thumbpath = urlunquote(thumbpath)
path = urlunquote(path) path = urlunquote(path)
full_thumbpath = os.path.join(settings.MEDIA_ROOT, thumbpath) full_thumbpath = os.path.normpath(
full_path = os.path.join(settings.MEDIA_ROOT, path) os.path.join(settings.MEDIA_ROOT, thumbpath))
full_path = os.path.normpath(os.path.join(settings.MEDIA_ROOT, path))
size, fit = size_fit.split('_') size, fit = size_fit.split('_')
public_media = os.path.join(settings.MEDIA_ROOT, 'public')
thumb_root = os.path.join(settings.MEDIA_ROOT, 'thumbnails', size_fit)
public_img = False
if (os.path.commonprefix([full_thumbpath, full_path])
.startswith(public_media)):
public_img = True
elif not (os.path.commonprefix([full_thumbpath, thumb_root])
.startswith(thumb_root) and
os.path.commonprefix([full_path, settings.MEDIA_ROOT])
.startswith(settings.MEDIA_ROOT)):
raise SuspiciousFileOperation(
"Path traversal detected: someone tried to generate a thumb from "
"{} to {}".format(full_path, full_thumbpath))
os.makedirs(os.path.dirname(full_thumbpath), exist_ok=True) os.makedirs(os.path.dirname(full_thumbpath), exist_ok=True)
if (not os.path.isfile(full_thumbpath) or if (not os.path.isfile(full_thumbpath) or
os.path.getmtime(full_path) > os.path.getmtime(full_thumbpath)): os.path.getmtime(full_path) > os.path.getmtime(full_thumbpath)):
...@@ -58,12 +77,11 @@ def generate_thumbnail(request, size_fit, path, thumbpath): ...@@ -58,12 +77,11 @@ def generate_thumbnail(request, size_fit, path, thumbpath):
thumb = ImageOps.fit(image, size, Image.ANTIALIAS) thumb = ImageOps.fit(image, size, Image.ANTIALIAS)
thumb.save(full_thumbpath) thumb.save(full_thumbpath)
# We can instantly redirect to the new correct image url # We can instantly redirect to the new correct image url if public
if path.split('/')[0] == 'public': if public_img:
return redirect(settings.MEDIA_URL + thumbpath, permanent=True) return redirect(settings.MEDIA_URL + thumbpath, permanent=True)
# Otherwise redirect to the route with an auth check # Otherwise redirect to the route with an auth check
return redirect( return redirect(
reverse('private-thumbnails', args=[size_fit, path]), reverse('private-thumbnails', args=[size_fit, path]),
permanent=True permanent=True)
)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment