diff options
| author | Natalia <124304+nessita@users.noreply.github.com> | 2026-01-21 09:53:10 -0300 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-02-03 08:15:39 -0500 |
| commit | 9f2ada875bbee62ac46032e38ddb22755d67ae5a (patch) | |
| tree | 4e8cc9c58caa63ea928c24721aad30e0804450e8 /django | |
| parent | 17a1d64a58ef24c0c3b78d66d86f5415075f18f0 (diff) | |
[5.2.x] Fixed CVE-2026-1285 -- Mitigated potential DoS in django.utils.text.Truncator for HTML input.
The `TruncateHTMLParser` used `deque.remove()` to remove tags from the
stack when processing end tags. With crafted input containing many
unmatched end tags, this caused repeated full scans of the tag stack,
leading to quadratic time complexity.
The fix uses LIFO semantics, only removing a tag from the stack when it
matches the most recently opened tag. This avoids linear scans for
unmatched end tags and reduces complexity to linear time.
Refs #30686 and 6ee37ada3241ed263d8d1c2901b030d964cbd161.
Thanks Seokchan Yoon for the report, and Jake Howard and Jacob Walls for
reviews.
Backport of a33540b3e20b5d759aa8b2e4b9ca0e8edd285344 from main.
Diffstat (limited to 'django')
| -rw-r--r-- | django/utils/text.py | 9 |
1 files changed, 5 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) |
