new file mode 100644
@@ -0,0 +1,149 @@
+From 072963e4c4d0b3a7a8c5412bc0c7d27d1a9c3521 Mon Sep 17 00:00:00 2001
+From: Shai Berger <shai@platonix.com>
+Date: Mon, 19 Feb 2024 13:56:37 +0100
+Subject: [PATCH] Fixed CVE-2024-27351 -- Prevented potential ReDoS in
+ Truncator.words().
+
+Thanks Seokchan Yoon for the report.
+
+CVE: CVE-2024-27351
+
+Upstream-Status: Backport
+https://github.com/django/django/commit/072963e4c4d0b3a7a8c5412bc0c7d27d1a9c3521
+
+Signed-off-by: Shai Berger <shai@platonix.com>
+Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
+Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com>
+---
+ django/utils/text.py | 57 ++++++++++++++++++++++++++++++++--
+ docs/releases/2.2.28.txt | 8 +++++
+ tests/utils_tests/test_text.py | 26 ++++++++++++++++
+ 3 files changed, 89 insertions(+), 2 deletions(-)
+
+diff --git a/django/utils/text.py b/django/utils/text.py
+index 06a377b..2c4040e 100644
+--- a/django/utils/text.py
++++ b/django/utils/text.py
+@@ -15,8 +15,61 @@ def capfirst(x):
+ return x and str(x)[0].upper() + str(x)[1:]
+
+
+-# Set up regular expressions
+-re_words = re.compile(r'<[^>]+?>|([^<>\s]+)', re.S)
++# ----- Begin security-related performance workaround -----
++
++# We used to have, below
++#
++# re_words = _lazy_re_compile(r"<[^>]+?>|([^<>\s]+)", re.S)
++#
++# But it was shown that this regex, in the way we use it here, has some
++# catastrophic edge-case performance features. Namely, when it is applied to
++# text with only open brackets "<<<...". The class below provides the services
++# and correct answers for the use cases, but in these edge cases does it much
++# faster.
++re_notag = _lazy_re_compile(r"([^<>\s]+)", re.S)
++re_prt = _lazy_re_compile(r"<|([^<>\s]+)", re.S)
++
++
++class WordsRegex:
++ @staticmethod
++ def search(text, pos):
++ # Look for "<" or a non-tag word.
++ partial = re_prt.search(text, pos)
++ if partial is None or partial[1] is not None:
++ return partial
++
++ # "<" was found, look for a closing ">".
++ end = text.find(">", partial.end(0))
++ if end < 0:
++ # ">" cannot be found, look for a word.
++ return re_notag.search(text, pos + 1)
++ else:
++ # "<" followed by a ">" was found -- fake a match.
++ end += 1
++ return FakeMatch(text[partial.start(0): end], end)
++
++
++class FakeMatch:
++ __slots__ = ["_text", "_end"]
++
++ def end(self, group=0):
++ assert group == 0, "This specific object takes only group=0"
++ return self._end
++
++ def __getitem__(self, group):
++ if group == 1:
++ return None
++ assert group == 0, "This specific object takes only group in {0,1}"
++ return self._text
++
++ def __init__(self, text, end):
++ self._text, self._end = text, end
++
++
++# ----- End security-related performance workaround -----
++
++# Set up regular expressions.
++re_words = WordsRegex
+ re_chars = re.compile(r'<[^>]+?>|(.)', re.S)
+ re_tag = re.compile(r'<(/)?(\S+?)(?:(\s*/)|\s.*?)?>', re.S)
+ re_newlines = re.compile(r'\r\n|\r') # Used in normalize_newlines
+diff --git a/docs/releases/2.2.28.txt b/docs/releases/2.2.28.txt
+index c653cb6..8f79fd0 100644
+--- a/docs/releases/2.2.28.txt
++++ b/docs/releases/2.2.28.txt
+@@ -6,6 +6,14 @@ Django 2.2.28 release notes
+
+ Django 2.2.28 fixes two security issues with severity "high" in 2.2.27.
+
++CVE-2024-27351: Potential regular expression denial-of-service in ``django.utils.text.Truncator.words()``
++=========================================================================================================
++
++``django.utils.text.Truncator.words()`` method (with ``html=True``) and
++:tfilter:`truncatewords_html` template filter were subject to a potential
++regular expression denial-of-service attack using a suitably crafted string
++(follow up to :cve:`2019-14232` and :cve:`2023-43665`).
++
+ CVE-2022-28346: Potential SQL injection in ``QuerySet.annotate()``, ``aggregate()``, and ``extra()``
+ ====================================================================================================
+
+diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py
+index cb3063d..7e9f2b3 100644
+--- a/tests/utils_tests/test_text.py
++++ b/tests/utils_tests/test_text.py
+@@ -156,6 +156,32 @@ class TestUtilsText(SimpleTestCase):
+ truncator = text.Truncator('<p>I <3 python, what about you?</p>')
+ self.assertEqual('<p>I <3 python,…</p>', truncator.words(3, html=True))
+
++ # Only open brackets.
++ test = "<" * 60_000
++ truncator = text.Truncator(test)
++ self.assertEqual(truncator.words(1, html=True), test)
++
++ # Tags with special chars in attrs.
++ truncator = text.Truncator(
++ """<i style="margin: 5%; font: *;">Hello, my dear lady!</i>"""
++ )
++ self.assertEqual(
++ """<i style="margin: 5%; font: *;">Hello, my dear…</i>""",
++ truncator.words(3, html=True),
++ )
++
++ # Tags with special non-latin chars in attrs.
++ truncator = text.Truncator("""<p data-x="א">Hello, my dear lady!</p>""")
++ self.assertEqual(
++ """<p data-x="א">Hello, my dear…</p>""",
++ truncator.words(3, html=True),
++ )
++
++ # Misplaced brackets.
++ truncator = text.Truncator("hello >< world")
++ self.assertEqual(truncator.words(1, html=True), "hello…")
++ self.assertEqual(truncator.words(2, html=True), "hello >< world")
++
+ @patch("django.utils.text.Truncator.MAX_LENGTH_HTML", 10_000)
+ def test_truncate_words_html_size_limit(self):
+ max_len = text.Truncator.MAX_LENGTH_HTML
+--
+2.35.5
+
new file mode 100644
@@ -0,0 +1,145 @@
+From 072963e4c4d0b3a7a8c5412bc0c7d27d1a9c3521 Mon Sep 17 00:00:00 2001
+From: Shai Berger <shai@platonix.com>
+Date: Mon, 19 Feb 2024 13:56:37 +0100
+Subject: [PATCH] Fixed CVE-2024-27351 -- Prevented potential ReDoS in
+ Truncator.words().
+
+Thanks Seokchan Yoon for the report.
+
+CVE: CVE-2024-27351
+
+Upstream-Status: Backport
+https://github.com/django/django/commit/072963e4c4d0b3a7a8c5412bc0c7d27d1a9c3521
+
+Signed-off-by: Shai Berger <shai@platonix.com>
+Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
+Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com>
+---
+ django/utils/text.py | 57 ++++++++++++++++++++++++++++++++--
+ docs/releases/3.2.23.txt | 8 +++++
+ tests/utils_tests/test_text.py | 26 ++++++++++++++++
+ 3 files changed, 89 insertions(+), 2 deletions(-)
+
+diff --git a/django/utils/text.py b/django/utils/text.py
+index 83e258f..88da9a2 100644
+--- a/django/utils/text.py
++++ b/django/utils/text.py
+@@ -18,8 +18,61 @@ def capfirst(x):
+ return x and str(x)[0].upper() + str(x)[1:]
+
+
+-# Set up regular expressions
+-re_words = _lazy_re_compile(r'<[^>]+?>|([^<>\s]+)', re.S)
++# ----- Begin security-related performance workaround -----
++
++# We used to have, below
++#
++# re_words = _lazy_re_compile(r"<[^>]+?>|([^<>\s]+)", re.S)
++#
++# But it was shown that this regex, in the way we use it here, has some
++# catastrophic edge-case performance features. Namely, when it is applied to
++# text with only open brackets "<<<...". The class below provides the services
++# and correct answers for the use cases, but in these edge cases does it much
++# faster.
++re_notag = _lazy_re_compile(r"([^<>\s]+)", re.S)
++re_prt = _lazy_re_compile(r"<|([^<>\s]+)", re.S)
++
++
++class WordsRegex:
++ @staticmethod
++ def search(text, pos):
++ # Look for "<" or a non-tag word.
++ partial = re_prt.search(text, pos)
++ if partial is None or partial[1] is not None:
++ return partial
++
++ # "<" was found, look for a closing ">".
++ end = text.find(">", partial.end(0))
++ if end < 0:
++ # ">" cannot be found, look for a word.
++ return re_notag.search(text, pos + 1)
++ else:
++ # "<" followed by a ">" was found -- fake a match.
++ end += 1
++ return FakeMatch(text[partial.start(0): end], end)
++
++
++class FakeMatch:
++ __slots__ = ["_text", "_end"]
++
++ def end(self, group=0):
++ assert group == 0, "This specific object takes only group=0"
++ return self._end
++
++ def __getitem__(self, group):
++ if group == 1:
++ return None
++ assert group == 0, "This specific object takes only group in {0,1}"
++ return self._text
++
++ def __init__(self, text, end):
++ self._text, self._end = text, end
++
++
++# ----- End security-related performance workaround -----
++
++# Set up regular expressions.
++re_words = WordsRegex
+ re_chars = _lazy_re_compile(r'<[^>]+?>|(.)', re.S)
+ re_tag = _lazy_re_compile(r'<(/)?(\S+?)(?:(\s*/)|\s.*?)?>', re.S)
+ re_newlines = _lazy_re_compile(r'\r\n|\r') # Used in normalize_newlines
+diff --git a/docs/releases/3.2.23.txt b/docs/releases/3.2.23.txt
+index ba23d11..dd9d68a 100644
+--- a/docs/releases/3.2.23.txt
++++ b/docs/releases/3.2.23.txt
+@@ -17,3 +17,11 @@ large number of Unicode characters.
+ In order to avoid the vulnerability, invalid values longer than
+ ``UsernameField.max_length`` are no longer normalized, since they cannot pass
+ validation anyway.
++
++CVE-2024-27351: Potential regular expression denial-of-service in ``django.utils.text.Truncator.words()``
++=========================================================================================================
++
++``django.utils.text.Truncator.words()`` method (with ``html=True``) and
++:tfilter:`truncatewords_html` template filter were subject to a potential
++regular expression denial-of-service attack using a suitably crafted string
++(follow up to :cve:`2019-14232` and :cve:`2023-43665`).
+diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py
+index 0a6f0bc..758919c 100644
+--- a/tests/utils_tests/test_text.py
++++ b/tests/utils_tests/test_text.py
+@@ -159,6 +159,32 @@ class TestUtilsText(SimpleTestCase):
+ truncator = text.Truncator('<p>I <3 python, what about you?</p>')
+ self.assertEqual('<p>I <3 python,…</p>', truncator.words(3, html=True))
+
++ # Only open brackets.
++ test = "<" * 60_000
++ truncator = text.Truncator(test)
++ self.assertEqual(truncator.words(1, html=True), test)
++
++ # Tags with special chars in attrs.
++ truncator = text.Truncator(
++ """<i style="margin: 5%; font: *;">Hello, my dear lady!</i>"""
++ )
++ self.assertEqual(
++ """<i style="margin: 5%; font: *;">Hello, my dear…</i>""",
++ truncator.words(3, html=True),
++ )
++
++ # Tags with special non-latin chars in attrs.
++ truncator = text.Truncator("""<p data-x="א">Hello, my dear lady!</p>""")
++ self.assertEqual(
++ """<p data-x="א">Hello, my dear…</p>""",
++ truncator.words(3, html=True),
++ )
++
++ # Misplaced brackets.
++ truncator = text.Truncator("hello >< world")
++ self.assertEqual(truncator.words(1, html=True), "hello…")
++ self.assertEqual(truncator.words(2, html=True), "hello >< world")
++
+ @patch("django.utils.text.Truncator.MAX_LENGTH_HTML", 10_000)
+ def test_truncate_words_html_size_limit(self):
+ max_len = text.Truncator.MAX_LENGTH_HTML
+--
+2.35.5
@@ -24,6 +24,7 @@ SRC_URI += "file://CVE-2023-31047.patch \
file://CVE-2024-45230.patch \
file://CVE-2024-45231.patch \
file://CVE-2024-53907.patch \
+ file://CVE-2024-27351.patch \
"
SRC_URI[sha256sum] = "0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413"
@@ -6,6 +6,7 @@ SRC_URI[sha256sum] = "82968f3640e29ef4a773af2c28448f5f7a08d001c6ac05b32d02aeee65
RDEPENDS:${PN} += "\
${PYTHON_PN}-sqlparse \
"
+SRC_URI += "file://CVE-2024-27351.patch"
# Set DEFAULT_PREFERENCE so that the LTS version of django is built by
# default. To build the 3.x branch,
Reference: https://nvd.nist.gov/vuln/detail/CVE-2024-27351 Upstream-patch: https://github.com/django/django/commit/072963e4c4d0b3a7a8c5412bc0c7d27d1a9c3521 Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com> --- .../CVE-2024-27351.patch | 149 ++++++++++++++++++ .../CVE-2024-27351.patch | 145 +++++++++++++++++ .../python/python3-django_2.2.28.bb | 1 + .../python/python3-django_3.2.23.bb | 1 + 4 files changed, 296 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-django-2.2.28/CVE-2024-27351.patch create mode 100644 meta-python/recipes-devtools/python/python3-django-3.2.23/CVE-2024-27351.patch