Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Next
- Add project urls to setup.py
- Drop support for EOL Python <3.6 and Django <2.2 versions
- Rename default branch to main
- Fix capturing brackets in script template tags

3.7
===
Expand Down
19 changes: 19 additions & 0 deletions csp/tests/test_jinja_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,22 @@ def test_nested_script_tags_are_removed(self):
'var hello=\'world\';</script>')

self.assert_template_eq(*self.process_templates(tpl, expected))

def test_regex_captures_script_content_including_brackets(self):
"""
Ensure that script content get captured properly.
Especially when using angle brackets."""
tpl = """
{% script %}
<script type="text/javascript">
let capture_text = "<script></script>"
</script>
{% endscript %}
"""

expected = (
'<script nonce="{}">'
'let capture_text = "<script></script>"'
'</script>')

self.assert_template_eq(*self.process_templates(tpl, expected))
20 changes: 20 additions & 0 deletions csp/tests/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,23 @@ def test_nested_script_tags_are_removed(self):
'var hello=\'world\';</script>')

self.assert_template_eq(*self.process_templates(tpl, expected))

def test_regex_captures_script_content_including_brackets(self):
"""
Ensure that script content get captured properly.
Especially when using angle brackets."""
tpl = """
{% load csp %}
{% script %}
<script type="text/javascript">
let capture_text = "<script></script>"
</script>
{% endscript %}
"""

expected = (
'<script nonce="{}">'
'let capture_text = "<script></script>"'
'</script>')

self.assert_template_eq(*self.process_templates(tpl, expected))
2 changes: 1 addition & 1 deletion csp/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ScriptTestBase(object):
def assert_template_eq(self, tpl1, tpl2):
aaa = tpl1.replace('\n', '').replace(' ', '')
bbb = tpl2.replace('\n', '').replace(' ', '')
assert aaa == bbb
assert aaa == bbb, "{} != {}".format(aaa, bbb)

def process_templates(self, tpl, expected):
request = rf.get('/')
Expand Down
12 changes: 11 additions & 1 deletion csp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,19 @@ def _async_attr_mapper(attr_name, val):
ATTR_FORMAT_STR = ''.join(['{{{}}}'.format(a) for a in SCRIPT_ATTRS])


_script_tag_contents_re = re.compile(
r"""<script # match the opening script tag
[\s|\S]*?> # minimally match attrs and spaces in opening script tag
([\s|\S]+) # greedily capture the script tag contents
</script> # match the closing script tag
""",
re.VERBOSE,
)


def _unwrap_script(text):
"""Extract content defined between script tags"""
matches = re.search(r'<script[\s|\S]*>([\s|\S]+?)</script>', text)
matches = re.search(_script_tag_contents_re, text)
if matches and len(matches.groups()):
return matches.group(1).strip()

Expand Down
48 changes: 41 additions & 7 deletions docs/nonce.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,32 @@ This library contains an optional context processor, adding ``csp.context_proces

.. note::

If you're making use of ``csp.extensions.NoncedScript`` you need to have ``jinja2>=2.9.6`` installed, so please make sure to either use ``django-csp[jinja2]`` in your requirements or define it yourself.
If you're making use of ``csp.extensions.NoncedScript`` you need to have ``jinja2>=2.9.6`` installed, so please make sure to either use ``django-csp[jinja2]`` in your requirements or define it yourself.

Since it can be easy to forget to include the ``nonce`` property in a script tag, there is also a ``script`` template tag available for both Django templates and Jinja environments.

It can be easy to forget to include the ``nonce`` property in a script tag, so there is also a ``script`` template tag available for both Django templates and Jinja environments.

This tag will output a properly nonced script every time. For the sake of syntax highlighting, you can wrap the content inside of the ``script`` tag in ``<script>`` html tags, which will be subsequently removed in the rendered output. Any valid script tag attributes can be specified and will be forwarded into the rendered html.

Django:

Django Templates
----------------

Add the CSP template tags to the TEMPLATES section of your settings file:

.. code-block:: python

TEMPLATES = [
{
"OPTIONS": {
'libraries': {
'csp': 'csp.templatetags.csp',
}
},
}
]

Then load the ``csp`` template tags and use ``script`` in the template:

.. code-block:: jinja

Expand All @@ -60,9 +79,24 @@ Django:
{% endscript %}


Jinja:
Jinja
-----

Add ``csp.extensions.NoncedScript`` to the TEMPLATES section of your settings file:

.. code-block:: python

TEMPLATES = [
{
'BACKEND':'django.template.backends.jinja2.Jinja2',
'OPTIONS': {
'extensions': [
'csp.extensions.NoncedScript',
],
}
}
]

(assumes ``csp.extensions.NoncedScript`` is added to the jinja extensions setting)

.. code-block:: jinja

Expand All @@ -72,9 +106,9 @@ Jinja:
</script>
{% endscript %}

Will output -

Both templates output the following with a different nonce:

.. code-block:: html

<script nonce='123456' type="application/javascript" async=false>var hello='world';</script>