Browse Source

Fixed CVE-2022-22818 -- Fixed possible XSS via {% debug %} template tag.

Thanks Keryn Knight for the report.

Co-authored-by: Adam Johnson <me@adamj.eu>
Markus Holtermann 3 years ago
parent
commit
394517f078

+ 6 - 3
django/template/defaulttags.py

@@ -8,7 +8,7 @@ from itertools import cycle as itertools_cycle, groupby
 
 from django.conf import settings
 from django.utils import timezone
-from django.utils.html import conditional_escape, format_html
+from django.utils.html import conditional_escape, escape, format_html
 from django.utils.lorem_ipsum import paragraphs, words
 from django.utils.safestring import mark_safe
 
@@ -99,10 +99,13 @@ class CycleNode(Node):
 
 class DebugNode(Node):
     def render(self, context):
+        if not settings.DEBUG:
+            return ''
+
         from pprint import pformat
-        output = [pformat(val) for val in context]
+        output = [escape(pformat(val)) for val in context]
         output.append('\n\n')
-        output.append(pformat(sys.modules))
+        output.append(escape(pformat(sys.modules)))
         return ''.join(output)
 
 

+ 7 - 1
docs/ref/templates/builtins.txt

@@ -194,7 +194,13 @@ from its first value when it's next encountered.
 ---------
 
 Outputs a whole load of debugging information, including the current context
-and imported modules.
+and imported modules. ``{% debug %}`` outputs nothing when the :setting:`DEBUG`
+setting is ``False``.
+
+.. versionchanged:: 2.2.27
+
+    In older versions, debugging information was displayed when the
+    :setting:`DEBUG` setting was ``False``.
 
 .. templatetag:: extends
 

+ 9 - 1
docs/releases/2.2.27.txt

@@ -6,4 +6,12 @@ Django 2.2.27 release notes
 
 Django 2.2.27 fixes two security issues with severity "medium" in 2.2.26.
 
-...
+CVE-2022-22818: Possible XSS via ``{% debug %}`` template tag
+=============================================================
+
+The ``{% debug %}`` template tag didn't properly encode the current context,
+posing an XSS attack vector.
+
+In order to avoid this vulnerability, ``{% debug %}`` no longer outputs an
+information when the ``DEBUG`` setting is ``False``, and it ensures all context
+variables are correctly escaped when the ``DEBUG`` setting is ``True``.

+ 9 - 1
docs/releases/3.2.12.txt

@@ -6,4 +6,12 @@ Django 3.2.12 release notes
 
 Django 3.2.12 fixes two security issues with severity "medium" in 3.2.11.
 
-...
+CVE-2022-22818: Possible XSS via ``{% debug %}`` template tag
+=============================================================
+
+The ``{% debug %}`` template tag didn't properly encode the current context,
+posing an XSS attack vector.
+
+In order to avoid this vulnerability, ``{% debug %}`` no longer outputs an
+information when the ``DEBUG`` setting is ``False``, and it ensures all context
+variables are correctly escaped when the ``DEBUG`` setting is ``True``.

+ 10 - 0
docs/releases/4.0.2.txt

@@ -8,6 +8,16 @@ Django 4.0.2 fixes two security issues with severity "medium" and several bugs
 in 4.0.1. Also, the latest string translations from Transifex are incorporated,
 with a special mention for Bulgarian (fully translated).
 
+CVE-2022-22818: Possible XSS via ``{% debug %}`` template tag
+=============================================================
+
+The ``{% debug %}`` template tag didn't properly encode the current context,
+posing an XSS attack vector.
+
+In order to avoid this vulnerability, ``{% debug %}`` no longer outputs an
+information when the ``DEBUG`` setting is ``False``, and it ensures all context
+variables are correctly escaped when the ``DEBUG`` setting is ``True``.
+
 Bugfixes
 ========
 

+ 46 - 0
tests/template_tests/syntax_tests/test_debug.py

@@ -0,0 +1,46 @@
+from django.contrib.auth.models import Group
+from django.test import SimpleTestCase, override_settings
+
+from ..utils import setup
+
+
+@override_settings(DEBUG=True)
+class DebugTests(SimpleTestCase):
+
+    @override_settings(DEBUG=False)
+    @setup({'non_debug': '{% debug %}'})
+    def test_non_debug(self):
+        output = self.engine.render_to_string('non_debug', {})
+        self.assertEqual(output, '')
+
+    @setup({'modules': '{% debug %}'})
+    def test_modules(self):
+        output = self.engine.render_to_string('modules', {})
+        self.assertIn(
+            '&#x27;django&#x27;: &lt;module &#x27;django&#x27; ',
+            output,
+        )
+
+    @setup({'plain': '{% debug %}'})
+    def test_plain(self):
+        output = self.engine.render_to_string('plain', {'a': 1})
+        self.assertTrue(output.startswith(
+            '{&#x27;a&#x27;: 1}'
+            '{&#x27;False&#x27;: False, &#x27;None&#x27;: None, '
+            '&#x27;True&#x27;: True}\n\n{'
+        ))
+
+    @setup({'non_ascii': '{% debug %}'})
+    def test_non_ascii(self):
+        group = Group(name="清風")
+        output = self.engine.render_to_string('non_ascii', {'group': group})
+        self.assertTrue(output.startswith(
+            '{&#x27;group&#x27;: &lt;Group: 清風&gt;}'
+        ))
+
+    @setup({'script': '{% debug %}'})
+    def test_script(self):
+        output = self.engine.render_to_string('script', {'frag': '<script>'})
+        self.assertTrue(output.startswith(
+            '{&#x27;frag&#x27;: &#x27;&lt;script&gt;&#x27;}'
+        ))

+ 0 - 10
tests/template_tests/tests.py

@@ -1,6 +1,5 @@
 import sys
 
-from django.contrib.auth.models import Group
 from django.template import (
     Context, Engine, TemplateDoesNotExist, TemplateSyntaxError,
 )
@@ -163,15 +162,6 @@ class TemplateTestMixin:
         with self.assertRaises(NoReverseMatch):
             t.render(Context())
 
-    def test_debug_tag_non_ascii(self):
-        """
-        #23060 -- Test non-ASCII model representation in debug output.
-        """
-        group = Group(name="清風")
-        c1 = Context({"objs": [group]})
-        t1 = self._engine().from_string('{% debug %}')
-        self.assertIn("清風", t1.render(c1))
-
     def test_extends_generic_template(self):
         """
         #24338 -- Allow extending django.template.backends.django.Template