diff -pruN 3:4.2.18-1/debian/changelog 3:4.2.18-1ubuntu1.1/debian/changelog
--- 3:4.2.18-1/debian/changelog	2025-01-15 17:38:10.000000000 +0000
+++ 3:4.2.18-1ubuntu1.1/debian/changelog	2025-04-30 14:30:41.000000000 +0000
@@ -1,3 +1,21 @@
+python-django (3:4.2.18-1ubuntu1.1) plucky-security; urgency=medium
+
+  * SECURITY UPDATE: Denial of service in strip_tags()
+    - debian/patches/CVE-2025-32873.patch: check tag depth in
+      django/utils/html.py, tests/utils_tests/test_html.py.
+    - CVE-2025-32873
+
+ -- Marc Deslauriers <marc.deslauriers@ubuntu.com>  Wed, 30 Apr 2025 10:30:41 -0400
+
+python-django (3:4.2.18-1ubuntu1) plucky; urgency=medium
+
+  * SECURITY UPDATE: Denial of service.
+    - debian/patches/CVE-2025-26699.patch: Change wrap to use textwrap library
+      in ./django/utils/text.py.
+    - CVE-2025-26699
+
+ -- Hlib Korzhynskyy <hlib.korzhynskyy@canonical.com>  Fri, 07 Mar 2025 09:49:59 -0330
+
 python-django (3:4.2.18-1) unstable; urgency=high
 
   * New upstream security release. (Closes: #1093049)
diff -pruN 3:4.2.18-1/debian/control 3:4.2.18-1ubuntu1.1/debian/control
--- 3:4.2.18-1/debian/control	2025-01-15 17:38:10.000000000 +0000
+++ 3:4.2.18-1ubuntu1.1/debian/control	2025-03-07 13:19:59.000000000 +0000
@@ -1,7 +1,8 @@
 Source: python-django
 Section: python
 Priority: optional
-Maintainer: Debian Python Team <team+python@tracker.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian Python Team <team+python@tracker.debian.org>
 Uploaders:
  Luke Faraone <lfaraone@debian.org>,
  Raphaël Hertzog <hertzog@debian.org>,
diff -pruN 3:4.2.18-1/debian/patches/CVE-2025-26699.patch 3:4.2.18-1ubuntu1.1/debian/patches/CVE-2025-26699.patch
--- 3:4.2.18-1/debian/patches/CVE-2025-26699.patch	1970-01-01 00:00:00.000000000 +0000
+++ 3:4.2.18-1ubuntu1.1/debian/patches/CVE-2025-26699.patch	2025-03-07 13:19:59.000000000 +0000
@@ -0,0 +1,82 @@
+Backport of:
+
+From c8efec38271ffae122e427161078ebc605616c5d Mon Sep 17 00:00:00 2001
+From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
+Date: Tue, 25 Feb 2025 09:40:54 +0100
+Subject: [PATCH] [4.2.x] Fixed CVE-2025-26699 -- Mitigated potential DoS in
+ wordwrap template filter.
+
+Thanks sw0rd1ight for the report.
+
+Backport of 2d54d7b0e53c3bb4eb6b2b234596c6ec19fb76bf from main.
+---
+ django/utils/text.py                          | 28 +++++++------------
+ docs/releases/4.2.20.txt                      |  6 ++++
+ .../filter_tests/test_wordwrap.py             | 11 ++++++++
+ 3 files changed, 27 insertions(+), 18 deletions(-)
+
+Index: python-django-4.2.18/django/utils/text.py
+===================================================================
+--- python-django-4.2.18.orig/django/utils/text.py
++++ python-django-4.2.18/django/utils/text.py
+@@ -1,6 +1,7 @@
+ import gzip
+ import re
+ import secrets
++import textwrap
+ import unicodedata
+ from gzip import GzipFile
+ from gzip import compress as gzip_compress
+@@ -97,24 +98,15 @@ def wrap(text, width):
+     ``width``.
+     """
+ 
+-    def _generator():
+-        for line in text.splitlines(True):  # True keeps trailing linebreaks
+-            max_width = min((line.endswith("\n") and width + 1 or width), width)
+-            while len(line) > max_width:
+-                space = line[: max_width + 1].rfind(" ") + 1
+-                if space == 0:
+-                    space = line.find(" ") + 1
+-                    if space == 0:
+-                        yield line
+-                        line = ""
+-                        break
+-                yield "%s\n" % line[: space - 1]
+-                line = line[space:]
+-                max_width = min((line.endswith("\n") and width + 1 or width), width)
+-            if line:
+-                yield line
+-
+-    return "".join(_generator())
++    wrapper = textwrap.TextWrapper(
++        width=width,
++        break_long_words=False,
++        break_on_hyphens=False,
++    )
++    result = []
++    for line in text.splitlines(True):
++        result.extend(wrapper.wrap(line))
++    return "\n".join(result)
+ 
+ 
+ class Truncator(SimpleLazyObject):
+Index: python-django-4.2.18/tests/template_tests/filter_tests/test_wordwrap.py
+===================================================================
+--- python-django-4.2.18.orig/tests/template_tests/filter_tests/test_wordwrap.py
++++ python-django-4.2.18/tests/template_tests/filter_tests/test_wordwrap.py
+@@ -78,3 +78,14 @@ class FunctionTests(SimpleTestCase):
+             "this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\n"
+             "I'm afraid",
+         )
++
++    def test_wrap_long_text(self):
++        long_text = (
++            "this is a long paragraph of text that really needs"
++            " to be wrapped I'm afraid " * 20_000
++        )
++        self.assertIn(
++            "this is a\nlong\nparagraph\nof text\nthat\nreally\nneeds to\nbe wrapped\n"
++            "I'm afraid",
++            wordwrap(long_text, 10),
++        )
diff -pruN 3:4.2.18-1/debian/patches/CVE-2025-32873.patch 3:4.2.18-1ubuntu1.1/debian/patches/CVE-2025-32873.patch
--- 3:4.2.18-1/debian/patches/CVE-2025-32873.patch	1970-01-01 00:00:00.000000000 +0000
+++ 3:4.2.18-1ubuntu1.1/debian/patches/CVE-2025-32873.patch	2025-04-30 14:30:04.000000000 +0000
@@ -0,0 +1,107 @@
+From 9cc5f9ff937463c3a2fd8ebac42c8392ad1986da Mon Sep 17 00:00:00 2001
+From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
+Date: Tue, 8 Apr 2025 16:30:17 +0200
+Subject: [PATCH] [4.2.x] Fixed CVE-2025-32873 -- Mitigated potential DoS in
+ strip_tags().
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Thanks to Elias Myllymäki for the report, and Shai Berger and Jake
+Howard for the reviews.
+
+Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
+---
+ django/utils/html.py           |  6 ++++++
+ docs/releases/4.2.21.txt       | 14 +++++++++++++-
+ tests/utils_tests/test_html.py | 15 ++++++++++++++-
+ 3 files changed, 33 insertions(+), 2 deletions(-)
+
+#diff --git a/docs/releases/4.2.21.txt b/docs/releases/4.2.21.txt
+#index 1064dcf202..cc39105a01 100644
+#--- a/docs/releases/4.2.21.txt
+#+++ b/docs/releases/4.2.21.txt
+#@@ -4,7 +4,19 @@ Django 4.2.21 release notes
+# 
+# *Expected May 7, 2025*
+# 
+#-Django 4.2.21 fixes a data loss bug and a regression in 4.2.20.
+#+Django 4.2.21 fixes a security issue with severity "moderate", a data loss bug,
+#+and a regression in 4.2.20.
+#+
+#+CVE-2025-32873: Denial-of-service possibility in ``strip_tags()``
+#+=================================================================
+#+
+#+:func:`~django.utils.html.strip_tags` would be slow to evaluate certain inputs
+#+containing large sequences of incomplete HTML tags. This function is used to
+#+implement the :tfilter:`striptags` template filter, which was thus also
+#+vulnerable.
+#+
+#+:func:`~django.utils.html.strip_tags` now raises a :exc:`.SuspiciousOperation`
+#+exception if it encounters an unusually large number of unclosed opening tags.
+# 
+# Bugfixes
+# ========
+diff --git a/django/utils/html.py b/django/utils/html.py
+index a3a7238cba..84c37d1186 100644
+--- a/django/utils/html.py
++++ b/django/utils/html.py
+@@ -17,6 +17,9 @@ from django.utils.text import normalize_newlines
+ MAX_URL_LENGTH = 2048
+ MAX_STRIP_TAGS_DEPTH = 50
+ 
++# HTML tag that opens but has no closing ">" after 1k+ chars.
++long_open_tag_without_closing_re = _lazy_re_compile(r"<[a-zA-Z][^>]{1000,}")
++
+ 
+ @keep_lazy(SafeString)
+ def escape(text):
+@@ -175,6 +178,9 @@ def _strip_once(value):
+ def strip_tags(value):
+     """Return the given HTML with all tags stripped."""
+     value = str(value)
++    for long_open_tag in long_open_tag_without_closing_re.finditer(value):
++        if long_open_tag.group().count("<") >= MAX_STRIP_TAGS_DEPTH:
++            raise SuspiciousOperation
+     # Note: in typical case this loop executes _strip_once twice (the second
+     # execution does not remove any more tags).
+     strip_tags_depth = 0
+diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py
+index 579bb2a1e3..25168e2348 100644
+--- a/tests/utils_tests/test_html.py
++++ b/tests/utils_tests/test_html.py
+@@ -115,17 +115,30 @@ class TestUtilsHtml(SimpleTestCase):
+             ("><!" + ("&" * 16000) + "D", "><!" + ("&" * 16000) + "D"),
+             ("X<<<<br>br>br>br>X", "XX"),
+             ("<" * 50 + "a>" * 50, ""),
++            (">" + "<a" * 500 + "a", ">" + "<a" * 500 + "a"),
++            ("<a" * 49 + "a" * 951, "<a" * 49 + "a" * 951),
++            ("<" + "a" * 1_002, "<" + "a" * 1_002),
+         )
+         for value, output in items:
+             with self.subTest(value=value, output=output):
+                 self.check_output(strip_tags, value, output)
+                 self.check_output(strip_tags, lazystr(value), output)
+ 
+-    def test_strip_tags_suspicious_operation(self):
++    def test_strip_tags_suspicious_operation_max_depth(self):
+         value = "<" * 51 + "a>" * 51, "<a>"
+         with self.assertRaises(SuspiciousOperation):
+             strip_tags(value)
+ 
++    def test_strip_tags_suspicious_operation_large_open_tags(self):
++        items = [
++            ">" + "<a" * 501,
++            "<a" * 50 + "a" * 950,
++        ]
++        for value in items:
++            with self.subTest(value=value):
++                with self.assertRaises(SuspiciousOperation):
++                    strip_tags(value)
++
+     def test_strip_tags_files(self):
+         # Test with more lengthy content (also catching performance regressions)
+         for filename in ("strip_tags1.html", "strip_tags2.txt"):
+-- 
+2.43.0
+
diff -pruN 3:4.2.18-1/debian/patches/series 3:4.2.18-1ubuntu1.1/debian/patches/series
--- 3:4.2.18-1/debian/patches/series	2025-01-15 17:38:10.000000000 +0000
+++ 3:4.2.18-1ubuntu1.1/debian/patches/series	2025-04-30 14:29:34.000000000 +0000
@@ -3,3 +3,5 @@
 0004-Use-locally-installed-documentation-sources.patch
 0004-Set-the-default-shebang-to-new-projects-to-use-Pytho.patch
 py313-test-help-default-options-with-custom-arguments.patch
+CVE-2025-26699.patch
+CVE-2025-32873.patch
