diff --git a/django/utils/html.py b/django/utils/html.py index d5fe2f4a6b..0ecd48957a 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -4,7 +4,9 @@ from __future__ import unicode_literals import re import sys +import warnings +from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_text, force_str from django.utils.functional import allow_lazy from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS @@ -177,6 +179,11 @@ strip_tags = allow_lazy(strip_tags) def remove_tags(html, tags): """Returns the given HTML with given tags removed.""" + warnings.warn( + "django.utils.html.remove_tags() and the removetags template filter " + "are deprecated. Consider using the bleach library instead.", + RemovedInDjango20Warning, stacklevel=3 + ) tags = [re.escape(tag) for tag in tags.split()] tags_re = '(%s)' % '|'.join(tags) starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) @@ -195,6 +202,10 @@ strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, six.text_type) def strip_entities(value): """Returns the given HTML with all entities (&something;) stripped.""" + warnings.warn( + "django.utils.html.strip_entities() is deprecated.", + RemovedInDjango20Warning, stacklevel=2 + ) return re.sub(r'&(?:\w+|#\d+);', '', force_text(value)) strip_entities = allow_lazy(strip_entities, six.text_type) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index ed84626d3f..eca4a5f4f3 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -49,6 +49,11 @@ about each item can often be found in the release notes of two versions prior. * The backward compatible shim to rename ``django.forms.Form._has_changed()`` to ``has_changed()`` will be removed. +* The ``removetags`` template filter will be removed. + +* The ``remove_tags()`` and ``strip_entities()`` functions in + ``django.utils.html`` will be removed. + .. _deprecation-removed-in-1.9: 1.9 diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index fcda7aaa56..516ddbe1de 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -1918,6 +1918,13 @@ If ``value`` is the list ``['a', 'b', 'c', 'd']``, the output could be ``"b"``. removetags ^^^^^^^^^^ +.. deprecated:: 1.8 + + ``removetags`` cannot guarantee HTML safe output and has been deprecated due + to security concerns. Consider using `bleach`_ instead. + +.. _bleach: http://bleach.readthedocs.org/en/latest/ + Removes a space-separated list of [X]HTML tags from the output. For example:: diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index b7e3693f59..2f2b15de35 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -630,10 +630,13 @@ escaping HTML. If you are looking for a more robust solution, take a look at the `bleach`_ Python library. - .. _bleach: https://pypi.python.org/pypi/bleach - .. function:: remove_tags(value, tags) + .. deprecated:: 1.8 + + ``remove_tags()`` cannot guarantee HTML safe output and has been + deprecated due to security concerns. Consider using `bleach`_ instead. + Removes a space-separated list of [X]HTML tag names from the output. Absolutely NO guarantee is provided about the resulting string being HTML @@ -656,6 +659,7 @@ escaping HTML. the return value will be ``"Joel a slug"``. .. _str.format: http://docs.python.org/library/stdtypes.html#str.format +.. _bleach: https://pypi.python.org/pypi/bleach ``django.utils.http`` ===================== diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 9b013c676c..22faffd330 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -682,3 +682,14 @@ Using the new syntax, this becomes:: Rename this method to :meth:`~django.forms.Field.has_changed` by removing the leading underscore. The old name will still work until Django 2.0. + +``django.utils.html.remove_tags()`` and ``removetags`` template filter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``django.utils.html.remove_tags()`` as well as the template filter +``removetags`` have been deprecated as they cannot guarantee safe output. Their +existence is likely to lead to their use in security-sensitive contexts where +they are not actually safe. + +The unused and undocumented ``django.utils.html.strip_entities()`` function has +also been deprecated. diff --git a/tests/defaultfilters/tests.py b/tests/defaultfilters/tests.py index c61227efd5..7d135f8caf 100644 --- a/tests/defaultfilters/tests.py +++ b/tests/defaultfilters/tests.py @@ -433,9 +433,13 @@ class DefaultFiltersTests(TestCase): 'line 1
line 2') def test_removetags(self): - self.assertEqual(removetags('some html with disallowed tags', 'script img'), - 'some html with alert("You smell") disallowed tags') + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + self.assertEqual(removetags('some html with disallowed tags', 'script img'), + 'some html with alert("You smell") disallowed tags') + + def test_striptags(self): self.assertEqual(striptags('some html with disallowed tags'), 'some html with alert("You smell") disallowed tags') @@ -713,7 +717,9 @@ class DefaultFiltersTests(TestCase): self.assertEqual(escape(123), '123') self.assertEqual(linebreaks_filter(123), '

123

') self.assertEqual(linebreaksbr(123), '123') - self.assertEqual(removetags(123, 'a'), '123') + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + self.assertEqual(removetags(123, 'a'), '123') self.assertEqual(striptags(123), '123') diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index c24040ebfe..ed3a019721 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -590,7 +590,8 @@ class TemplateTests(TestCase): # Ignore deprecations of using the wrong number of variables with the 'for' tag. # and warnings for {% url %} reversing by dotted path warnings.filterwarnings("ignore", category=RemovedInDjango20Warning, module="django.template.defaulttags") - # Ignore deprecations of old style unordered_list. + # Ignore deprecations of old style unordered_list + # and removetags. warnings.filterwarnings("ignore", category=RemovedInDjango20Warning, module="django.template.defaultfilters") output = self.render(test_template, vals) except ShouldNotExecuteException: diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index 0c6167ef50..d08eb0be2b 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals from datetime import datetime import os from unittest import TestCase +import warnings from django.utils import html, safestring from django.utils._os import upath @@ -124,7 +125,9 @@ class TestUtilsHtml(TestCase): # Strings that should come out untouched. values = ("&", "&a", "&a", "a&#a") for value in values: - self.check_output(f, value) + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + self.check_output(f, value) # Valid entities that should be stripped from the patterns. entities = ("", " ", "&a;", "&fdasdfasdfasdf;") patterns = ( @@ -135,7 +138,9 @@ class TestUtilsHtml(TestCase): ) for entity in entities: for in_pattern, output in patterns: - self.check_output(f, in_pattern % {'entity': entity}, output) + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + self.check_output(f, in_pattern % {'entity': entity}, output) def test_escapejs(self): f = html.escapejs @@ -156,7 +161,9 @@ class TestUtilsHtml(TestCase): ("x

y

", "a b", "x

y

"), ) for value, tags, output in items: - self.assertEqual(f(value, tags), output) + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + self.assertEqual(f(value, tags), output) def test_smart_urlquote(self): quote = html.smart_urlquote