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.
| Package | Affected versions | Patched versions |
|---|---|---|
pretixPyPI | < 2024.7.1 | 2024.7.1 |
Affected products
1Patches
10f44a2ad4e17Escape HTML in placeholder samples in mail preview (#4413)
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- github.com/advisories/GHSA-45rp-q25w-4426ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-8113ghsaADVISORY
- github.com/pretix/pretix/commit/0f44a2ad4e170882dbe6b9d95dba6c36e4e181cfghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/pretix/PYSEC-2024-180.yamlghsaWEB
- pretix.eu/about/en/blog/20240823-release-2024-7-1ghsaWEB
- pretix.eu/about/en/blog/20240823-release-2024-7-1/mitrerelease-notes
News mentions
0No linked articles in our index yet.