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. Thanks Elias Myllymäki for the report. Co-authored-by: Shai Berger CVE: CVE-2024-41989 Upstream-Status: Backport [https://github.com/django/django/commit/fc76660f589ac07e45e9cd34ccb8087aeb11904b] Signed-off-by: Soumya Sambu --- django/template/defaultfilters.py | 13 +++++++++++++ .../filter_tests/test_floatformat.py | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index a1d77f5..4884852 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -135,6 +135,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/tests/template_tests/filter_tests/test_floatformat.py b/tests/template_tests/filter_tests/test_floatformat.py index cfc3eaf..bd0a998 100644 --- a/tests/template_tests/filter_tests/test_floatformat.py +++ b/tests/template_tests/filter_tests/test_floatformat.py @@ -55,6 +55,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_zero_values(self): self.assertEqual(floatformat(0, 6), '0.000000') @@ -68,6 +69,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: -- 2.40.0