1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
From: Chris Lamb <lamby@debian.org>
Date: Thu, 8 Aug 2019 10:36:25 +0100
Subject: CVE-2019-14235
Backported from
<https://github.com/django/django/commit/869b34e9b3be3a4cfcb3a145f218ffd3f5e3fd79>
---
django/utils/encoding.py | 17 ++++++++++-------
tests/utils_tests/test_encoding.py | 12 +++++++++++-
2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/django/utils/encoding.py b/django/utils/encoding.py
index 66077e2..2a03d10 100644
--- a/django/utils/encoding.py
+++ b/django/utils/encoding.py
@@ -236,13 +236,16 @@ def repercent_broken_unicode(path):
we need to re-percent-encode any octet produced that is not part of a
strictly legal UTF-8 octet sequence.
"""
- try:
- path.decode('utf-8')
- except UnicodeDecodeError as e:
- repercent = quote(path[e.start:e.end], safe=b"/#%[]=:;$&()+,!?*@'~")
- path = repercent_broken_unicode(
- path[:e.start] + force_bytes(repercent) + path[e.end:])
- return path
+ while True:
+ try:
+ path.decode('utf-8')
+ except UnicodeDecodeError as e:
+ # CVE-2019-14235: A recursion shouldn't be used since the exception
+ # handling uses massive amounts of memory
+ repercent = quote(path[e.start:e.end], safe=b"/#%[]=:;$&()+,!?*@'~")
+ path = path[:e.start] + force_bytes(repercent) + path[e.end:]
+ else:
+ return path
def filepath_to_uri(path):
diff --git a/tests/utils_tests/test_encoding.py b/tests/utils_tests/test_encoding.py
index 5ddb18d..df4cc9d 100644
--- a/tests/utils_tests/test_encoding.py
+++ b/tests/utils_tests/test_encoding.py
@@ -2,12 +2,13 @@
from __future__ import unicode_literals
import datetime
+import sys
import unittest
from django.utils import six
from django.utils.encoding import (
escape_uri_path, filepath_to_uri, force_bytes, force_text, iri_to_uri,
- smart_text, uri_to_iri,
+ repercent_broken_unicode, smart_text, uri_to_iri,
)
from django.utils.functional import SimpleLazyObject
from django.utils.http import urlquote_plus
@@ -76,6 +77,15 @@ class TestEncodingUtils(unittest.TestCase):
self.assertEqual(smart_text(1), '1')
self.assertEqual(smart_text('foo'), 'foo')
+ def test_repercent_broken_unicode_recursion_error(self):
+ # Prepare a string long enough to force a recursion error if the tested
+ # function uses recursion.
+ data = b'\xfc' * sys.getrecursionlimit()
+ try:
+ self.assertEqual(repercent_broken_unicode(data), b'%FC' * sys.getrecursionlimit())
+ except RecursionError:
+ self.fail('Unexpected RecursionError raised.')
+
class TestRFC3987IEncodingUtils(unittest.TestCase):
|