Handle safe types centrally

This commit is contained in:
Raphael Michel
2026-07-10 12:17:44 +02:00
parent 899ff4821e
commit df2e82e707
5 changed files with 21 additions and 7 deletions
+21
View File
@@ -266,6 +266,22 @@ def _merge_csp(a, b):
class SecurityMiddleware(MiddlewareMixin):
SAFE_TYPES = (
# CSP policies are only used for:
# - HTML and SVG in top-level contexts
# - SVG or JS Workers delivered in embedded contexts
# See: https://www.w3.org/TR/CSP2/#which-policy-applies
# Therefore, we can save bandwidth on not including our (sometimes huge) policy
# on API responses or CSS. We do however include it with other types as a precaution
# (whitelist instead of blacklist) and we also do not whitelist JavaScript in
# we ever add service workers to not break the protection of this feature:
# https://www.w3.org/TR/CSP2/#sandboxing-and-workers
'application/json',
'text/css',
# We used to skip CSP for PDF since it was necessary for inline previews in Safari,
# but at the moment it does not seem to be an issue to just send it.
)
def process_response(self, request, resp):
if settings.DEBUG and resp.status_code >= 400:
# Don't use CSP on debug error page as it breaks of Django's fancy error
@@ -277,6 +293,11 @@ class SecurityMiddleware(MiddlewareMixin):
# https://github.com/pretix/pretix/issues/765
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
if "Content-Type" in resp and resp["Content-Type"].split(";")[0] in self.SAFE_TYPES:
if 'Content-Security-Policy' in resp:
del resp['Content-Security-Policy']
return resp
if not getattr(resp, '_csp_ignore', False):
resp['Content-Security-Policy'] = _render_csp(self._build_csp(request, resp))
elif 'Content-Security-Policy' in resp:
-1
View File
@@ -70,7 +70,6 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
if 'placeholders' in request.GET:
return self.get_placeholders_help(request)
resp = super().get(request, *args, **kwargs)
resp._csp_ignore = True
return resp
def get_placeholders_help(self, request):
-1
View File
@@ -162,7 +162,6 @@ class XHRView(View):
paypal_order = prov._create_paypal_order(request, None, cart_total)
r = JsonResponse(paypal_order.dict() if paypal_order else {})
r._csp_ignore = True
return r
-1
View File
@@ -48,7 +48,6 @@ def theme_css(request, **kwargs):
obj = getattr(request, "event", request.organizer)
css = get_theme_vars_css(obj, widget=False)
resp = HttpResponse(css, content_type="text/css")
resp._csp_ignore = True
resp["Access-Control-Allow-Origin"] = "*"
if "version" in request.GET:
resp["Expires"] = http_date(time.time() + 3600 * 24 * 30)
-4
View File
@@ -164,7 +164,6 @@ def widget_css(request, version, **kwargs):
css = f"/* v{version} */\n" + theme_css + widget_css
resp = FileResponse(css, content_type='text/css')
resp._csp_ignore = True
resp['Access-Control-Allow-Origin'] = '*'
return resp
@@ -246,7 +245,6 @@ def widget_js(request, version, lang, **kwargs):
cached_js = cache.get(cache_prefix)
if cached_js and not settings.DEBUG:
resp = HttpResponse(cached_js, content_type='text/javascript')
resp._csp_ignore = True
resp['Access-Control-Allow-Origin'] = '*'
return resp
@@ -278,7 +276,6 @@ def widget_js(request, version, lang, **kwargs):
gs.settings.set(checksum_key, checksum)
cache.set(cache_prefix, data, 3600 * 4)
resp = HttpResponse(data, content_type='text/javascript')
resp._csp_ignore = True
resp['Access-Control-Allow-Origin'] = '*'
return resp
@@ -414,7 +411,6 @@ class WidgetAPIProductList(EventListMixin, View):
self.post_process(data)
resp = JsonResponse(data)
resp['Access-Control-Allow-Origin'] = '*'
resp._csp_ignore = True
return resp
def get(self, request, *args, **kwargs):