VYPR
High severityNVD Advisory· Published Aug 23, 2024· Updated Aug 30, 2024

Stored XSS in Placeholder Samples in Mail Preview

CVE-2024-8113

Description

Stored XSS in organizer and event settings of pretix up to 2024.7.0 allows malicious event organizers to inject HTML tags into e-mail previews on settings page. The default Content Security Policy of pretix prevents execution of attacker-provided scripts, making exploitation unlikely. However, combined with a CSP bypass (which is not currently known) the vulnerability could be used to impersonate other organizers or staff users.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
pretixPyPI
< 2024.7.12024.7.1

Affected products

1

Patches

1
0f44a2ad4e17

Escape HTML in placeholder samples in mail preview (#4413)

https://github.com/pretix/pretixMiraAug 23, 2024via ghsa
4 files changed · +11 8
  • src/pretix/base/forms/widgets.py+2 1 modified
    @@ -38,6 +38,7 @@
     from django import forms
     from django.utils.formats import get_format
     from django.utils.functional import lazy
    +from django.utils.html import escape
     from django.utils.timezone import get_current_timezone, now
     from django.utils.translation import gettext_lazy as _
     
    @@ -64,7 +65,7 @@ def format_placeholders_help_text(placeholders, event=None):
         placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
         placeholders.sort(key=lambda x: x[0])
         phs = [
    -        '<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (_("Sample: %s") % v if v else "", k)
    +        '<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k))
             for k, v in placeholders
         ]
         return _('Available placeholders: {list}').format(
    
  • src/pretix/control/views/event.py+3 2 modified
    @@ -62,6 +62,7 @@
     from django.shortcuts import get_object_or_404, redirect
     from django.urls import reverse
     from django.utils.functional import cached_property
    +from django.utils.html import escape
     from django.utils.http import url_has_allowed_host_and_scheme
     from django.utils.timezone import now
     from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
    @@ -726,7 +727,7 @@ def placeholders(self, item):
                 else:
                     ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
                         _('This value will be replaced based on dynamic parameters.'),
    -                    s
    +                    escape(s)
                     )
             return ctx
     
    @@ -776,7 +777,7 @@ def post(self, request, *args, **kwargs):
         def placeholders(self, item):
             ctx = {}
             for p in get_available_placeholders(self.request.event, MailSettingsForm.base_context[item]).values():
    -            ctx[p.identifier] = str(p.render_sample(self.request.event))
    +            ctx[p.identifier] = escape(str(p.render_sample(self.request.event)))
             return ctx
     
         def get(self, request, *args, **kwargs):
    
  • src/pretix/control/views/vouchers.py+2 2 modified
    @@ -50,7 +50,7 @@
     from django.shortcuts import redirect, render
     from django.urls import resolve, reverse
     from django.utils.functional import cached_property
    -from django.utils.html import format_html
    +from django.utils.html import format_html, escape
     from django.utils.safestring import mark_safe
     from django.utils.timezone import now
     from django.utils.translation import gettext_lazy as _
    @@ -562,7 +562,7 @@ def placeholders(self, item):
                 else:
                     ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
                         _('This value will be replaced based on dynamic parameters.'),
    -                    s
    +                    escape(s)
                     )
             return self.SafeDict(ctx)
     
    
  • src/pretix/plugins/sendmail/views.py+4 3 modified
    @@ -46,6 +46,7 @@
     from django.template.loader import get_template
     from django.urls import reverse
     from django.utils.functional import cached_property
    +from django.utils.html import escape
     from django.utils.timezone import now
     from django.utils.translation import gettext_lazy as _, ngettext
     from django.views.generic import DeleteView, FormView, ListView, TemplateView
    @@ -193,7 +194,7 @@ def form_valid(self, form):
                         for k, v in get_available_placeholders(self.request.event, self.context_parameters).items():
                             context_dict[k] = '<span class="placeholder" title="{}">{}</span>'.format(
                                 _('This value will be replaced based on dynamic parameters.'),
    -                            v.render_sample(self.request.event)
    +                            escape(v.render_sample(self.request.event))
                             )
     
                         subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[])
    @@ -608,7 +609,7 @@ def form_valid(self, form):
                                                                                     'position_or_address']).items():
                             context_dict[k] = '<span class="placeholder" title="{}">{}</span>'.format(
                                 _('This value will be replaced based on dynamic parameters.'),
    -                            v.render_sample(self.request.event)
    +                            escape(v.render_sample(self.request.event))
                             )
     
                         subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[])
    @@ -684,7 +685,7 @@ def get_context_data(self, **kwargs):
                     for k, v in get_available_placeholders(self.request.event, ['event', 'order', 'position_or_address']).items():
                         placeholders[k] = '<span class="placeholder" title="{}">{}</span>'.format(
                             _('This value will be replaced based on dynamic parameters.'),
    -                        v.render_sample(self.request.event)
    +                        escape(v.render_sample(self.request.event))
                         )
     
                     subject = bleach.clean(self.object.subject.localize(lang), tags=[])
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.