[extensions-web/wip/ne0sight] extensions: restrict accepted images types



commit 99f792643f927e78658d671d5fd8645af2d09fd8
Author: Yuri Konotopov <ykonotopov gnome org>
Date:   Fri May 29 20:16:38 2020 +0400

    extensions: restrict accepted images types

 sweettooth/extensions/form_fields.py | 33 +++++++++++++++++++++++++++++++++
 sweettooth/extensions/forms.py       | 21 ++++++++++++++++++++-
 sweettooth/extensions/models.py      | 25 +++++++++++++++++++------
 sweettooth/extensions/views.py       | 19 ++++++++++++++++---
 4 files changed, 88 insertions(+), 10 deletions(-)
---
diff --git a/sweettooth/extensions/form_fields.py b/sweettooth/extensions/form_fields.py
new file mode 100644
index 0000000..3d21edc
--- /dev/null
+++ b/sweettooth/extensions/form_fields.py
@@ -0,0 +1,33 @@
+"""
+    GNOME Shell Extensions Repository
+    Copyright (C) 2020 Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
+
+from django.core.exceptions import ValidationError
+from django.forms.fields import ImageField
+
+class RestrictedImageField(ImageField):
+    def __init__(self, *, allowed_types=["gif", "jpeg", "png", "webp"], **kwargs):
+        self.allowed_types = allowed_types
+        super().__init__(**kwargs)
+
+    def to_python(self, data):
+        f = super().to_python(data)
+        if f is None or f.image is None:
+            return None
+
+        if f.image.format is None or not f.image.format.lower() in self.allowed_types:
+            raise ValidationError(
+                self.error_messages['invalid_image'],
+                code='invalid_image_type',
+            )
+
+        if hasattr(f, 'seek') and callable(f.seek):
+            f.seek(0)
+
+        return f
diff --git a/sweettooth/extensions/forms.py b/sweettooth/extensions/forms.py
index 67be262..fec95b0 100644
--- a/sweettooth/extensions/forms.py
+++ b/sweettooth/extensions/forms.py
@@ -1,5 +1,18 @@
+"""
+    GNOME Shell Extensions Repository
+    Copyright (C) 2011 Jasper St. Pierre <jstpierre mecheye net>
+    Copyright (C) 2020 Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
 
 from django import forms
+from django.core.validators import FileExtensionValidator
+
+from .form_fields import RestrictedImageField
 
 class UploadForm(forms.Form):
     source = forms.FileField(required=True)
@@ -23,5 +36,11 @@ I agree that GNOME Shell Extensions can remove, modify or reassign maintainershi
             raise forms.ValidationError("You must agree to the GNOME Shell Extensions terms of service.")
         return tos_compliant
 
+
 class ImageUploadForm(forms.Form):
-    file = forms.ImageField(required=True)
+    allowed_types = ["gif", "jpg", "jpeg", "png", "webp"]
+    file = RestrictedImageField(
+        required=True,
+        allowed_types=allowed_types,
+        validators=[FileExtensionValidator(allowed_types)]
+    )
diff --git a/sweettooth/extensions/models.py b/sweettooth/extensions/models.py
index ca09fb9..432350b 100644
--- a/sweettooth/extensions/models.py
+++ b/sweettooth/extensions/models.py
@@ -1,5 +1,20 @@
+"""
+    GNOME Shell Extensions Repository
+    Copyright (C) 2011-2013 Jasper St. Pierre <jstpierre mecheye net>
+    Copyright (C) 2019 Claude Paroz <claude 2xlibre net>
+    Copyright (C) 2016-2020 Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
 
+import autoslug
 import json
+import os
+import re
+import zlib
 
 from zipfile import ZipFile, BadZipfile
 
@@ -8,10 +23,6 @@ from django.db import models
 from django.dispatch import Signal
 from django.urls import reverse
 
-import autoslug
-import re
-import zlib
-
 (STATUS_UNREVIEWED,
  STATUS_REJECTED,
  STATUS_INACTIVE,
@@ -82,11 +93,13 @@ def build_shell_version_array(versions):
 
 
 def make_screenshot_filename(obj, filename=None):
-    return "screenshots/screenshot_%d.png" % (obj.pk,)
+    ext = os.path.splitext(filename)[1].lower()
+    return "screenshots/screenshot_%d%s" % (obj.pk, ext)
 
 
 def make_icon_filename(obj, filename=None):
-    return "icons/icon_%d.png" % (obj.pk,)
+    ext = os.path.splitext(filename)[1].lower()
+    return "icons/icon_%d%s" % (obj.pk, ext)
 
 
 class Extension(models.Model):
diff --git a/sweettooth/extensions/views.py b/sweettooth/extensions/views.py
index 0cc9eb6..da165e5 100644
--- a/sweettooth/extensions/views.py
+++ b/sweettooth/extensions/views.py
@@ -1,3 +1,13 @@
+"""
+    GNOME Shell Extensions Repository
+    Copyright (C) 2011-2016 Jasper St. Pierre <jstpierre mecheye net>
+    Copyright (C) 2016-2020 Yuri Konotopov <ykonotopov gnome org>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+"""
 
 import json
 from math import ceil
@@ -7,7 +17,7 @@ from django.core.paginator import Paginator, InvalidPage
 from django.contrib.auth.decorators import login_required
 from django.contrib import messages
 from django.db import transaction
-from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, 
HttpResponseServerError, Http404
+from django.http import JsonResponse, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, 
HttpResponseServerError, Http404
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template.loader import render_to_string
 from django.views.decorators.http import require_POST
@@ -348,8 +358,11 @@ def validate_uploaded_image(request, extension):
 
     form = ImageUploadForm(request.POST, request.FILES)
 
-    if not form.is_valid() or form.cleaned_data['file'].size > 2*1024*1024:
-        return HttpResponseForbidden()
+    if not form.is_valid():
+        return JsonResponse(form.errors.get_json_data(), status=403)
+
+    if form.cleaned_data['file'].size > 2*1024*1024:
+        return HttpResponseForbidden(content="Too big image")
 
     return form.cleaned_data['file']
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]