diff options
| -rw-r--r-- | django/utils/text.py | 14 | ||||
| -rw-r--r-- | docs/releases/4.2.28.txt | 14 | ||||
| -rw-r--r-- | tests/utils_tests/test_text.py | 10 |
3 files changed, 29 insertions, 9 deletions
diff --git a/django/utils/text.py b/django/utils/text.py index b018f2601f..694baf1ac3 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -272,15 +272,11 @@ class Truncator(SimpleLazyObject): if self_closing or tagname in html4_singlets: pass elif closing_tag: - # Check for match in open tags list - try: - i = open_tags.index(tagname) - except ValueError: - pass - else: - # SGML: An end tag closes, back to the matching start tag, - # all unclosed intervening start tags with omitted end tags - open_tags = open_tags[i + 1 :] + # Remove from the list only if the tag matches the most + # recently opened tag (LIFO). This avoids O(n) linear scans + # for unmatched end tags if `list.index()` would be called. + if open_tags and open_tags[0] == tagname: + open_tags = open_tags[1:] else: # Add it to the start of the open tags list open_tags.insert(0, tagname) diff --git a/docs/releases/4.2.28.txt b/docs/releases/4.2.28.txt index aa06882806..3c9529a4f8 100644 --- a/docs/releases/4.2.28.txt +++ b/docs/releases/4.2.28.txt @@ -41,3 +41,17 @@ As a reminder, all untrusted user input should be validated before use. This issue has severity "high" according to the :ref:`Django security policy <security-disclosure>`. +Django 4.2.28 fixes two security issues with severity "moderate", three +security issues with severity "moderate", and one security issue with severity +"low" in 4.2.27. + +CVE-2026-1285: Potential denial-of-service vulnerability in ``django.utils.text.Truncator`` HTML methods +======================================================================================================== + +``django.utils.text.Truncator.chars()`` and ``Truncator.words()`` methods (with +``html=True``) and the :tfilter:`truncatechars_html` and +:tfilter:`truncatewords_html` template filters were subject to a potential +denial-of-service attack via certain inputs with a large number of unmatched +HTML end tags, which could cause quadratic time complexity during HTML parsing. + +This issue has severity "moderate" according to the Django security policy. diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py index d1890e7b6d..5a018d7320 100644 --- a/tests/utils_tests/test_text.py +++ b/tests/utils_tests/test_text.py @@ -95,6 +95,16 @@ class TestUtilsText(SimpleTestCase): text.Truncator(lazystr("The quick brown fox")).chars(10), "The quick…" ) + def test_truncate_chars_html_with_misnested_tags(self): + # LIFO removal keeps all tags when a middle tag is closed out of order. + # With <a><b><c></b>, the </b> doesn't match <c>, so all tags remain + # in the stack and are properly closed at truncation. + truncator = text.Truncator("<a><b><c></b>XXXX") + self.assertEqual( + truncator.chars(2, html=True, truncate=""), + "<a><b><c></b>XX</c></b></a>", + ) + @patch("django.utils.text.Truncator.MAX_LENGTH_HTML", 10_000) def test_truncate_chars_html_size_limit(self): max_len = text.Truncator.MAX_LENGTH_HTML |
