Skip to content

Commit 8dbb44d

Browse files
committed
[5.1.x] Fixed CVE-2025-26699 -- Mitigated potential DoS in wordwrap template filter.
Thanks sw0rd1ight for the report. Backport of 55d89e2 from main.
1 parent d7dc1f6 commit 8dbb44d

File tree

5 files changed

+39
-18
lines changed

5 files changed

+39
-18
lines changed

django/utils/text.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import gzip
22
import re
33
import secrets
4+
import textwrap
45
import unicodedata
56
from collections import deque
67
from gzip import GzipFile
@@ -49,24 +50,15 @@ def wrap(text, width):
4950
``width``.
5051
"""
5152

52-
def _generator():
53-
for line in text.splitlines(True): # True keeps trailing linebreaks
54-
max_width = min((line.endswith("\n") and width + 1 or width), width)
55-
while len(line) > max_width:
56-
space = line[: max_width + 1].rfind(" ") + 1
57-
if space == 0:
58-
space = line.find(" ") + 1
59-
if space == 0:
60-
yield line
61-
line = ""
62-
break
63-
yield "%s\n" % line[: space - 1]
64-
line = line[space:]
65-
max_width = min((line.endswith("\n") and width + 1 or width), width)
66-
if line:
67-
yield line
68-
69-
return "".join(_generator())
53+
wrapper = textwrap.TextWrapper(
54+
width=width,
55+
break_long_words=False,
56+
break_on_hyphens=False,
57+
)
58+
result = []
59+
for line in text.splitlines(True):
60+
result.extend(wrapper.wrap(line))
61+
return "\n".join(result)
7062

7163

7264
def add_truncation_text(text, truncate=None):

docs/releases/4.2.20.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ Django 4.2.20 release notes
55
*March 6, 2025*
66

77
Django 4.2.20 fixes a security issue with severity "moderate" in 4.2.19.
8+
9+
CVE-2025-26699: Potential denial-of-service vulnerability in ``django.utils.text.wrap()``
10+
=========================================================================================
11+
12+
The ``wrap()`` and :tfilter:`wordwrap` template filter were subject to a
13+
potential denial-of-service attack when used with very long strings.

docs/releases/5.0.13.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ Django 5.0.13 release notes
55
*March 6, 2025*
66

77
Django 5.0.13 fixes a security issue with severity "moderate" in 5.0.12.
8+
9+
CVE-2025-26699: Potential denial-of-service vulnerability in ``django.utils.text.wrap()``
10+
=========================================================================================
11+
12+
The ``wrap()`` and :tfilter:`wordwrap` template filter were subject to a
13+
potential denial-of-service attack when used with very long strings.

docs/releases/5.1.7.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ Django 5.1.7 release notes
77
Django 5.1.7 fixes a security issue with severity "moderate" and several bugs
88
in 5.1.6.
99

10+
CVE-2025-26699: Potential denial-of-service vulnerability in ``django.utils.text.wrap()``
11+
=========================================================================================
12+
13+
The ``wrap()`` and :tfilter:`wordwrap` template filter were subject to a
14+
potential denial-of-service attack when used with very long strings.
15+
1016
Bugfixes
1117
========
1218

tests/template_tests/filter_tests/test_wordwrap.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,14 @@ def test_wrap_lazy_string(self):
7878
"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\n"
7979
"I'm afraid",
8080
)
81+
82+
def test_wrap_long_text(self):
83+
long_text = (
84+
"this is a long paragraph of text that really needs"
85+
" to be wrapped I'm afraid " * 20_000
86+
)
87+
self.assertIn(
88+
"this is a\nlong\nparagraph\nof text\nthat\nreally\nneeds to\nbe wrapped\n"
89+
"I'm afraid",
90+
wordwrap(long_text, 10),
91+
)

0 commit comments

Comments
 (0)