From 01422046065d2b51f8f613409cad2c81b39487e5 Mon Sep 17 00:00:00 2001 From: Markus Holtermann Date: Sun, 2 Jan 2022 00:37:40 +0100 Subject: [PATCH] [4.0.x] Fixed CVE-2022-22818 -- Fixed possible XSS via {% debug %} template tag. Thanks Keryn Knight for the report. Backport of 394517f07886495efcf79f95c7ee402a9437bd68 from main. Co-authored-by: Adam Johnson --- django/template/defaulttags.py | 9 ++-- docs/ref/templates/builtins.txt | 8 +++- docs/releases/2.2.27.txt | 10 +++- docs/releases/3.2.12.txt | 10 +++- docs/releases/4.0.2.txt | 10 ++++ .../template_tests/syntax_tests/test_debug.py | 46 +++++++++++++++++++ tests/template_tests/tests.py | 10 ---- 7 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 tests/template_tests/syntax_tests/test_debug.py diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index bc401853a4..99c09a483a 100644 --- a/django/template/defaulttags.py +++ b/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) diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 733cc3cdb2..a664e9d2d2 100644 --- a/docs/ref/templates/builtins.txt +++ b/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 diff --git a/docs/releases/2.2.27.txt b/docs/releases/2.2.27.txt index a35082fa33..b1712c649c 100644 --- a/docs/releases/2.2.27.txt +++ b/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``. diff --git a/docs/releases/3.2.12.txt b/docs/releases/3.2.12.txt index 8e4e9149fe..31bc7d2c59 100644 --- a/docs/releases/3.2.12.txt +++ b/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``. diff --git a/docs/releases/4.0.2.txt b/docs/releases/4.0.2.txt index c5176e01fc..d949d49dd6 100644 --- a/docs/releases/4.0.2.txt +++ b/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 ======== diff --git a/tests/template_tests/syntax_tests/test_debug.py b/tests/template_tests/syntax_tests/test_debug.py new file mode 100644 index 0000000000..2f527b44ae --- /dev/null +++ b/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( + ''django': <module 'django' ', + output, + ) + + @setup({'plain': '{% debug %}'}) + def test_plain(self): + output = self.engine.render_to_string('plain', {'a': 1}) + self.assertTrue(output.startswith( + '{'a': 1}' + '{'False': False, 'None': None, ' + ''True': 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( + '{'group': <Group: 清風>}' + )) + + @setup({'script': '{% debug %}'}) + def test_script(self): + output = self.engine.render_to_string('script', {'frag': '