diff options
| -rw-r--r-- | django/utils/text.py | 9 | ||||
| -rw-r--r-- | docs/releases/4.2.28.txt | 12 | ||||
| -rw-r--r-- | docs/releases/5.2.11.txt | 12 | ||||
| -rw-r--r-- | tests/utils_tests/test_text.py | 10 |
4 files changed, 39 insertions, 4 deletions
diff --git a/django/utils/text.py b/django/utils/text.py index 26edde99e3..21efb00b98 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -126,10 +126,11 @@ class TruncateHTMLParser(HTMLParser): def handle_endtag(self, tag): if tag not in self.void_elements: self.output += f"</{tag}>" - try: - self.tags.remove(tag) - except ValueError: - pass + # Remove from the stack only if the tag matches the most recently + # opened tag (LIFO). This avoids O(n) linear scans for unmatched + # end tags if `deque.remove()` would be called. + if self.tags and self.tags[0] == tag: + self.tags.popleft() def handle_data(self, data): data, output = self.process(data) diff --git a/docs/releases/4.2.28.txt b/docs/releases/4.2.28.txt index aa06882806..6ff358a8ec 100644 --- a/docs/releases/4.2.28.txt +++ b/docs/releases/4.2.28.txt @@ -41,3 +41,15 @@ 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>`. + +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 :ref:`Django security +policy <security-disclosure>`. diff --git a/docs/releases/5.2.11.txt b/docs/releases/5.2.11.txt index 73a0cd23b3..bc5fb02063 100644 --- a/docs/releases/5.2.11.txt +++ b/docs/releases/5.2.11.txt @@ -41,3 +41,15 @@ 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>`. + +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 :ref:`Django security +policy <security-disclosure>`. diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py index 63c7889cbc..11c01874cb 100644 --- a/tests/utils_tests/test_text.py +++ b/tests/utils_tests/test_text.py @@ -202,6 +202,16 @@ class TestUtilsText(SimpleTestCase): truncator = text.Truncator("<p>I <3 python, what about you?</p>") self.assertEqual("<p>I <3 python, wh…</p>", truncator.chars(16, html=True)) + 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>", + ) + def test_truncate_words(self): truncator = text.Truncator("The quick brown fox jumped over the lazy dog.") self.assertEqual( |
