diff options
| author | Tim Graham <timograham@gmail.com> | 2017-08-09 21:12:37 -0400 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2017-09-05 10:58:38 -0400 |
| commit | 46e2b9e059e617afe6fe56da9f132568a7e6b198 (patch) | |
| tree | 7856e671b8a8ef84c11bce51f973fd58ece3a663 | |
| parent | 73b6d027472fbbf823da4a084cdb9fb12e30dc60 (diff) | |
Fixed CVE-2017-12794 -- Fixed XSS possibility in traceback section of technical 500 debug page.
This is a security fix.
| -rw-r--r-- | django/views/debug.py | 4 | ||||
| -rw-r--r-- | django/views/templates/technical_500.html | 20 | ||||
| -rw-r--r-- | docs/releases/1.10.8.txt | 9 | ||||
| -rw-r--r-- | docs/releases/1.11.5.txt | 9 | ||||
| -rw-r--r-- | tests/view_tests/tests/test_debug.py | 12 |
5 files changed, 35 insertions, 19 deletions
diff --git a/django/views/debug.py b/django/views/debug.py index fca137fd07..35cff6338f 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -8,7 +8,7 @@ from pathlib import Path from django.conf import settings from django.http import HttpResponse, HttpResponseNotFound from django.template import Context, Engine, TemplateDoesNotExist -from django.template.defaultfilters import force_escape, pprint +from django.template.defaultfilters import pprint from django.urls import Resolver404, resolve from django.utils import timezone from django.utils.datastructures import MultiValueDict @@ -271,7 +271,7 @@ class ExceptionReporter: # Trim large blobs of data if len(v) > 4096: v = '%s... <trimmed %d bytes string>' % (v[0:4096], len(v)) - frame_vars.append((k, force_escape(v))) + frame_vars.append((k, v)) frame['vars'] = frame_vars frames[i] = frame diff --git a/django/views/templates/technical_500.html b/django/views/templates/technical_500.html index 01534f80d9..25cf231ead 100644 --- a/django/views/templates/technical_500.html +++ b/django/views/templates/technical_500.html @@ -212,38 +212,37 @@ <h2>Traceback <span class="commands">{% if not is_email %}<a href="#" onclick="return switchPastebinFriendly(this);"> Switch to copy-and-paste view</a></span>{% endif %} </h2> - {% autoescape off %} <div id="browserTraceback"> <ul class="traceback"> {% for frame in frames %} {% ifchanged frame.exc_cause %}{% if frame.exc_cause %} <li><h3> {% if frame.exc_cause_explicit %} - The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception: + The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception: {% else %} - During handling of the above exception ({{ frame.exc_cause }}), another exception occurred: + During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred: {% endif %} </h3></li> {% endif %}{% endifchanged %} <li class="frame {{ frame.type }}"> - <code>{{ frame.filename|escape }}</code> in <code>{{ frame.function|escape }}</code> + <code>{{ frame.filename }}</code> in <code>{{ frame.function }}</code> {% if frame.context_line %} <div class="context" id="c{{ frame.id }}"> {% if frame.pre_context and not is_email %} <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}"> {% for line in frame.pre_context %} - <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li> + <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li> {% endfor %} </ol> {% endif %} <ol start="{{ frame.lineno }}" class="context-line"> - <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line|escape }}</pre>{% if not is_email %} <span>...</span>{% endif %}</li> + <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line }}</pre>{% if not is_email %} <span>...</span>{% endif %}</li> </ol> {% if frame.post_context and not is_email %} <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}"> {% for line in frame.post_context %} - <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li> + <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li> {% endfor %} </ol> {% endif %} @@ -268,7 +267,7 @@ <tbody> {% for var in frame.vars|dictsort:0 %} <tr> - <td>{{ var.0|force_escape }}</td> + <td>{{ var.0 }}</td> <td class="code"><pre>{{ var.1 }}</pre></td> </tr> {% endfor %} @@ -279,7 +278,6 @@ {% endfor %} </ul> </div> - {% endautoescape %} <form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post"> {% if not is_email %} <div id="pastebinTraceback" class="pastebin"> @@ -318,9 +316,9 @@ In template {{ template_info.name }}, error at line {{ template_info.line }} Traceback:{% for frame in frames %} {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}{% if frame.exc_cause_explicit %} -The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception: +The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception: {% else %} -During handling of the above exception ({{ frame.exc_cause }}), another exception occurred: +During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred: {% endif %}{% endif %}{% endifchanged %} File "{{ frame.filename }}" in {{ frame.function }} {% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line }}{% endif %}{% endfor %} diff --git a/docs/releases/1.10.8.txt b/docs/releases/1.10.8.txt index 160e555fef..3785e6535a 100644 --- a/docs/releases/1.10.8.txt +++ b/docs/releases/1.10.8.txt @@ -5,3 +5,12 @@ Django 1.10.8 release notes *September 5, 2017* Django 1.10.8 fixes a security issue in 1.10.7. + +CVE-2017-12794: Possible XSS in traceback section of technical 500 debug page +============================================================================= + +In older versions, HTML autoescaping was disabled in a portion of the template +for the technical 500 debug page. Given the right circumstances, this allowed +a cross-site scripting attack. This vulnerability shouldn't affect most +production sites since you shouldn't run with ``DEBUG = True`` (which makes +this page accessible) in your production settings. diff --git a/docs/releases/1.11.5.txt b/docs/releases/1.11.5.txt index c0af25fb43..dda3489b93 100644 --- a/docs/releases/1.11.5.txt +++ b/docs/releases/1.11.5.txt @@ -6,6 +6,15 @@ Django 1.11.5 release notes Django 1.11.5 fixes a security issue and several bugs in 1.11.4. +CVE-2017-12794: Possible XSS in traceback section of technical 500 debug page +============================================================================= + +In older versions, HTML autoescaping was disabled in a portion of the template +for the technical 500 debug page. Given the right circumstances, this allowed +a cross-site scripting attack. This vulnerability shouldn't affect most +production sites since you shouldn't run with ``DEBUG = True`` (which makes +this page accessible) in your production settings. + Bugfixes ======== diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index 8de484d6a9..71c60210e4 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -349,10 +349,10 @@ class ExceptionReporterTests(SimpleTestCase): request = self.rf.get('/test_view/') try: try: - raise AttributeError('Top level') + raise AttributeError(mark_safe('<p>Top level</p>')) except AttributeError as explicit: try: - raise ValueError('Second exception') from explicit + raise ValueError(mark_safe('<p>Second exception</p>')) from explicit except ValueError: raise IndexError(mark_safe('<p>Final exception</p>')) except Exception: @@ -366,13 +366,13 @@ class ExceptionReporterTests(SimpleTestCase): html = reporter.get_traceback_html() # Both messages are twice on page -- one rendered as html, # one as plain text (for pastebin) - self.assertEqual(2, html.count(explicit_exc.format("Top level"))) - self.assertEqual(2, html.count(implicit_exc.format("Second exception"))) + self.assertEqual(2, html.count(explicit_exc.format('<p>Top level</p>'))) + self.assertEqual(2, html.count(implicit_exc.format('<p>Second exception</p>'))) self.assertEqual(10, html.count('<p>Final exception</p>')) text = reporter.get_traceback_text() - self.assertIn(explicit_exc.format("Top level"), text) - self.assertIn(implicit_exc.format("Second exception"), text) + self.assertIn(explicit_exc.format('<p>Top level</p>'), text) + self.assertIn(implicit_exc.format('<p>Second exception</p>'), text) self.assertEqual(3, text.count('<p>Final exception</p>')) def test_reporting_frames_without_source(self): |
