mirror of
https://github.com/pretix/pretix.git
synced 2025-12-05 21:32:28 +00:00
Compare commits
12 Commits
reusableme
...
v4.20.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73775786f4 | ||
|
|
8898b58be3 | ||
|
|
0376690c5a | ||
|
|
2ce5dedfcf | ||
|
|
9612e678fe | ||
|
|
213dbbb847 | ||
|
|
db0786619b | ||
|
|
91b45a8707 | ||
|
|
bff2881573 | ||
|
|
dc83b61071 | ||
|
|
a0870b1429 | ||
|
|
ff68bb9f03 |
@@ -60,7 +60,7 @@ dependencies = [
|
||||
"dnspython==2.3.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
"geoip2==4.*",
|
||||
"importlib_metadata==6.6.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"importlib_metadata==6.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"isoweek",
|
||||
"jsonschema",
|
||||
"kombu==5.2.*",
|
||||
@@ -77,6 +77,7 @@ dependencies = [
|
||||
"PyJWT==2.6.*",
|
||||
"phonenumberslite==8.13.*",
|
||||
"Pillow==9.5.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==4.23.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
|
||||
1
setup.py
1
setup.py
@@ -29,7 +29,6 @@ sys.path.append(str(Path.cwd() / 'src'))
|
||||
|
||||
|
||||
def _CustomBuild(*args, **kwargs):
|
||||
print(sys.path)
|
||||
from pretix._build import CustomBuild
|
||||
return CustomBuild(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "4.20.0"
|
||||
__version__ = "4.20.4"
|
||||
|
||||
@@ -249,3 +249,20 @@ PRETIX_PRIMARY_COLOR = '#8E44B3'
|
||||
# stressful for some cache setups so it is enabled by default and currently can't be enabled through pretix.cfg
|
||||
CACHE_LARGE_VALUES_ALLOWED = False
|
||||
CACHE_LARGE_VALUES_ALIAS = 'default'
|
||||
|
||||
# Allowed file extensions for various places plus matching Pillow formats.
|
||||
# Never allow EPS, it is full of dangerous bugs.
|
||||
FILE_UPLOAD_EXTENSIONS_IMAGE = (".png", ".jpg", ".gif", ".jpeg")
|
||||
PILLOW_FORMATS_IMAGE = ('PNG', 'GIF', 'JPEG')
|
||||
|
||||
FILE_UPLOAD_EXTENSIONS_FAVICON = (".ico", ".png", "jpg", ".gif", ".jpeg")
|
||||
|
||||
FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE = (".png", "jpg", ".gif", ".jpeg", ".bmp", ".tif", ".tiff", ".jfif")
|
||||
PILLOW_FORMATS_QUESTIONS_IMAGE = ('PNG', 'GIF', 'JPEG', 'BMP', 'TIFF')
|
||||
|
||||
FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT = (
|
||||
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
|
||||
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
|
||||
".bmp", ".tif", ".tiff"
|
||||
)
|
||||
FILE_UPLOAD_EXTENSIONS_OTHER = FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT
|
||||
|
||||
@@ -45,6 +45,10 @@ def npm_install():
|
||||
|
||||
class CustomBuild(build):
|
||||
def run(self):
|
||||
if "src" not in os.listdir(".") or "pretix" not in os.listdir("src"):
|
||||
# Only run this command on the pretix module, not on other modules even if it's registered globally
|
||||
# in some cases
|
||||
return build.run(self)
|
||||
if "PRETIX_DOCKER_BUILD" in os.environ:
|
||||
return # this is a hack to allow calling this file early in our docker build to make use of caching
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix._build_settings")
|
||||
@@ -68,6 +72,10 @@ class CustomBuild(build):
|
||||
|
||||
class CustomBuildExt(build_ext):
|
||||
def run(self):
|
||||
if "src" not in os.listdir(".") or "pretix" not in os.listdir("src"):
|
||||
# Only run this command on the pretix module, not on other modules even if it's registered globally
|
||||
# in some cases
|
||||
return build_ext.run(self)
|
||||
if "PRETIX_DOCKER_BUILD" in os.environ:
|
||||
return # this is a hack to allow calling this file early in our docker build to make use of caching
|
||||
npm_install()
|
||||
|
||||
@@ -26,6 +26,7 @@ from decimal import Decimal
|
||||
|
||||
import django_filters
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import (
|
||||
Exists, F, OuterRef, Prefetch, Q, Subquery, prefetch_related_objects,
|
||||
@@ -1181,7 +1182,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
|
||||
ftype, ignored = mimetypes.guess_type(image_file.name)
|
||||
extension = os.path.basename(image_file.name).split('.')[-1]
|
||||
else:
|
||||
img = Image.open(image_file)
|
||||
img = Image.open(image_file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
ftype = Image.MIME[img.format]
|
||||
extensions = {
|
||||
'GIF': 'gif', 'TIFF': 'tif', 'BMP': 'bmp', 'JPEG': 'jpg', 'PNG': 'png'
|
||||
|
||||
@@ -496,14 +496,14 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
|
||||
file = BytesIO(data['content'])
|
||||
|
||||
try:
|
||||
image = Image.open(file)
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
# verify() must be called immediately after the constructor.
|
||||
image.verify()
|
||||
|
||||
# We want to do more than just verify(), so we need to re-open the file
|
||||
if hasattr(file, 'seek'):
|
||||
file.seek(0)
|
||||
image = Image.open(file)
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
|
||||
# load() is a potential DoS vector (see Django bug #18520), so we verify the size first
|
||||
if image.width > 10_000 or image.height > 10_000:
|
||||
@@ -562,7 +562,7 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
|
||||
return f
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('ext_whitelist', (".png", ".jpg", ".jpeg", ".jfif", ".tif", ".tiff", ".bmp"))
|
||||
kwargs.setdefault('ext_whitelist', settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
|
||||
kwargs.setdefault('max_size', settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -822,11 +822,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
help_text=help_text,
|
||||
initial=initial.file if initial else None,
|
||||
widget=UploadedFileWidget(position=pos, event=event, answer=initial),
|
||||
ext_whitelist=(
|
||||
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
|
||||
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
|
||||
".bmp", ".tif", ".tiff"
|
||||
),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_OTHER,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
|
||||
)
|
||||
elif q.type == Question.TYPE_DATE:
|
||||
|
||||
@@ -1213,7 +1213,7 @@ class QuestionAnswer(models.Model):
|
||||
|
||||
@property
|
||||
def is_image(self):
|
||||
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.png', '.gif', '.tiff', '.bmp', '.jpeg'))
|
||||
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
|
||||
|
||||
@property
|
||||
def file_name(self):
|
||||
|
||||
@@ -521,7 +521,7 @@ def images_from_questions(sender, *args, **kwargs):
|
||||
else:
|
||||
a = op.answers.filter(question_id=question_id).first() or a
|
||||
|
||||
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tif", ".tiff")):
|
||||
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE):
|
||||
return None
|
||||
else:
|
||||
if etag:
|
||||
|
||||
@@ -2710,7 +2710,7 @@ Your {organizer} team"""))
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your event name and date '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
@@ -2753,7 +2753,7 @@ Your {organizer} team"""))
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
@@ -2793,7 +2793,7 @@ Your {organizer} team"""))
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Social media image'),
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
|
||||
'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
|
||||
@@ -2814,7 +2814,7 @@ Your {organizer} team"""))
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Logo image'),
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
required=False,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('We will show your logo with a maximal height and width of 2.5 cm.')
|
||||
|
||||
@@ -127,7 +127,7 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
|
||||
|
||||
@property
|
||||
def is_img(self):
|
||||
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif'))
|
||||
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_IMAGE)
|
||||
|
||||
def __str__(self):
|
||||
if hasattr(self.file, 'display_name'):
|
||||
|
||||
@@ -416,7 +416,7 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
|
||||
organizer_logo_image = ExtFileField(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
required=False,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
@@ -426,7 +426,7 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
)
|
||||
favicon = ExtFileField(
|
||||
label=_('Favicon'),
|
||||
ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_FAVICON,
|
||||
required=False,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_FAVICON,
|
||||
help_text=_('If you provide a favicon, we will show it instead of the default pretix icon. '
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
import logging
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from PIL.Image import MAX_IMAGE_PIXELS, DecompressionBombError
|
||||
@@ -51,7 +52,7 @@ def validate_uploaded_file_for_valid_image(f):
|
||||
|
||||
try:
|
||||
try:
|
||||
image = Image.open(file)
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
# verify() must be called immediately after the constructor.
|
||||
image.verify()
|
||||
except DecompressionBombError:
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#
|
||||
from datetime import datetime
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def monkeypatch_vobject_performance():
|
||||
"""
|
||||
@@ -52,5 +54,19 @@ def monkeypatch_vobject_performance():
|
||||
icalendar.tzinfo_eq = new_tzinfo_eq
|
||||
|
||||
|
||||
def monkeypatch_pillow_safer():
|
||||
"""
|
||||
Pillow supports many file formats, among them EPS. For EPS, Pillow loads GhostScript whenever GhostScript
|
||||
is installed (cannot officially be disabled). However, GhostScript is known for regular security vulnerabilities.
|
||||
We have no use of reading EPS files and usually prevent this by using `Image.open(…, formats=[…])` to disable EPS
|
||||
support explicitly. However, we are worried about our dependencies like reportlab using `Image.open` without the
|
||||
`formats=` parameter. Therefore, as a defense in depth approach, we monkeypatch EPS support away by modifying the
|
||||
internal image format registry of Pillow.
|
||||
"""
|
||||
if "EPS" in Image.ID:
|
||||
Image.ID.remove("EPS")
|
||||
|
||||
|
||||
def monkeypatch_all_at_ready():
|
||||
monkeypatch_vobject_performance()
|
||||
monkeypatch_pillow_safer()
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from arabic_reshaper import ArabicReshaper
|
||||
from django.conf import settings
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from PIL.Image import Resampling
|
||||
from PIL import Image
|
||||
from reportlab.lib.utils import ImageReader
|
||||
|
||||
|
||||
@@ -33,7 +34,7 @@ class ThumbnailingImageReader(ImageReader):
|
||||
height = width * self._image.size[1] / self._image.size[0]
|
||||
self._image.thumbnail(
|
||||
size=(int(width * dpi / 72), int(height * dpi / 72)),
|
||||
resample=Resampling.BICUBIC
|
||||
resample=Image.Resampling.BICUBIC
|
||||
)
|
||||
self._data = None
|
||||
return width, height
|
||||
@@ -44,6 +45,9 @@ class ThumbnailingImageReader(ImageReader):
|
||||
# (smaller) size of the modified image.
|
||||
return None
|
||||
|
||||
def _read_image(self, fp):
|
||||
return Image.open(fp, formats=settings.PILLOW_FORMATS_IMAGE)
|
||||
|
||||
|
||||
reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
|
||||
'delete_harakat': True,
|
||||
|
||||
@@ -23,6 +23,7 @@ import hashlib
|
||||
import math
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
@@ -165,7 +166,7 @@ def resize_image(image, size):
|
||||
|
||||
def create_thumbnail(sourcename, size):
|
||||
source = default_storage.open(sourcename)
|
||||
image = Image.open(BytesIO(source.read()))
|
||||
image = Image.open(BytesIO(source.read()), formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
try:
|
||||
image.load()
|
||||
except:
|
||||
|
||||
@@ -76,11 +76,7 @@ class BaseMailForm(FormPlaceholderMixin, forms.Form):
|
||||
attachment = CachedFileField(
|
||||
label=_("Attachment"),
|
||||
required=False,
|
||||
ext_whitelist=(
|
||||
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
|
||||
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
|
||||
".bmp", ".tif", ".tiff"
|
||||
),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT,
|
||||
help_text=_('Sending an attachment increases the chance of your email not arriving or being sorted into spam folders. We recommend only using PDFs '
|
||||
'of no more than 2 MB in size.'),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT
|
||||
|
||||
@@ -176,13 +176,13 @@ if SITE_URL.endswith('/'):
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).hostname]
|
||||
|
||||
TRUST_X_FORWARDED_FOR = config.get('pretix', 'trust_x_forwarded_for', fallback=False)
|
||||
USE_X_FORWARDED_HOST = config.get('pretix', 'trust_x_forwarded_host', fallback=False)
|
||||
TRUST_X_FORWARDED_FOR = config.getboolean('pretix', 'trust_x_forwarded_for', fallback=False)
|
||||
USE_X_FORWARDED_HOST = config.getboolean('pretix', 'trust_x_forwarded_host', fallback=False)
|
||||
|
||||
|
||||
REQUEST_ID_HEADER = config.get('pretix', 'request_id_header', fallback=False)
|
||||
|
||||
if config.get('pretix', 'trust_x_forwarded_proto', fallback=False):
|
||||
if config.getboolean('pretix', 'trust_x_forwarded_proto', fallback=False):
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
|
||||
@@ -694,4 +694,5 @@ FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file
|
||||
FILE_UPLOAD_MAX_SIZE_EMAIL_AUTO_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_email_auto_attachment", fallback=1)
|
||||
FILE_UPLOAD_MAX_SIZE_OTHER = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_other", fallback=10)
|
||||
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # sadly. we would prefer BigInt, and should use it for all new models but the migration will be hard
|
||||
|
||||
Reference in New Issue
Block a user