From e0e28bfe715b3f7d4e6cc7ab7bf4000b22c0cf79 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 13 Aug 2014 15:32:14 +0200 Subject: [PATCH] Fixed #20368 -- Made TECHNICAL_500 more robust against bad input. This limits large variables and avoids non-utf-8 in the TECHNICAL_500 output. --- django/views/debug.py | 12 +++++++- tests/view_tests/tests/test_debug.py | 46 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/django/views/debug.py b/django/views/debug.py index 23bf16604d..20fe104399 100644 --- a/django/views/debug.py +++ b/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... ' % (v[0:4096], len(v)) + frame_vars.append((k, force_escape(v))) + frame['vars'] = frame_vars frames[i] = frame unicode_hint = '' diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index 3d51da9f94..22bd11989f 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -302,6 +302,52 @@ class ExceptionReporterTests(TestCase): self.assertIn('

Request information

', html) self.assertIn('

Request data not supplied

', 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('
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('<trimmed %d bytes string>' % (large + repr_of_str_adds,), html)
+
     @skipIf(six.PY2, 'Bug manifests on PY3 only')
     def test_unfrozen_importlib(self):
         """