mirror of
https://github.com/pretix/pretix.git
synced 2026-05-17 17:14:04 +00:00
[SECURITY] Fix XSS injection vulnerabilities in question answers, event, quota and product names
This commit is contained in:
@@ -5,6 +5,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'code': co.code
|
'code': co.code
|
||||||
}),
|
}),
|
||||||
'val': co.code,
|
'val': escape(co.code),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Voucher):
|
elif isinstance(co, Voucher):
|
||||||
a_text = _('Voucher {val}…')
|
a_text = _('Voucher {val}…')
|
||||||
@@ -76,7 +77,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'voucher': co.id
|
'voucher': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.code[:6],
|
'val': escape(co.code[:6]),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Item):
|
elif isinstance(co, Item):
|
||||||
a_text = _('Product {val}')
|
a_text = _('Product {val}')
|
||||||
@@ -86,7 +87,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'item': co.id
|
'item': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Quota):
|
elif isinstance(co, Quota):
|
||||||
a_text = _('Quota {val}')
|
a_text = _('Quota {val}')
|
||||||
@@ -96,7 +97,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'quota': co.id
|
'quota': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, ItemCategory):
|
elif isinstance(co, ItemCategory):
|
||||||
a_text = _('Category {val}')
|
a_text = _('Category {val}')
|
||||||
@@ -106,7 +107,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'category': co.id
|
'category': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.name,
|
'val': escape(co.name),
|
||||||
}
|
}
|
||||||
elif isinstance(co, Question):
|
elif isinstance(co, Question):
|
||||||
a_text = _('Question {val}')
|
a_text = _('Question {val}')
|
||||||
@@ -116,7 +117,7 @@ class LogEntry(models.Model):
|
|||||||
'organizer': self.event.organizer.slug,
|
'organizer': self.event.organizer.slug,
|
||||||
'question': co.id
|
'question': co.id
|
||||||
}),
|
}),
|
||||||
'val': co.question,
|
'val': escape(co.question),
|
||||||
}
|
}
|
||||||
|
|
||||||
if a_text and a_map:
|
if a_text and a_map:
|
||||||
|
|||||||
13
src/pretix/base/templatetags/escapejson.py
Normal file
13
src/pretix/base/templatetags/escapejson.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from django import template
|
||||||
|
from django.template.defaultfilters import stringfilter
|
||||||
|
|
||||||
|
from pretix.helpers.escapejson import escapejson
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter("escapejson")
|
||||||
|
@stringfilter
|
||||||
|
def escapejs_filter(value):
|
||||||
|
"""Hex encodes characters for use in a application/json type script."""
|
||||||
|
return escapejson(value)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "pretixcontrol/items/base.html" %}
|
{% extends "pretixcontrol/items/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load escapejson %}
|
||||||
{% load formset_tags %}
|
{% load formset_tags %}
|
||||||
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
|
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
<div class="chart" id="question_chart" data-type="{{ question.type }}">
|
<div class="chart" id="question_chart" data-type="{{ question.type }}">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="question-chart-data">{{ stats_json|safe }}</script>
|
<script type="application/json" id="question-chart-data">{{ stats_json|escapejson }}</script>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5 col-xs-12">
|
<div class="col-md-5 col-xs-12">
|
||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends "pretixcontrol/items/base.html" %}
|
{% extends "pretixcontrol/items/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load escapejson %}
|
||||||
{% load eventsignal %}
|
{% load eventsignal %}
|
||||||
{% block title %}{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}{% endblock %}
|
{% block title %}{% blocktrans with name=quota.name %}Quota: {{ name }}{% endblocktrans %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
<div class="chart" id="quota_chart">
|
<div class="chart" id="quota_chart">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="quota-chart-data">{{ quota_chart_data|safe }}</script>
|
<script type="application/json" id="quota-chart-data">{{ quota_chart_data|escapejson }}</script>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5 col-xs-12">
|
<div class="col-md-5 col-xs-12">
|
||||||
<legend>{% trans "Availability calculation" %}</legend>
|
<legend>{% trans "Availability calculation" %}</legend>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from django.shortcuts import render
|
|||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.formats import date_format
|
from django.utils.formats import date_format
|
||||||
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
@@ -138,7 +139,7 @@ def quota_widgets(sender, **kwargs):
|
|||||||
status, left = q.availability()
|
status, left = q.availability()
|
||||||
widgets.append({
|
widgets.append({
|
||||||
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
'content': NUM_WIDGET.format(num='{}/{}'.format(left, q.size) if q.size is not None else '\u221e',
|
||||||
text=_('{quota} left').format(quota=q.name)),
|
text=_('{quota} left').format(quota=escape(q.name))),
|
||||||
'display_size': 'small',
|
'display_size': 'small',
|
||||||
'priority': 50,
|
'priority': 50,
|
||||||
'url': reverse('control:event.items.quotas.show', kwargs={
|
'url': reverse('control:event.items.quotas.show', kwargs={
|
||||||
@@ -269,7 +270,8 @@ def user_event_widgets(**kwargs):
|
|||||||
for event in events:
|
for event in events:
|
||||||
widgets.append({
|
widgets.append({
|
||||||
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
'content': '<div class="event">{event}<span class="from">{df}</span><span class="to">{dt}</span></div>'.format(
|
||||||
event=event.name, df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
event=escape(event.name),
|
||||||
|
df=date_format(event.date_from, 'SHORT_DATE_FORMAT') if event.date_from else '',
|
||||||
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
dt=date_format(event.date_to, 'SHORT_DATE_FORMAT') if event.date_to else ''
|
||||||
),
|
),
|
||||||
'display_size': 'small',
|
'display_size': 'small',
|
||||||
|
|||||||
16
src/pretix/helpers/escapejson.py
Normal file
16
src/pretix/helpers/escapejson.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.functional import keep_lazy
|
||||||
|
from django.utils.safestring import SafeText, mark_safe
|
||||||
|
|
||||||
|
_json_escapes = {
|
||||||
|
ord('>'): '\\u003E',
|
||||||
|
ord('<'): '\\u003C',
|
||||||
|
ord('&'): '\\u0026',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keep_lazy(six.text_type, SafeText)
|
||||||
|
def escapejson(value):
|
||||||
|
"""Hex encodes characters for use in a application/json type script."""
|
||||||
|
return mark_safe(force_text(value).translate(_json_escapes))
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load compress %}
|
{% load compress %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
{% load escapejson %}
|
||||||
{% block title %}{% trans "Statistics" %}{% endblock %}
|
{% block title %}{% trans "Statistics" %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "Statistics" %}</h1>
|
<h1>{% trans "Statistics" %}</h1>
|
||||||
@@ -30,9 +31,9 @@
|
|||||||
<div id="obp_chart" class="chart"></div>
|
<div id="obp_chart" class="chart"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="application/json" id="obd-data">{{ obd_data|safe }}</script>
|
<script type="application/json" id="obd-data">{{ obd_data|escapejson }}</script>
|
||||||
<script type="application/json" id="rev-data">{{ rev_data|safe }}</script>
|
<script type="application/json" id="rev-data">{{ rev_data|escapejson }}</script>
|
||||||
<script type="application/json" id="obp-data">{{ obp_data|safe }}</script>
|
<script type="application/json" id="obp-data">{{ obp_data|escapejson }}</script>
|
||||||
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
<script type="application/text" id="currency">{{ request.event.currency }}</script>
|
||||||
<script type="application/javascript" src="{% static "pretixplugins/statistics/statistics.js" %}"></script>
|
<script type="application/javascript" src="{% static "pretixplugins/statistics/statistics.js" %}"></script>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
Reference in New Issue
Block a user