diff --git a/src/pretix/base/templatetags/rich_text.py b/src/pretix/base/templatetags/rich_text.py index 7995e879c..2d4b060e6 100644 --- a/src/pretix/base/templatetags/rich_text.py +++ b/src/pretix/base/templatetags/rich_text.py @@ -1,6 +1,12 @@ +import urllib.parse + import bleach import markdown +from bleach import DEFAULT_CALLBACKS from django import template +from django.core import signing +from django.urls import reverse +from django.utils.http import is_safe_url from django.utils.safestring import mark_safe register = template.Library() @@ -48,6 +54,15 @@ ALLOWED_ATTRIBUTES = { } +def safelink_callback(attrs, new=False): + url = attrs.get((None, 'href'), '/') + if not is_safe_url(url): + signer = signing.Signer(salt='safe-redirect') + attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url)) + attrs[None, 'target'] = '_blank' + return attrs + + @register.filter def rich_text(text: str, **kwargs): """ @@ -58,5 +73,5 @@ def rich_text(text: str, **kwargs): markdown.markdown(text), tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, - )) + ), callbacks=DEFAULT_CALLBACKS + [safelink_callback]) return mark_safe(body_md) diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py index 5f5463cfd..bb0996fdf 100644 --- a/src/tests/presale/test_event.py +++ b/src/tests/presale/test_event.py @@ -68,6 +68,18 @@ class EventMiddlewareTest(EventTestMixin, SoupTest): class ItemDisplayTest(EventTestMixin, SoupTest): + def test_link_rewrite(self): + q = Quota.objects.create(event=self.event, name='Quota', size=2) + item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True, + description="http://example.org [Sample](http://example.net)") + q.items.add(item) + html = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug)).rendered_content + + self.assertNotIn('href="http://example.org', html) + self.assertNotIn('href="http://example.net', html) + self.assertIn('href="/redirect/?url=http%3A//example.org%3A', html) + self.assertIn('href="/redirect/?url=http%3A//example.net%3A', html) + def test_not_active(self): q = Quota.objects.create(event=self.event, name='Quota', size=2) item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=False)