From fc76660f589ac07e45e9cd34ccb8087aeb11904b Mon Sep 17 00:00:00 2001 From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:38:34 +0200 Subject: [PATCH] [4.2.x] Fixed CVE-2024-41989 -- Prevented excessive memory consumption in floatformat. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks Elias Myllymäki for the report. Co-authored-by: Shai Berger --- django/template/defaultfilters.py | 13 +++++++++++++ docs/releases/4.2.15.txt | 9 +++++++++ .../filter_tests/test_floatformat.py | 17 +++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index d446b54ade..3f89eba6bb 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -163,6 +163,19 @@ def floatformat(text, arg=-1): except ValueError: return input_val + _, digits, exponent = d.as_tuple() + try: + number_of_digits_and_exponent_sum = len(digits) + abs(exponent) + except TypeError: + # Exponent values can be "F", "n", "N". + number_of_digits_and_exponent_sum = 0 + + # Values with more than 200 digits, or with a large exponent, are returned "as is" + # to avoid high memory consumption and potential denial-of-service attacks. + # The cut-off of 200 is consistent with django.utils.numberformat.floatformat(). + if number_of_digits_and_exponent_sum > 200: + return input_val + try: m = int(d) - d except (ValueError, OverflowError, InvalidOperation): diff --git a/docs/releases/4.2.15.txt b/docs/releases/4.2.15.txt index d312f8580f..f3fdb0a3cf 100644 --- a/docs/releases/4.2.15.txt +++ b/docs/releases/4.2.15.txt @@ -7,6 +7,15 @@ Django 4.2.15 release notes Django 4.2.15 fixes three security issues with severity "moderate", one security issue with severity "high", and a regression in 4.2.14. +CVE-2024-41989: Memory exhaustion in ``django.utils.numberformat.floatformat()`` +================================================================================ + +If :tfilter:`floatformat` received a string representation of a number in +scientific notation with a large exponent, it could lead to significant memory +consumption. + +To avoid this, decimals with more than 200 digits are now returned as is. + Bugfixes ======== diff --git a/tests/template_tests/filter_tests/test_floatformat.py b/tests/template_tests/filter_tests/test_floatformat.py index db17622309..c22b5dca6b 100644 --- a/tests/template_tests/filter_tests/test_floatformat.py +++ b/tests/template_tests/filter_tests/test_floatformat.py @@ -77,6 +77,7 @@ class FunctionTests(SimpleTestCase): self.assertEqual(floatformat(1.5e-15, 20), "0.00000000000000150000") self.assertEqual(floatformat(1.5e-15, -20), "0.00000000000000150000") self.assertEqual(floatformat(1.00000000000000015, 16), "1.0000000000000002") + self.assertEqual(floatformat("1e199"), "1" + "0" * 199) def test_force_grouping(self): with translation.override("en"): @@ -134,6 +135,22 @@ class FunctionTests(SimpleTestCase): self.assertEqual(floatformat(pos_inf), "inf") self.assertEqual(floatformat(neg_inf), "-inf") self.assertEqual(floatformat(pos_inf / pos_inf), "nan") + self.assertEqual(floatformat("inf"), "inf") + self.assertEqual(floatformat("NaN"), "NaN") + + def test_too_many_digits_to_render(self): + cases = [ + "1e200", + "1E200", + "1E10000000000000000", + "-1E10000000000000000", + "1e10000000000000000", + "-1e10000000000000000", + "1" + "0" * 1_000_000, + ] + for value in cases: + with self.subTest(value=value): + self.assertEqual(floatformat(value), value) def test_float_dunder_method(self): class FloatWrapper: