-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add templatetags to clean HTML within django templates (#16)
- Loading branch information
Showing
6 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import nh3 | ||
from django import template | ||
from django.utils.safestring import SafeText, mark_safe | ||
|
||
from django_nh3.utils import get_nh3_default_options | ||
|
||
register = template.Library() | ||
|
||
|
||
@register.filter(name="nh3") | ||
def nh3_value(value: str | None, tags: str | None = None) -> SafeText: | ||
""" | ||
Takes an input HTML value and sanitizes it utilizing nh3, | ||
returning a SafeText object that can be rendered by Django. | ||
Accepts an optional argument of allowed tags. Should be a comma delimited | ||
string (ie. "img,span" or "img") | ||
""" | ||
if value is None: | ||
return None | ||
|
||
args = {} | ||
|
||
nh3_args = get_nh3_default_options() | ||
if tags is not None: | ||
args = nh3_args.copy() | ||
args["tags"] = set(tags.split(",")) | ||
else: | ||
args = nh3_args | ||
|
||
nh3_value = nh3.clean(value, **args) | ||
return mark_safe(nh3_value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import logging | ||
from typing import Any | ||
|
||
from django.conf import settings | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def get_nh3_default_options() -> dict[str, Any]: | ||
""" | ||
Pull the django-nh3 settings similarly to how django-bleach handled them. | ||
Some django-bleach settings can be mapped to django-nh3 settings without | ||
any changes: | ||
BLEACH_ALLOWED_TAGS -> NH3_ALLOWED_TAGS | ||
BLEACH_ALLOWED_ATTRIBUTES -> NH3_ALLOWED_ATTRIBUTES | ||
BLEACH_STRIP_COMMENTS -> NH3_STRIP_COMMENTS | ||
While other settings are have no current support in nh3: | ||
BLEACH_ALLOWED_STYLES -> There is no support for styling | ||
BLEACH_ALLOWED_PROTOCOLS -> There is no suport for protocols | ||
BLEACH_STRIP_TAGS -> This is the default behavior of nh3 | ||
""" | ||
nh3_args: dict[str, Any] = {} | ||
|
||
nh3_settings = { | ||
"NH3_ALLOWED_TAGS": "tags", | ||
"NH3_ALLOWED_ATTRIBUTES": "attributes", | ||
"NH3_STRIP_COMMENTS": "strip_comments", | ||
} | ||
|
||
for setting, kwarg in nh3_settings.items(): | ||
if hasattr(settings, setting): | ||
attr = getattr(settings, setting) | ||
|
||
# Convert from general iterables to sets | ||
if setting == "NH3_ALLOWED_TAGS": | ||
attr = set(attr) | ||
elif setting == "NH3_ALLOWED_ATTRIBUTES": | ||
copy_dict = attr.copy() | ||
for tag, attributes in attr.items(): | ||
copy_dict[tag] = set(attributes) | ||
attr = copy_dict | ||
|
||
nh3_args[kwarg] = attr | ||
|
||
return nh3_args |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
ALLOWED_ATTRIBUTES = {"*": {"class", "style"}, "a": {"href", "title"}} | ||
|
||
ALLOWED_TAGS = {"a", "li", "ul"} | ||
|
||
STRIP_COMMENTS = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from unittest.mock import patch | ||
|
||
from django.test import TestCase | ||
from django_nh3.utils import get_nh3_default_options | ||
|
||
from .constants import ALLOWED_ATTRIBUTES, ALLOWED_TAGS, STRIP_COMMENTS | ||
|
||
|
||
class TestBleachOptions(TestCase): | ||
@patch( | ||
"django_nh3.utils.settings", | ||
NH3_ALLOWED_ATTRIBUTES=ALLOWED_ATTRIBUTES, | ||
) | ||
def test_custom_attrs(self, settings): | ||
nh3_args = get_nh3_default_options() | ||
self.assertEqual(nh3_args["attributes"], ALLOWED_ATTRIBUTES) | ||
|
||
@patch( | ||
"django_nh3.utils.settings", | ||
NH3_ALLOWED_TAGS=ALLOWED_TAGS, | ||
) | ||
def test_custom_tags(self, settings): | ||
nh3_args = get_nh3_default_options() | ||
self.assertEqual(nh3_args["tags"], ALLOWED_TAGS) | ||
|
||
@patch( | ||
"django_nh3.utils.settings", | ||
NH3_STRIP_COMMENTS=STRIP_COMMENTS, | ||
) | ||
def test_strip_comments(self, settings): | ||
nh3_args = get_nh3_default_options() | ||
self.assertEqual(nh3_args["strip_comments"], STRIP_COMMENTS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from django.template import Context, Template | ||
from django.test import TestCase | ||
|
||
|
||
class TestBleachTemplates(TestCase): | ||
"""Test template tags""" | ||
|
||
def test_bleaching(self): | ||
"""Test that unsafe tags are sanitised""" | ||
context = Context( | ||
{"some_unsafe_content": '<script>alert("Hello World!")</script>'}, | ||
) | ||
template_to_render = Template( | ||
"{% load nh3_tags %}" "{{ some_unsafe_content|nh3 }}" | ||
) | ||
rendered_template = template_to_render.render(context) | ||
self.assertEqual("", rendered_template) | ||
|
||
def test_bleaching_none(self): | ||
"""Test that None is handled properly as an input""" | ||
context = Context({"none_value": None}) | ||
template_to_render = Template( | ||
"{% load nh3_tags %}" "{{ none_value|nh3 }}" | ||
) | ||
rendered_template = template_to_render.render(context) | ||
self.assertEqual("None", rendered_template) | ||
|
||
def test_bleaching_tags(self): | ||
"""Test provided tags are kept""" | ||
context = Context( | ||
{ | ||
"some_unsafe_content": ( | ||
"<b><img src='' " | ||
"onerror='alert(\\'hax\\')'>" | ||
"I'm not trying to XSS you</b>" | ||
) | ||
} | ||
) | ||
template_to_render = Template( | ||
"{% load nh3_tags %}" '{{ some_unsafe_content|nh3:"img" }}' | ||
) | ||
rendered_template = template_to_render.render(context) | ||
self.assertInHTML( | ||
'<img src="">I\'m not trying to XSS you', rendered_template | ||
) |