浏览代码

Fixed #20368 -- Made TECHNICAL_500 more robust against bad input.

This limits large variables and avoids non-utf-8 in the TECHNICAL_500 output.
Walter Doekes 10 年之前
父节点
当前提交
e0e28bfe71
共有 2 个文件被更改,包括 57 次插入1 次删除
  1. 11 1
      django/views/debug.py
  2. 46 0
      tests/view_tests/tests/test_debug.py

+ 11 - 1
django/views/debug.py

@@ -309,7 +309,17 @@ class ExceptionReporter(object):
         frames = self.get_traceback_frames()
         for i, frame in enumerate(frames):
             if 'vars' in frame:
-                frame['vars'] = [(k, force_escape(pprint(v))) for k, v in frame['vars']]
+                frame_vars = []
+                for k, v in frame['vars']:
+                    v = pprint(v)
+                    # The force_escape filter assume unicode, make sure that works
+                    if isinstance(v, six.binary_type):
+                        v = v.decode('utf-8', 'replace')  # don't choke on non-utf-8 input
+                    # 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'] = frame_vars
             frames[i] = frame
 
         unicode_hint = ''

+ 46 - 0
tests/view_tests/tests/test_debug.py

@@ -302,6 +302,52 @@ class ExceptionReporterTests(TestCase):
         self.assertIn('<h2>Request information</h2>', html)
         self.assertIn('<p>Request data not supplied</p>', html)
 
+    def test_non_utf8_values_handling(self):
+        "Non-UTF-8 exceptions/values should not make the output generation choke."
+        try:
+            class NonUtf8Output(Exception):
+                def __repr__(self):
+                    return b'EXC\xe9EXC'
+            somevar = b'VAL\xe9VAL'  # NOQA
+            raise NonUtf8Output()
+        except Exception:
+            exc_type, exc_value, tb = sys.exc_info()
+        reporter = ExceptionReporter(None, exc_type, exc_value, tb)
+        html = reporter.get_traceback_html()
+        self.assertIn('VAL\\xe9VAL', html)
+        self.assertIn('EXC\\xe9EXC', html)
+
+    def test_unprintable_values_handling(self):
+        "Unprintable values should not make the output generation choke."
+        try:
+            class OomOutput(object):
+                def __repr__(self):
+                    raise MemoryError('OOM')
+            oomvalue = OomOutput()  # NOQA
+            raise ValueError()
+        except Exception:
+            exc_type, exc_value, tb = sys.exc_info()
+        reporter = ExceptionReporter(None, exc_type, exc_value, tb)
+        html = reporter.get_traceback_html()
+        self.assertIn('<td class="code"><pre>Error in formatting', html)
+
+    def test_too_large_values_handling(self):
+        "Large values should not create a large HTML."
+        large = 32 * 1024 * 1024
+        repr_of_str_adds = len(repr(''))
+        try:
+            class LargeOutput(object):
+                def __repr__(self):
+                    return repr('A' * large)
+            largevalue = LargeOutput()  # NOQA
+            raise ValueError()
+        except Exception:
+            exc_type, exc_value, tb = sys.exc_info()
+        reporter = ExceptionReporter(None, exc_type, exc_value, tb)
+        html = reporter.get_traceback_html()
+        self.assertEqual(len(html) // 1024 // 1024, 0)  # still fit in 1MB
+        self.assertIn('&lt;trimmed %d bytes string&gt;' % (large + repr_of_str_adds,), html)
+
     @skipIf(six.PY2, 'Bug manifests on PY3 only')
     def test_unfrozen_importlib(self):
         """