neon-jungle / wagtail-schema.org

Schema.org JSON-LD tags for Wagtail sites
BSD 2-Clause "Simplified" License
70 stars 17 forks source link

Currently unable to add Nonce for Content Security Policy #17

Open dgabrahams opened 1 year ago

dgabrahams commented 1 year ago

The wagtail-schema.org app seems to use this template tag to output the script element:

    json_out = json.dumps(entity, cls=JSONLDEncoder, sort_keys=True)
    return mark_safe('<script type="application/ld+json">{}</script>'.format(
        json_out))

To work with django-csp it should be modified to check if request.csp_nonce is available and if yes add the nonce so the final output will be: <script type="application/ld+json" nonce="xxx">...</script>

It might also be prudent to allow a more versatile approach where custom nonces can be passed, this way users are not tied to having to use django-csp

Update: I made wagtail-schema.org compatible with django-csp by making the following changes in a custom templatetag file:

import json

from django import template
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

from wagtailschemaorg.encoder import JSONLDEncoder
from wagtailschemaorg.registry import registry
from wagtailschemaorg.templatetags.wagtailschemaorg_tags import *

register = template.Library()

def nl(xs):
    return mark_safe('\n'.join(map(conditional_escape, xs)))

def ld_for_site(site, nonce=None):
    f = lambda lst: ld_print_entity(lst, nonce)
    return nl(map(f, registry.get_entities(site)))

def ld_print_entity(entity, nonce):
    json_out = json.dumps(entity, cls=JSONLDEncoder, sort_keys=True)
    return mark_safe('<script type="application/ld+json"{}>{}</script>'.format(nonce,json_out))

@register.simple_tag(takes_context=True)
def custom_ld_for_site(context, site=None):
    if site is None:
        site = Site.find_for_request(context["request"])

    nonce = ""
    if context.request.csp_nonce:
        nonce = f" nonce=\"{context.request.csp_nonce}\""

    return ld_for_site(site, nonce)

Include on a Django template:

{% custom_ld_for_site %}