summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/utils/text.py14
-rw-r--r--docs/releases/4.2.28.txt14
-rw-r--r--tests/utils_tests/test_text.py10
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