[SECURITY] Fix XSS injection vulnerabilities in question answers, event, quota and product names

This commit is contained in:
Raphael Michel
2017-08-20 15:30:13 +02:00
parent 3f0af67345
commit ac1467bd4b
7 changed files with 48 additions and 13 deletions

View File

@@ -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:

View 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)

View File

@@ -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">

View File

@@ -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>

View File

@@ -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',

View 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))

View File

@@ -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 %}