Sync from SUSE:SLFO:Main python-Django revision 2bd7ba1c498ee607b8fe04ff26398472
This commit is contained in:
parent
e1462fda93
commit
237f286c53
@ -1,121 +0,0 @@
|
||||
From 2d173757922183f7e9b79d31fd4ccd9086cc6ce2 Mon Sep 17 00:00:00 2001
|
||||
From: Shai Berger <shai@platonix.com>
|
||||
Date: Mon, 19 Feb 2024 13:56:37 +0100
|
||||
Subject: [PATCH] [4.2.x] Fixed CVE-2024-27351 -- Prevented potential ReDoS in
|
||||
Truncator.words().
|
||||
|
||||
Thanks Seokchan Yoon for the report.
|
||||
|
||||
Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
|
||||
---
|
||||
django/utils/text.py | 57 ++++++++++++++++++++++++++++++++--
|
||||
docs/releases/3.2.25.txt | 8 +++++
|
||||
docs/releases/4.2.11.txt | 8 +++++
|
||||
tests/utils_tests/test_text.py | 26 ++++++++++++++++
|
||||
4 files changed, 97 insertions(+), 2 deletions(-)
|
||||
|
||||
Index: Django-4.2.6/django/utils/text.py
|
||||
===================================================================
|
||||
--- Django-4.2.6.orig/django/utils/text.py
|
||||
+++ Django-4.2.6/django/utils/text.py
|
||||
@@ -23,8 +23,61 @@ def capfirst(x):
|
||||
return x[0].upper() + 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
|
||||
Index: Django-4.2.6/tests/utils_tests/test_text.py
|
||||
===================================================================
|
||||
--- Django-4.2.6.orig/tests/utils_tests/test_text.py
|
||||
+++ Django-4.2.6/tests/utils_tests/test_text.py
|
||||
@@ -183,6 +183,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
|
164
CVE-2024-38875.patch
Normal file
164
CVE-2024-38875.patch
Normal file
@ -0,0 +1,164 @@
|
||||
From 79f368764295df109a37192f6182fb6f361d85b5 Mon Sep 17 00:00:00 2001
|
||||
From: Adam Johnson <me@adamj.eu>
|
||||
Date: Mon, 24 Jun 2024 15:30:59 +0200
|
||||
Subject: [PATCH] [4.2.x] Fixed CVE-2024-38875 -- Mitigated potential DoS in
|
||||
urlize and urlizetrunc template filters.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Thank you to Elias Myllymäki for the report.
|
||||
|
||||
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
---
|
||||
django/utils/html.py | 90 +++++++++++++++++++++++++---------
|
||||
tests/utils_tests/test_html.py | 7 +++
|
||||
2 files changed, 73 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/django/utils/html.py b/django/utils/html.py
|
||||
index fdb88d6709..fd313ff9ca 100644
|
||||
--- a/django/utils/html.py
|
||||
+++ b/django/utils/html.py
|
||||
@@ -7,7 +7,7 @@ from html.parser import HTMLParser
|
||||
from urllib.parse import parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit
|
||||
|
||||
from django.utils.encoding import punycode
|
||||
-from django.utils.functional import Promise, keep_lazy, keep_lazy_text
|
||||
+from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
|
||||
from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
from django.utils.safestring import SafeData, SafeString, mark_safe
|
||||
@@ -225,6 +225,16 @@ def smart_urlquote(url):
|
||||
return urlunsplit((scheme, netloc, path, query, fragment))
|
||||
|
||||
|
||||
+class CountsDict(dict):
|
||||
+ def __init__(self, *args, word, **kwargs):
|
||||
+ super().__init__(*args, *kwargs)
|
||||
+ self.word = word
|
||||
+
|
||||
+ def __missing__(self, key):
|
||||
+ self[key] = self.word.count(key)
|
||||
+ return self[key]
|
||||
+
|
||||
+
|
||||
class Urlizer:
|
||||
"""
|
||||
Convert any URLs in text into clickable links.
|
||||
@@ -330,40 +340,72 @@ class Urlizer:
|
||||
return x
|
||||
return "%s…" % x[: max(0, limit - 1)]
|
||||
|
||||
+ @cached_property
|
||||
+ def wrapping_punctuation_openings(self):
|
||||
+ return "".join(dict(self.wrapping_punctuation).keys())
|
||||
+
|
||||
+ @cached_property
|
||||
+ def trailing_punctuation_chars_no_semicolon(self):
|
||||
+ return self.trailing_punctuation_chars.replace(";", "")
|
||||
+
|
||||
+ @cached_property
|
||||
+ def trailing_punctuation_chars_has_semicolon(self):
|
||||
+ return ";" in self.trailing_punctuation_chars
|
||||
+
|
||||
def trim_punctuation(self, word):
|
||||
"""
|
||||
Trim trailing and wrapping punctuation from `word`. Return the items of
|
||||
the new state.
|
||||
"""
|
||||
- lead, middle, trail = "", word, ""
|
||||
+ # Strip all opening wrapping punctuation.
|
||||
+ middle = word.lstrip(self.wrapping_punctuation_openings)
|
||||
+ lead = word[: len(word) - len(middle)]
|
||||
+ trail = ""
|
||||
+
|
||||
# Continue trimming until middle remains unchanged.
|
||||
trimmed_something = True
|
||||
- while trimmed_something:
|
||||
+ counts = CountsDict(word=middle)
|
||||
+ while trimmed_something and middle:
|
||||
trimmed_something = False
|
||||
# Trim wrapping punctuation.
|
||||
for opening, closing in self.wrapping_punctuation:
|
||||
- if middle.startswith(opening):
|
||||
- middle = middle[len(opening) :]
|
||||
- lead += opening
|
||||
- trimmed_something = True
|
||||
- # Keep parentheses at the end only if they're balanced.
|
||||
- if (
|
||||
- middle.endswith(closing)
|
||||
- and middle.count(closing) == middle.count(opening) + 1
|
||||
- ):
|
||||
- middle = middle[: -len(closing)]
|
||||
- trail = closing + trail
|
||||
- trimmed_something = True
|
||||
- # Trim trailing punctuation (after trimming wrapping punctuation,
|
||||
- # as encoded entities contain ';'). Unescape entities to avoid
|
||||
- # breaking them by removing ';'.
|
||||
- middle_unescaped = html.unescape(middle)
|
||||
- stripped = middle_unescaped.rstrip(self.trailing_punctuation_chars)
|
||||
- if middle_unescaped != stripped:
|
||||
- punctuation_count = len(middle_unescaped) - len(stripped)
|
||||
- trail = middle[-punctuation_count:] + trail
|
||||
- middle = middle[:-punctuation_count]
|
||||
+ if counts[opening] < counts[closing]:
|
||||
+ rstripped = middle.rstrip(closing)
|
||||
+ if rstripped != middle:
|
||||
+ strip = counts[closing] - counts[opening]
|
||||
+ trail = middle[-strip:]
|
||||
+ middle = middle[:-strip]
|
||||
+ trimmed_something = True
|
||||
+ counts[closing] -= strip
|
||||
+
|
||||
+ rstripped = middle.rstrip(self.trailing_punctuation_chars_no_semicolon)
|
||||
+ if rstripped != middle:
|
||||
+ trail = middle[len(rstripped) :] + trail
|
||||
+ middle = rstripped
|
||||
trimmed_something = True
|
||||
+
|
||||
+ if self.trailing_punctuation_chars_has_semicolon and middle.endswith(";"):
|
||||
+ # Only strip if not part of an HTML entity.
|
||||
+ amp = middle.rfind("&")
|
||||
+ if amp == -1:
|
||||
+ can_strip = True
|
||||
+ else:
|
||||
+ potential_entity = middle[amp:]
|
||||
+ escaped = html.unescape(potential_entity)
|
||||
+ can_strip = (escaped == potential_entity) or escaped.endswith(";")
|
||||
+
|
||||
+ if can_strip:
|
||||
+ rstripped = middle.rstrip(";")
|
||||
+ amount_stripped = len(middle) - len(rstripped)
|
||||
+ if amp > -1 and amount_stripped > 1:
|
||||
+ # Leave a trailing semicolon as might be an entity.
|
||||
+ trail = middle[len(rstripped) + 1 :] + trail
|
||||
+ middle = rstripped + ";"
|
||||
+ else:
|
||||
+ trail = middle[len(rstripped) :] + trail
|
||||
+ middle = rstripped
|
||||
+ trimmed_something = True
|
||||
+
|
||||
return lead, middle, trail
|
||||
|
||||
@staticmethod
|
||||
diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py
|
||||
index b7a7396075..6dab41634a 100644
|
||||
--- a/tests/utils_tests/test_html.py
|
||||
+++ b/tests/utils_tests/test_html.py
|
||||
@@ -342,6 +342,13 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
"foo@.example.com",
|
||||
"foo@localhost",
|
||||
"foo@localhost.",
|
||||
+ # trim_punctuation catastrophic tests
|
||||
+ "(" * 100_000 + ":" + ")" * 100_000,
|
||||
+ "(" * 100_000 + "&:" + ")" * 100_000,
|
||||
+ "([" * 100_000 + ":" + "])" * 100_000,
|
||||
+ "[(" * 100_000 + ":" + ")]" * 100_000,
|
||||
+ "([[" * 100_000 + ":" + "]])" * 100_000,
|
||||
+ "&:" + ";" * 100_000,
|
||||
)
|
||||
for value in tests:
|
||||
with self.subTest(value=value):
|
||||
--
|
||||
2.45.2
|
||||
|
87
CVE-2024-39329.patch
Normal file
87
CVE-2024-39329.patch
Normal file
@ -0,0 +1,87 @@
|
||||
From 156d3186c96e3ec2ca73b8b25dc2ef366e38df14 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Manfre <mike@manfre.net>
|
||||
Date: Fri, 14 Jun 2024 22:12:58 -0400
|
||||
Subject: [PATCH] [4.2.x] Fixed CVE-2024-39329 -- Standarized timing of
|
||||
verify_password() when checking unusuable passwords.
|
||||
|
||||
Refs #20760.
|
||||
|
||||
Thanks Michael Manfre for the fix and to Adam Johnson for the review.
|
||||
---
|
||||
django/contrib/auth/hashers.py | 10 ++++++++--
|
||||
tests/auth_tests/test_hashers.py | 32 ++++++++++++++++++++++++++++++++
|
||||
2 files changed, 40 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
|
||||
index 9db5a12e13..f11bc8a0f3 100644
|
||||
--- a/django/contrib/auth/hashers.py
|
||||
+++ b/django/contrib/auth/hashers.py
|
||||
@@ -43,14 +43,20 @@ def check_password(password, encoded, setter=None, preferred="default"):
|
||||
If setter is specified, it'll be called when you need to
|
||||
regenerate the password.
|
||||
"""
|
||||
- if password is None or not is_password_usable(encoded):
|
||||
- return False
|
||||
+ fake_runtime = password is None or not is_password_usable(encoded)
|
||||
|
||||
preferred = get_hasher(preferred)
|
||||
try:
|
||||
hasher = identify_hasher(encoded)
|
||||
except ValueError:
|
||||
# encoded is gibberish or uses a hasher that's no longer installed.
|
||||
+ fake_runtime = True
|
||||
+
|
||||
+ if fake_runtime:
|
||||
+ # Run the default password hasher once to reduce the timing difference
|
||||
+ # between an existing user with an unusable password and a nonexistent
|
||||
+ # user or missing hasher (similar to #20760).
|
||||
+ make_password(get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH))
|
||||
return False
|
||||
|
||||
hasher_changed = hasher.algorithm != preferred.algorithm
|
||||
diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py
|
||||
index 36f22d5f09..3da495f2be 100644
|
||||
--- a/tests/auth_tests/test_hashers.py
|
||||
+++ b/tests/auth_tests/test_hashers.py
|
||||
@@ -613,6 +613,38 @@ class TestUtilsHashPass(SimpleTestCase):
|
||||
check_password("wrong_password", encoded)
|
||||
self.assertEqual(hasher.harden_runtime.call_count, 1)
|
||||
|
||||
+ def test_check_password_calls_make_password_to_fake_runtime(self):
|
||||
+ hasher = get_hasher("default")
|
||||
+ cases = [
|
||||
+ (None, None, None), # no plain text password provided
|
||||
+ ("foo", make_password(password=None), None), # unusable encoded
|
||||
+ ("letmein", make_password(password="letmein"), ValueError), # valid encoded
|
||||
+ ]
|
||||
+ for password, encoded, hasher_side_effect in cases:
|
||||
+ with (
|
||||
+ self.subTest(encoded=encoded),
|
||||
+ mock.patch(
|
||||
+ "django.contrib.auth.hashers.identify_hasher",
|
||||
+ side_effect=hasher_side_effect,
|
||||
+ ) as mock_identify_hasher,
|
||||
+ mock.patch(
|
||||
+ "django.contrib.auth.hashers.make_password"
|
||||
+ ) as mock_make_password,
|
||||
+ mock.patch(
|
||||
+ "django.contrib.auth.hashers.get_random_string",
|
||||
+ side_effect=lambda size: "x" * size,
|
||||
+ ),
|
||||
+ mock.patch.object(hasher, "verify"),
|
||||
+ ):
|
||||
+ # Ensure make_password is called to standardize timing.
|
||||
+ check_password(password, encoded)
|
||||
+ self.assertEqual(hasher.verify.call_count, 0)
|
||||
+ self.assertEqual(mock_identify_hasher.mock_calls, [mock.call(encoded)])
|
||||
+ self.assertEqual(
|
||||
+ mock_make_password.mock_calls,
|
||||
+ [mock.call("x" * UNUSABLE_PASSWORD_SUFFIX_LENGTH)],
|
||||
+ )
|
||||
+
|
||||
def test_encode_invalid_salt(self):
|
||||
hasher_classes = [
|
||||
MD5PasswordHasher,
|
||||
--
|
||||
2.45.2
|
||||
|
180
CVE-2024-39330.patch
Normal file
180
CVE-2024-39330.patch
Normal file
@ -0,0 +1,180 @@
|
||||
From 2b00edc0151a660d1eb86da4059904a0fc4e095e Mon Sep 17 00:00:00 2001
|
||||
From: Natalia <124304+nessita@users.noreply.github.com>
|
||||
Date: Wed, 20 Mar 2024 13:55:21 -0300
|
||||
Subject: [PATCH] [4.2.x] Fixed CVE-2024-39330 -- Added extra file name
|
||||
validation in Storage's save method.
|
||||
|
||||
Thanks to Josh Schneier for the report, and to Carlton Gibson and Sarah
|
||||
Boyce for the reviews.
|
||||
---
|
||||
django/core/files/storage/base.py | 11 +++++
|
||||
django/core/files/utils.py | 7 ++--
|
||||
tests/file_storage/test_base.py | 70 +++++++++++++++++++++++++++++++
|
||||
tests/file_storage/tests.py | 11 ++---
|
||||
tests/file_uploads/tests.py | 2 +-
|
||||
5 files changed, 88 insertions(+), 13 deletions(-)
|
||||
create mode 100644 tests/file_storage/test_base.py
|
||||
|
||||
diff --git a/django/core/files/storage/base.py b/django/core/files/storage/base.py
|
||||
index 16ac22f70a..03a1b44edb 100644
|
||||
--- a/django/core/files/storage/base.py
|
||||
+++ b/django/core/files/storage/base.py
|
||||
@@ -34,7 +34,18 @@ class Storage:
|
||||
if not hasattr(content, "chunks"):
|
||||
content = File(content, name)
|
||||
|
||||
+ # Ensure that the name is valid, before and after having the storage
|
||||
+ # system potentially modifying the name. This duplicates the check made
|
||||
+ # inside `get_available_name` but it's necessary for those cases where
|
||||
+ # `get_available_name` is overriden and validation is lost.
|
||||
+ validate_file_name(name, allow_relative_path=True)
|
||||
+
|
||||
+ # Potentially find a different name depending on storage constraints.
|
||||
name = self.get_available_name(name, max_length=max_length)
|
||||
+ # Validate the (potentially) new name.
|
||||
+ validate_file_name(name, allow_relative_path=True)
|
||||
+
|
||||
+ # The save operation should return the actual name of the file saved.
|
||||
name = self._save(name, content)
|
||||
# Ensure that the name returned from the storage system is still valid.
|
||||
validate_file_name(name, allow_relative_path=True)
|
||||
diff --git a/django/core/files/utils.py b/django/core/files/utils.py
|
||||
index 85342b2f3f..11e4f07724 100644
|
||||
--- a/django/core/files/utils.py
|
||||
+++ b/django/core/files/utils.py
|
||||
@@ -10,10 +10,9 @@ def validate_file_name(name, allow_relative_path=False):
|
||||
raise SuspiciousFileOperation("Could not derive file name from '%s'" % name)
|
||||
|
||||
if allow_relative_path:
|
||||
- # Use PurePosixPath() because this branch is checked only in
|
||||
- # FileField.generate_filename() where all file paths are expected to be
|
||||
- # Unix style (with forward slashes).
|
||||
- path = pathlib.PurePosixPath(name)
|
||||
+ # Ensure that name can be treated as a pure posix path, i.e. Unix
|
||||
+ # style (with forward slashes).
|
||||
+ path = pathlib.PurePosixPath(str(name).replace("\\", "/"))
|
||||
if path.is_absolute() or ".." in path.parts:
|
||||
raise SuspiciousFileOperation(
|
||||
"Detected path traversal attempt in '%s'" % name
|
||||
diff --git a/tests/file_storage/test_base.py b/tests/file_storage/test_base.py
|
||||
new file mode 100644
|
||||
index 0000000000..c5338b8e66
|
||||
--- /dev/null
|
||||
+++ b/tests/file_storage/test_base.py
|
||||
@@ -0,0 +1,70 @@
|
||||
+import os
|
||||
+from unittest import mock
|
||||
+
|
||||
+from django.core.exceptions import SuspiciousFileOperation
|
||||
+from django.core.files.storage import Storage
|
||||
+from django.test import SimpleTestCase
|
||||
+
|
||||
+
|
||||
+class CustomStorage(Storage):
|
||||
+ """Simple Storage subclass implementing the bare minimum for testing."""
|
||||
+
|
||||
+ def exists(self, name):
|
||||
+ return False
|
||||
+
|
||||
+ def _save(self, name):
|
||||
+ return name
|
||||
+
|
||||
+
|
||||
+class StorageValidateFileNameTests(SimpleTestCase):
|
||||
+ invalid_file_names = [
|
||||
+ os.path.join("path", "to", os.pardir, "test.file"),
|
||||
+ os.path.join(os.path.sep, "path", "to", "test.file"),
|
||||
+ ]
|
||||
+ error_msg = "Detected path traversal attempt in '%s'"
|
||||
+
|
||||
+ def test_validate_before_get_available_name(self):
|
||||
+ s = CustomStorage()
|
||||
+ # The initial name passed to `save` is not valid nor safe, fail early.
|
||||
+ for name in self.invalid_file_names:
|
||||
+ with (
|
||||
+ self.subTest(name=name),
|
||||
+ mock.patch.object(s, "get_available_name") as mock_get_available_name,
|
||||
+ mock.patch.object(s, "_save") as mock_internal_save,
|
||||
+ ):
|
||||
+ with self.assertRaisesMessage(
|
||||
+ SuspiciousFileOperation, self.error_msg % name
|
||||
+ ):
|
||||
+ s.save(name, content="irrelevant")
|
||||
+ self.assertEqual(mock_get_available_name.mock_calls, [])
|
||||
+ self.assertEqual(mock_internal_save.mock_calls, [])
|
||||
+
|
||||
+ def test_validate_after_get_available_name(self):
|
||||
+ s = CustomStorage()
|
||||
+ # The initial name passed to `save` is valid and safe, but the returned
|
||||
+ # name from `get_available_name` is not.
|
||||
+ for name in self.invalid_file_names:
|
||||
+ with (
|
||||
+ self.subTest(name=name),
|
||||
+ mock.patch.object(s, "get_available_name", return_value=name),
|
||||
+ mock.patch.object(s, "_save") as mock_internal_save,
|
||||
+ ):
|
||||
+ with self.assertRaisesMessage(
|
||||
+ SuspiciousFileOperation, self.error_msg % name
|
||||
+ ):
|
||||
+ s.save("valid-file-name.txt", content="irrelevant")
|
||||
+ self.assertEqual(mock_internal_save.mock_calls, [])
|
||||
+
|
||||
+ def test_validate_after_internal_save(self):
|
||||
+ s = CustomStorage()
|
||||
+ # The initial name passed to `save` is valid and safe, but the result
|
||||
+ # from `_save` is not (this is achieved by monkeypatching _save).
|
||||
+ for name in self.invalid_file_names:
|
||||
+ with (
|
||||
+ self.subTest(name=name),
|
||||
+ mock.patch.object(s, "_save", return_value=name),
|
||||
+ ):
|
||||
+ with self.assertRaisesMessage(
|
||||
+ SuspiciousFileOperation, self.error_msg % name
|
||||
+ ):
|
||||
+ s.save("valid-file-name.txt", content="irrelevant")
|
||||
diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py
|
||||
index 7fb57fbce4..44bea8c180 100644
|
||||
--- a/tests/file_storage/tests.py
|
||||
+++ b/tests/file_storage/tests.py
|
||||
@@ -342,22 +342,17 @@ class FileStorageTests(SimpleTestCase):
|
||||
|
||||
self.storage.delete("path/to/test.file")
|
||||
|
||||
- def test_file_save_abs_path(self):
|
||||
- test_name = "path/to/test.file"
|
||||
- f = ContentFile("file saved with path")
|
||||
- f_name = self.storage.save(os.path.join(self.temp_dir, test_name), f)
|
||||
- self.assertEqual(f_name, test_name)
|
||||
-
|
||||
@unittest.skipUnless(
|
||||
symlinks_supported(), "Must be able to symlink to run this test."
|
||||
)
|
||||
def test_file_save_broken_symlink(self):
|
||||
"""A new path is created on save when a broken symlink is supplied."""
|
||||
nonexistent_file_path = os.path.join(self.temp_dir, "nonexistent.txt")
|
||||
- broken_symlink_path = os.path.join(self.temp_dir, "symlink.txt")
|
||||
+ broken_symlink_file_name = "symlink.txt"
|
||||
+ broken_symlink_path = os.path.join(self.temp_dir, broken_symlink_file_name)
|
||||
os.symlink(nonexistent_file_path, broken_symlink_path)
|
||||
f = ContentFile("some content")
|
||||
- f_name = self.storage.save(broken_symlink_path, f)
|
||||
+ f_name = self.storage.save(broken_symlink_file_name, f)
|
||||
self.assertIs(os.path.exists(os.path.join(self.temp_dir, f_name)), True)
|
||||
|
||||
def test_save_doesnt_close(self):
|
||||
diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py
|
||||
index 693efc4c62..24c703a309 100644
|
||||
--- a/tests/file_uploads/tests.py
|
||||
+++ b/tests/file_uploads/tests.py
|
||||
@@ -826,7 +826,7 @@ class DirectoryCreationTests(SimpleTestCase):
|
||||
default_storage.delete(UPLOAD_TO)
|
||||
# Create a file with the upload directory name
|
||||
with SimpleUploadedFile(UPLOAD_TO, b"x") as file:
|
||||
- default_storage.save(UPLOAD_TO, file)
|
||||
+ default_storage.save(UPLOAD_FOLDER, file)
|
||||
self.addCleanup(default_storage.delete, UPLOAD_TO)
|
||||
msg = "%s exists and is not a directory." % UPLOAD_TO
|
||||
with self.assertRaisesMessage(FileExistsError, msg):
|
||||
--
|
||||
2.45.2
|
||||
|
135
CVE-2024-39614.patch
Normal file
135
CVE-2024-39614.patch
Normal file
@ -0,0 +1,135 @@
|
||||
From 17358fb35fb7217423d4c4877ccb6d1a3a40b1c3 Mon Sep 17 00:00:00 2001
|
||||
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
Date: Wed, 26 Jun 2024 12:11:54 +0200
|
||||
Subject: [PATCH] [4.2.x] Fixed CVE-2024-39614 -- Mitigated potential DoS in
|
||||
get_supported_language_variant().
|
||||
|
||||
Language codes are now parsed with a maximum length limit of 500 chars.
|
||||
|
||||
Thanks to MProgrammer for the report.
|
||||
---
|
||||
django/utils/translation/trans_real.py | 25 ++++++++++++++++++++-----
|
||||
docs/ref/utils.txt | 10 ++++++++++
|
||||
tests/i18n/tests.py | 11 +++++++++++
|
||||
3 files changed, 41 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
|
||||
index 872c80b00f..ce13d1e69c 100644
|
||||
--- a/django/utils/translation/trans_real.py
|
||||
+++ b/django/utils/translation/trans_real.py
|
||||
@@ -31,9 +31,10 @@ _default = None
|
||||
CONTEXT_SEPARATOR = "\x04"
|
||||
|
||||
# Maximum number of characters that will be parsed from the Accept-Language
|
||||
-# header to prevent possible denial of service or memory exhaustion attacks.
|
||||
-# About 10x longer than the longest value shown on MDN’s Accept-Language page.
|
||||
-ACCEPT_LANGUAGE_HEADER_MAX_LENGTH = 500
|
||||
+# header or cookie to prevent possible denial of service or memory exhaustion
|
||||
+# attacks. About 10x longer than the longest value shown on MDN’s
|
||||
+# Accept-Language page.
|
||||
+LANGUAGE_CODE_MAX_LENGTH = 500
|
||||
|
||||
# Format of Accept-Language header values. From RFC 9110 Sections 12.4.2 and
|
||||
# 12.5.4, and RFC 5646 Section 2.1.
|
||||
@@ -497,11 +498,25 @@ def get_supported_language_variant(lang_code, strict=False):
|
||||
If `strict` is False (the default), look for a country-specific variant
|
||||
when neither the language code nor its generic variant is found.
|
||||
|
||||
+ The language code is truncated to a maximum length to avoid potential
|
||||
+ denial of service attacks.
|
||||
+
|
||||
lru_cache should have a maxsize to prevent from memory exhaustion attacks,
|
||||
as the provided language codes are taken from the HTTP request. See also
|
||||
<https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>.
|
||||
"""
|
||||
if lang_code:
|
||||
+ # Truncate the language code to a maximum length to avoid potential
|
||||
+ # denial of service attacks.
|
||||
+ if len(lang_code) > LANGUAGE_CODE_MAX_LENGTH:
|
||||
+ if (
|
||||
+ not strict
|
||||
+ and (index := lang_code.rfind("-", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0
|
||||
+ ):
|
||||
+ # There is a generic variant under the maximum length accepted length.
|
||||
+ lang_code = lang_code[:index]
|
||||
+ else:
|
||||
+ raise ValueError("'lang_code' exceeds the maximum accepted length")
|
||||
# If 'zh-hant-tw' is not supported, try special fallback or subsequent
|
||||
# language codes i.e. 'zh-hant' and 'zh'.
|
||||
possible_lang_codes = [lang_code]
|
||||
@@ -625,13 +640,13 @@ def parse_accept_lang_header(lang_string):
|
||||
functools.lru_cache() to avoid repetitive parsing of common header values.
|
||||
"""
|
||||
# If the header value doesn't exceed the maximum allowed length, parse it.
|
||||
- if len(lang_string) <= ACCEPT_LANGUAGE_HEADER_MAX_LENGTH:
|
||||
+ if len(lang_string) <= LANGUAGE_CODE_MAX_LENGTH:
|
||||
return _parse_accept_lang_header(lang_string)
|
||||
|
||||
# If there is at least one comma in the value, parse up to the last comma
|
||||
# before the max length, skipping any truncated parts at the end of the
|
||||
# header value.
|
||||
- if (index := lang_string.rfind(",", 0, ACCEPT_LANGUAGE_HEADER_MAX_LENGTH)) > 0:
|
||||
+ if (index := lang_string.rfind(",", 0, LANGUAGE_CODE_MAX_LENGTH)) > 0:
|
||||
return _parse_accept_lang_header(lang_string[:index])
|
||||
|
||||
# Don't attempt to parse if there is only one language-range value which is
|
||||
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
|
||||
index b2b826684d..471a4b31eb 100644
|
||||
--- a/docs/ref/utils.txt
|
||||
+++ b/docs/ref/utils.txt
|
||||
@@ -1155,6 +1155,11 @@ For a complete discussion on the usage of the following see the
|
||||
``lang_code`` is ``'es-ar'`` and ``'es'`` is in :setting:`LANGUAGES` but
|
||||
``'es-ar'`` isn't.
|
||||
|
||||
+ ``lang_code`` has a maximum accepted length of 500 characters. A
|
||||
+ :exc:`ValueError` is raised if ``lang_code`` exceeds this limit and
|
||||
+ ``strict`` is ``True``, or if there is no generic variant and ``strict``
|
||||
+ is ``False``.
|
||||
+
|
||||
If ``strict`` is ``False`` (the default), a country-specific variant may
|
||||
be returned when neither the language code nor its generic variant is found.
|
||||
For example, if only ``'es-co'`` is in :setting:`LANGUAGES`, that's
|
||||
@@ -1163,6 +1168,11 @@ For a complete discussion on the usage of the following see the
|
||||
|
||||
Raises :exc:`LookupError` if nothing is found.
|
||||
|
||||
+ .. versionchanged:: 4.2.14
|
||||
+
|
||||
+ In older versions, ``lang_code`` values over 500 characters were
|
||||
+ processed without raising a :exc:`ValueError`.
|
||||
+
|
||||
.. function:: to_locale(language)
|
||||
|
||||
Turns a language name (en-us) into a locale name (en_US).
|
||||
diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py
|
||||
index f517c5eac7..9b029d5992 100644
|
||||
--- a/tests/i18n/tests.py
|
||||
+++ b/tests/i18n/tests.py
|
||||
@@ -65,6 +65,7 @@ from django.utils.translation.reloader import (
|
||||
translation_file_changed,
|
||||
watch_for_translation_changes,
|
||||
)
|
||||
+from django.utils.translation.trans_real import LANGUAGE_CODE_MAX_LENGTH
|
||||
|
||||
from .forms import CompanyForm, I18nForm, SelectDateForm
|
||||
from .models import Company, TestModel
|
||||
@@ -1888,6 +1889,16 @@ class MiscTests(SimpleTestCase):
|
||||
g("xyz")
|
||||
with self.assertRaises(LookupError):
|
||||
g("xy-zz")
|
||||
+ msg = "'lang_code' exceeds the maximum accepted length"
|
||||
+ with self.assertRaises(LookupError):
|
||||
+ g("x" * LANGUAGE_CODE_MAX_LENGTH)
|
||||
+ with self.assertRaisesMessage(ValueError, msg):
|
||||
+ g("x" * (LANGUAGE_CODE_MAX_LENGTH + 1))
|
||||
+ # 167 * 3 = 501 which is LANGUAGE_CODE_MAX_LENGTH + 1.
|
||||
+ self.assertEqual(g("en-" * 167), "en")
|
||||
+ with self.assertRaisesMessage(ValueError, msg):
|
||||
+ g("en-" * 167, strict=True)
|
||||
+ self.assertEqual(g("en-" * 30000), "en") # catastrophic test
|
||||
|
||||
def test_get_supported_language_variant_null(self):
|
||||
g = trans_null.get_supported_language_variant
|
||||
--
|
||||
2.45.2
|
||||
|
79
CVE-2024-41989.patch
Normal file
79
CVE-2024-41989.patch
Normal file
@ -0,0 +1,79 @@
|
||||
From 0521744d21a7854e849336af1e3a3aad44cee017 Mon Sep 17 00:00:00 2001
|
||||
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
Date: Fri, 12 Jul 2024 11:38:34 +0200
|
||||
Subject: [PATCH 1/4] [4.2.x] Fixed CVE-2024-41989 -- Prevented excessive
|
||||
memory consumption in floatformat.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Thanks Elias Myllymäki for the report.
|
||||
|
||||
Co-authored-by: Shai Berger <shai@platonix.com>
|
||||
---
|
||||
django/template/defaultfilters.py | 13 +++++++++++++
|
||||
.../filter_tests/test_floatformat.py | 17 +++++++++++++++++
|
||||
3 files changed, 39 insertions(+)
|
||||
|
||||
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
|
||||
index d446b54ade..3f89eba6bb 100644
|
||||
--- a/django/template/defaultfilters.py
|
||||
+++ b/django/template/defaultfilters.py
|
||||
@@ -163,6 +163,19 @@ def floatformat(text, arg=-1):
|
||||
except ValueError:
|
||||
return input_val
|
||||
|
||||
+ _, digits, exponent = d.as_tuple()
|
||||
+ try:
|
||||
+ number_of_digits_and_exponent_sum = len(digits) + abs(exponent)
|
||||
+ except TypeError:
|
||||
+ # Exponent values can be "F", "n", "N".
|
||||
+ number_of_digits_and_exponent_sum = 0
|
||||
+
|
||||
+ # Values with more than 200 digits, or with a large exponent, are returned "as is"
|
||||
+ # to avoid high memory consumption and potential denial-of-service attacks.
|
||||
+ # The cut-off of 200 is consistent with django.utils.numberformat.floatformat().
|
||||
+ if number_of_digits_and_exponent_sum > 200:
|
||||
+ return input_val
|
||||
+
|
||||
try:
|
||||
m = int(d) - d
|
||||
except (ValueError, OverflowError, InvalidOperation):
|
||||
diff --git a/tests/template_tests/filter_tests/test_floatformat.py b/tests/template_tests/filter_tests/test_floatformat.py
|
||||
index db17622309..c22b5dca6b 100644
|
||||
--- a/tests/template_tests/filter_tests/test_floatformat.py
|
||||
+++ b/tests/template_tests/filter_tests/test_floatformat.py
|
||||
@@ -77,6 +77,7 @@ class FunctionTests(SimpleTestCase):
|
||||
self.assertEqual(floatformat(1.5e-15, 20), "0.00000000000000150000")
|
||||
self.assertEqual(floatformat(1.5e-15, -20), "0.00000000000000150000")
|
||||
self.assertEqual(floatformat(1.00000000000000015, 16), "1.0000000000000002")
|
||||
+ self.assertEqual(floatformat("1e199"), "1" + "0" * 199)
|
||||
|
||||
def test_force_grouping(self):
|
||||
with translation.override("en"):
|
||||
@@ -134,6 +135,22 @@ class FunctionTests(SimpleTestCase):
|
||||
self.assertEqual(floatformat(pos_inf), "inf")
|
||||
self.assertEqual(floatformat(neg_inf), "-inf")
|
||||
self.assertEqual(floatformat(pos_inf / pos_inf), "nan")
|
||||
+ self.assertEqual(floatformat("inf"), "inf")
|
||||
+ self.assertEqual(floatformat("NaN"), "NaN")
|
||||
+
|
||||
+ def test_too_many_digits_to_render(self):
|
||||
+ cases = [
|
||||
+ "1e200",
|
||||
+ "1E200",
|
||||
+ "1E10000000000000000",
|
||||
+ "-1E10000000000000000",
|
||||
+ "1e10000000000000000",
|
||||
+ "-1e10000000000000000",
|
||||
+ "1" + "0" * 1_000_000,
|
||||
+ ]
|
||||
+ for value in cases:
|
||||
+ with self.subTest(value=value):
|
||||
+ self.assertEqual(floatformat(value), value)
|
||||
|
||||
def test_float_dunder_method(self):
|
||||
class FloatWrapper:
|
||||
--
|
||||
2.34.1
|
||||
|
64
CVE-2024-41990.patch
Normal file
64
CVE-2024-41990.patch
Normal file
@ -0,0 +1,64 @@
|
||||
From 729d7934e34ff91f262f3e7089e32cab701b09ca Mon Sep 17 00:00:00 2001
|
||||
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
Date: Thu, 18 Jul 2024 13:19:34 +0200
|
||||
Subject: [PATCH 2/4] [4.2.x] Fixed CVE-2024-41990 -- Mitigated potential DoS
|
||||
in urlize and urlizetrunc template filters.
|
||||
|
||||
Thanks to MProgrammer for the report.
|
||||
---
|
||||
django/utils/html.py | 18 ++++++++----------
|
||||
tests/utils_tests/test_html.py | 2 ++
|
||||
3 files changed, 17 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/django/utils/html.py b/django/utils/html.py
|
||||
index fd313ff9ca..dd52f1f7fe 100644
|
||||
--- a/django/utils/html.py
|
||||
+++ b/django/utils/html.py
|
||||
@@ -378,7 +378,11 @@ class Urlizer:
|
||||
trimmed_something = True
|
||||
counts[closing] -= strip
|
||||
|
||||
- rstripped = middle.rstrip(self.trailing_punctuation_chars_no_semicolon)
|
||||
+ amp = middle.rfind("&")
|
||||
+ if amp == -1:
|
||||
+ rstripped = middle.rstrip(self.trailing_punctuation_chars)
|
||||
+ else:
|
||||
+ rstripped = middle.rstrip(self.trailing_punctuation_chars_no_semicolon)
|
||||
if rstripped != middle:
|
||||
trail = middle[len(rstripped) :] + trail
|
||||
middle = rstripped
|
||||
@@ -386,15 +390,9 @@ class Urlizer:
|
||||
|
||||
if self.trailing_punctuation_chars_has_semicolon and middle.endswith(";"):
|
||||
# Only strip if not part of an HTML entity.
|
||||
- amp = middle.rfind("&")
|
||||
- if amp == -1:
|
||||
- can_strip = True
|
||||
- else:
|
||||
- potential_entity = middle[amp:]
|
||||
- escaped = html.unescape(potential_entity)
|
||||
- can_strip = (escaped == potential_entity) or escaped.endswith(";")
|
||||
-
|
||||
- if can_strip:
|
||||
+ potential_entity = middle[amp:]
|
||||
+ escaped = html.unescape(potential_entity)
|
||||
+ if escaped == potential_entity or escaped.endswith(";"):
|
||||
rstripped = middle.rstrip(";")
|
||||
amount_stripped = len(middle) - len(rstripped)
|
||||
if amp > -1 and amount_stripped > 1:
|
||||
diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py
|
||||
index 6dab41634a..c45e0dfac1 100644
|
||||
--- a/tests/utils_tests/test_html.py
|
||||
+++ b/tests/utils_tests/test_html.py
|
||||
@@ -349,6 +349,8 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
"[(" * 100_000 + ":" + ")]" * 100_000,
|
||||
"([[" * 100_000 + ":" + "]])" * 100_000,
|
||||
"&:" + ";" * 100_000,
|
||||
+ "&.;" * 100_000,
|
||||
+ ".;" * 100_000,
|
||||
)
|
||||
for value in tests:
|
||||
with self.subTest(value=value):
|
||||
--
|
||||
2.34.1
|
||||
|
117
CVE-2024-41991.patch
Normal file
117
CVE-2024-41991.patch
Normal file
@ -0,0 +1,117 @@
|
||||
From 772a73f70c3d249c99c23012849e66276b7b0715 Mon Sep 17 00:00:00 2001
|
||||
From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
|
||||
Date: Wed, 10 Jul 2024 20:30:12 +0200
|
||||
Subject: [PATCH 3/4] [4.2.x] Fixed CVE-2024-41991 -- Prevented potential ReDoS
|
||||
in django.utils.html.urlize() and AdminURLFieldWidget.
|
||||
|
||||
Thanks Seokchan Yoon for the report.
|
||||
|
||||
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
---
|
||||
django/contrib/admin/widgets.py | 2 +-
|
||||
django/utils/html.py | 10 ++++++++--
|
||||
tests/admin_widgets/tests.py | 7 ++++++-
|
||||
tests/utils_tests/test_html.py | 13 +++++++++++++
|
||||
5 files changed, 35 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
|
||||
index 5e3416bc28..3d11a40efe 100644
|
||||
--- a/django/contrib/admin/widgets.py
|
||||
+++ b/django/contrib/admin/widgets.py
|
||||
@@ -383,7 +383,7 @@ class AdminURLFieldWidget(forms.URLInput):
|
||||
context["current_label"] = _("Currently:")
|
||||
context["change_label"] = _("Change:")
|
||||
context["widget"]["href"] = (
|
||||
- smart_urlquote(context["widget"]["value"]) if value else ""
|
||||
+ smart_urlquote(context["widget"]["value"]) if url_valid else ""
|
||||
)
|
||||
context["url_valid"] = url_valid
|
||||
return context
|
||||
diff --git a/django/utils/html.py b/django/utils/html.py
|
||||
index dd52f1f7fe..23575d3c11 100644
|
||||
--- a/django/utils/html.py
|
||||
+++ b/django/utils/html.py
|
||||
@@ -13,6 +13,8 @@ from django.utils.regex_helper import _lazy_re_compile
|
||||
from django.utils.safestring import SafeData, SafeString, mark_safe
|
||||
from django.utils.text import normalize_newlines
|
||||
|
||||
+MAX_URL_LENGTH = 2048
|
||||
+
|
||||
|
||||
@keep_lazy(SafeString)
|
||||
def escape(text):
|
||||
@@ -300,9 +302,9 @@ class Urlizer:
|
||||
# Make URL we want to point to.
|
||||
url = None
|
||||
nofollow_attr = ' rel="nofollow"' if nofollow else ""
|
||||
- if self.simple_url_re.match(middle):
|
||||
+ if len(middle) <= MAX_URL_LENGTH and self.simple_url_re.match(middle):
|
||||
url = smart_urlquote(html.unescape(middle))
|
||||
- elif self.simple_url_2_re.match(middle):
|
||||
+ elif len(middle) <= MAX_URL_LENGTH and self.simple_url_2_re.match(middle):
|
||||
url = smart_urlquote("http://%s" % html.unescape(middle))
|
||||
elif ":" not in middle and self.is_email_simple(middle):
|
||||
local, domain = middle.rsplit("@", 1)
|
||||
@@ -417,6 +419,10 @@ class Urlizer:
|
||||
except ValueError:
|
||||
# value contains more than one @.
|
||||
return False
|
||||
+ # Max length for domain name labels is 63 characters per RFC 1034.
|
||||
+ # Helps to avoid ReDoS vectors in the domain part.
|
||||
+ if len(p2) > 63:
|
||||
+ return False
|
||||
# Dot must be in p2 (e.g. example.com)
|
||||
if "." not in p2 or p2.startswith("."):
|
||||
return False
|
||||
diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py
|
||||
index 0e20206048..4281ed07c6 100644
|
||||
--- a/tests/admin_widgets/tests.py
|
||||
+++ b/tests/admin_widgets/tests.py
|
||||
@@ -461,7 +461,12 @@ class AdminSplitDateTimeWidgetTest(SimpleTestCase):
|
||||
class AdminURLWidgetTest(SimpleTestCase):
|
||||
def test_get_context_validates_url(self):
|
||||
w = widgets.AdminURLFieldWidget()
|
||||
- for invalid in ["", "/not/a/full/url/", 'javascript:alert("Danger XSS!")']:
|
||||
+ for invalid in [
|
||||
+ "",
|
||||
+ "/not/a/full/url/",
|
||||
+ 'javascript:alert("Danger XSS!")',
|
||||
+ "http://" + "한.글." * 1_000_000 + "com",
|
||||
+ ]:
|
||||
with self.subTest(url=invalid):
|
||||
self.assertFalse(w.get_context("name", invalid, {})["url_valid"])
|
||||
self.assertTrue(w.get_context("name", "http://example.com", {})["url_valid"])
|
||||
diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py
|
||||
index c45e0dfac1..83ebe4334b 100644
|
||||
--- a/tests/utils_tests/test_html.py
|
||||
+++ b/tests/utils_tests/test_html.py
|
||||
@@ -328,6 +328,15 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
'Search for <a href="http://google.com/?q=">google.com/?q=</a>!',
|
||||
),
|
||||
("foo@example.com", '<a href="mailto:foo@example.com">foo@example.com</a>'),
|
||||
+ (
|
||||
+ "test@" + "한.글." * 15 + "aaa",
|
||||
+ '<a href="mailto:test@'
|
||||
+ + "xn--6q8b.xn--bj0b." * 15
|
||||
+ + 'aaa">'
|
||||
+ + "test@"
|
||||
+ + "한.글." * 15
|
||||
+ + "aaa</a>",
|
||||
+ ),
|
||||
)
|
||||
for value, output in tests:
|
||||
with self.subTest(value=value):
|
||||
@@ -336,6 +345,10 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
def test_urlize_unchanged_inputs(self):
|
||||
tests = (
|
||||
("a" + "@a" * 50000) + "a", # simple_email_re catastrophic test
|
||||
+ # Unicode domain catastrophic tests.
|
||||
+ "a@" + "한.글." * 1_000_000 + "a",
|
||||
+ "http://" + "한.글." * 1_000_000 + "com",
|
||||
+ "www." + "한.글." * 1_000_000 + "com",
|
||||
("a" + "." * 1000000) + "a", # trailing_punctuation catastrophic test
|
||||
"foo@",
|
||||
"@foo.com",
|
||||
--
|
||||
2.34.1
|
||||
|
78
CVE-2024-42005.patch
Normal file
78
CVE-2024-42005.patch
Normal file
@ -0,0 +1,78 @@
|
||||
From b6de28f897709ee5d94ca2da21bcc98f9dade01c Mon Sep 17 00:00:00 2001
|
||||
From: Simon Charette <charette.s@gmail.com>
|
||||
Date: Thu, 25 Jul 2024 18:19:13 +0200
|
||||
Subject: [PATCH 4/4] [4.2.x] Fixed CVE-2024-42005 -- Mitigated
|
||||
QuerySet.values() SQL injection attacks against JSON fields.
|
||||
|
||||
Thanks Eyal (eyalgabay) for the report.
|
||||
---
|
||||
django/db/models/sql/query.py | 2 ++
|
||||
tests/expressions/models.py | 7 +++++++
|
||||
tests/expressions/test_queryset_values.py | 17 +++++++++++++++--
|
||||
4 files changed, 31 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
|
||||
index f98c6c668b..e68fd9efb7 100644
|
||||
--- a/django/db/models/sql/query.py
|
||||
+++ b/django/db/models/sql/query.py
|
||||
@@ -2415,6 +2415,8 @@ class Query(BaseExpression):
|
||||
self.has_select_fields = True
|
||||
|
||||
if fields:
|
||||
+ for field in fields:
|
||||
+ self.check_alias(field)
|
||||
field_names = []
|
||||
extra_names = []
|
||||
annotation_names = []
|
||||
diff --git a/tests/expressions/models.py b/tests/expressions/models.py
|
||||
index 0a8a0a6584..6b21e9ccf3 100644
|
||||
--- a/tests/expressions/models.py
|
||||
+++ b/tests/expressions/models.py
|
||||
@@ -106,3 +106,10 @@ class UUIDPK(models.Model):
|
||||
class UUID(models.Model):
|
||||
uuid = models.UUIDField(null=True)
|
||||
uuid_fk = models.ForeignKey(UUIDPK, models.CASCADE, null=True)
|
||||
+
|
||||
+
|
||||
+class JSONFieldModel(models.Model):
|
||||
+ data = models.JSONField(null=True)
|
||||
+
|
||||
+ class Meta:
|
||||
+ required_db_features = {"supports_json_field"}
|
||||
diff --git a/tests/expressions/test_queryset_values.py b/tests/expressions/test_queryset_values.py
|
||||
index 80addef37b..47bd1358de 100644
|
||||
--- a/tests/expressions/test_queryset_values.py
|
||||
+++ b/tests/expressions/test_queryset_values.py
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.db.models import F, Sum
|
||||
-from django.test import TestCase
|
||||
+from django.test import TestCase, skipUnlessDBFeature
|
||||
|
||||
-from .models import Company, Employee
|
||||
+from .models import Company, Employee, JSONFieldModel
|
||||
|
||||
|
||||
class ValuesExpressionsTests(TestCase):
|
||||
@@ -43,6 +43,19 @@ class ValuesExpressionsTests(TestCase):
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
Company.objects.values(**{crafted_alias: F("ceo__salary")})
|
||||
|
||||
+ @skipUnlessDBFeature("supports_json_field")
|
||||
+ def test_values_expression_alias_sql_injection_json_field(self):
|
||||
+ crafted_alias = """injected_name" from "expressions_company"; --"""
|
||||
+ msg = (
|
||||
+ "Column aliases cannot contain whitespace characters, quotation marks, "
|
||||
+ "semicolons, or SQL comments."
|
||||
+ )
|
||||
+ with self.assertRaisesMessage(ValueError, msg):
|
||||
+ JSONFieldModel.objects.values(f"data__{crafted_alias}")
|
||||
+
|
||||
+ with self.assertRaisesMessage(ValueError, msg):
|
||||
+ JSONFieldModel.objects.values_list(f"data__{crafted_alias}")
|
||||
+
|
||||
def test_values_expression_group_by(self):
|
||||
# values() applies annotate() first, so values selected are grouped by
|
||||
# id, not firstname.
|
||||
--
|
||||
2.34.1
|
||||
|
133
CVE-2024-45230.patch
Normal file
133
CVE-2024-45230.patch
Normal file
@ -0,0 +1,133 @@
|
||||
From 65a776dd25b657cc32edafaad98d91aa0b51e641 Mon Sep 17 00:00:00 2001
|
||||
From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
|
||||
Date: Mon, 12 Aug 2024 15:17:57 +0200
|
||||
Subject: [PATCH 1/2] [4.2.x] Fixed CVE-2024-45230 -- Mitigated potential DoS
|
||||
in urlize and urlizetrunc template filters.
|
||||
|
||||
Thanks MProgrammer (https://hackerone.com/mprogrammer) for the report.
|
||||
---
|
||||
django/utils/html.py | 17 ++++++++------
|
||||
docs/ref/templates/builtins.txt | 11 ++++++++++
|
||||
docs/releases/4.2.16.txt | 15 +++++++++++++
|
||||
docs/releases/index.txt | 1 +
|
||||
.../filter_tests/test_urlize.py | 22 +++++++++++++++++++
|
||||
tests/utils_tests/test_html.py | 1 +
|
||||
6 files changed, 60 insertions(+), 7 deletions(-)
|
||||
create mode 100644 docs/releases/4.2.16.txt
|
||||
|
||||
Index: Django-4.2.11/django/utils/html.py
|
||||
===================================================================
|
||||
--- Django-4.2.11.orig/django/utils/html.py
|
||||
+++ Django-4.2.11/django/utils/html.py
|
||||
@@ -395,14 +395,17 @@ class Urlizer:
|
||||
potential_entity = middle[amp:]
|
||||
escaped = html.unescape(potential_entity)
|
||||
if escaped == potential_entity or escaped.endswith(";"):
|
||||
- rstripped = middle.rstrip(";")
|
||||
- amount_stripped = len(middle) - len(rstripped)
|
||||
- if amp > -1 and amount_stripped > 1:
|
||||
- # Leave a trailing semicolon as might be an entity.
|
||||
- trail = middle[len(rstripped) + 1 :] + trail
|
||||
- middle = rstripped + ";"
|
||||
+ rstripped = middle.rstrip(self.trailing_punctuation_chars)
|
||||
+ trail_start = len(rstripped)
|
||||
+ amount_trailing_semicolons = len(middle) - len(middle.rstrip(";"))
|
||||
+ if amp > -1 and amount_trailing_semicolons > 1:
|
||||
+ # Leave up to most recent semicolon as might be an entity.
|
||||
+ recent_semicolon = middle[trail_start:].index(";")
|
||||
+ middle_semicolon_index = recent_semicolon + trail_start + 1
|
||||
+ trail = middle[middle_semicolon_index:] + trail
|
||||
+ middle = rstripped + middle[trail_start:middle_semicolon_index]
|
||||
else:
|
||||
- trail = middle[len(rstripped) :] + trail
|
||||
+ trail = middle[trail_start:] + trail
|
||||
middle = rstripped
|
||||
trimmed_something = True
|
||||
|
||||
Index: Django-4.2.11/docs/ref/templates/builtins.txt
|
||||
===================================================================
|
||||
--- Django-4.2.11.orig/docs/ref/templates/builtins.txt
|
||||
+++ Django-4.2.11/docs/ref/templates/builtins.txt
|
||||
@@ -2831,6 +2831,17 @@ Django's built-in :tfilter:`escape` filt
|
||||
email addresses that contain single quotes (``'``), things won't work as
|
||||
expected. Apply this filter only to plain text.
|
||||
|
||||
+.. warning::
|
||||
+
|
||||
+ Using ``urlize`` or ``urlizetrunc`` can incur a performance penalty, which
|
||||
+ can become severe when applied to user controlled values such as content
|
||||
+ stored in a :class:`~django.db.models.TextField`. You can use
|
||||
+ :tfilter:`truncatechars` to add a limit to such inputs:
|
||||
+
|
||||
+ .. code-block:: html+django
|
||||
+
|
||||
+ {{ value|truncatechars:500|urlize }}
|
||||
+
|
||||
.. templatefilter:: urlizetrunc
|
||||
|
||||
``urlizetrunc``
|
||||
Index: Django-4.2.11/docs/releases/4.2.16.txt
|
||||
===================================================================
|
||||
--- /dev/null
|
||||
+++ Django-4.2.11/docs/releases/4.2.16.txt
|
||||
@@ -0,0 +1,15 @@
|
||||
+===========================
|
||||
+Django 4.2.16 release notes
|
||||
+===========================
|
||||
+
|
||||
+*September 3, 2024*
|
||||
+
|
||||
+Django 4.2.16 fixes one security issue with severity "moderate" and one
|
||||
+security issues with severity "low" in 4.2.15.
|
||||
+
|
||||
+CVE-2024-45230: Potential denial-of-service vulnerability in ``django.utils.html.urlize()``
|
||||
+===========================================================================================
|
||||
+
|
||||
+:tfilter:`urlize` and :tfilter:`urlizetrunc` were subject to a potential
|
||||
+denial-of-service attack via very large inputs with a specific sequence of
|
||||
+characters.
|
||||
Index: Django-4.2.11/tests/template_tests/filter_tests/test_urlize.py
|
||||
===================================================================
|
||||
--- Django-4.2.11.orig/tests/template_tests/filter_tests/test_urlize.py
|
||||
+++ Django-4.2.11/tests/template_tests/filter_tests/test_urlize.py
|
||||
@@ -305,6 +305,28 @@ class FunctionTests(SimpleTestCase):
|
||||
"http://testing.com/example</a>.,:;)"!",
|
||||
)
|
||||
|
||||
+ def test_trailing_semicolon(self):
|
||||
+ self.assertEqual(
|
||||
+ urlize("http://example.com?x=&", autoescape=False),
|
||||
+ '<a href="http://example.com?x=" rel="nofollow">'
|
||||
+ "http://example.com?x=&</a>",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ urlize("http://example.com?x=&;", autoescape=False),
|
||||
+ '<a href="http://example.com?x=" rel="nofollow">'
|
||||
+ "http://example.com?x=&</a>;",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ urlize("http://example.com?x=&;;", autoescape=False),
|
||||
+ '<a href="http://example.com?x=" rel="nofollow">'
|
||||
+ "http://example.com?x=&</a>;;",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ urlize("http://example.com?x=&.;...;", autoescape=False),
|
||||
+ '<a href="http://example.com?x=" rel="nofollow">'
|
||||
+ "http://example.com?x=&</a>.;...;",
|
||||
+ )
|
||||
+
|
||||
def test_brackets(self):
|
||||
"""
|
||||
#19070 - Check urlize handles brackets properly
|
||||
Index: Django-4.2.11/tests/utils_tests/test_html.py
|
||||
===================================================================
|
||||
--- Django-4.2.11.orig/tests/utils_tests/test_html.py
|
||||
+++ Django-4.2.11/tests/utils_tests/test_html.py
|
||||
@@ -364,6 +364,7 @@ class TestUtilsHtml(SimpleTestCase):
|
||||
"&:" + ";" * 100_000,
|
||||
"&.;" * 100_000,
|
||||
".;" * 100_000,
|
||||
+ "&" + ";:" * 100_000,
|
||||
)
|
||||
for value in tests:
|
||||
with self.subTest(value=value):
|
159
CVE-2024-45231.patch
Normal file
159
CVE-2024-45231.patch
Normal file
@ -0,0 +1,159 @@
|
||||
From fe42da9cdacd9f43fb0d499244314c36f9a11a19 Mon Sep 17 00:00:00 2001
|
||||
From: Natalia <124304+nessita@users.noreply.github.com>
|
||||
Date: Mon, 19 Aug 2024 14:47:38 -0300
|
||||
Subject: [PATCH 2/2] [4.2.x] Fixed CVE-2024-45231 -- Avoided server error on
|
||||
password reset when email sending fails.
|
||||
|
||||
On successful submission of a password reset request, an email is sent
|
||||
to the accounts known to the system. If sending this email fails (due to
|
||||
email backend misconfiguration, service provider outage, network issues,
|
||||
etc.), an attacker might exploit this by detecting which password reset
|
||||
requests succeed and which ones generate a 500 error response.
|
||||
|
||||
Thanks to Thibaut Spriet for the report, and to Mariusz Felisiak and
|
||||
Sarah Boyce for the reviews.
|
||||
---
|
||||
django/contrib/auth/forms.py | 9 ++++++++-
|
||||
docs/ref/logging.txt | 12 ++++++++++++
|
||||
docs/releases/4.2.16.txt | 11 +++++++++++
|
||||
docs/topics/auth/default.txt | 4 +++-
|
||||
tests/auth_tests/test_forms.py | 21 +++++++++++++++++++++
|
||||
tests/mail/custombackend.py | 5 +++++
|
||||
6 files changed, 60 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
|
||||
index 061dc81b42..20ce1ba39c 100644
|
||||
--- a/django/contrib/auth/forms.py
|
||||
+++ b/django/contrib/auth/forms.py
|
||||
@@ -1,3 +1,4 @@
|
||||
+import logging
|
||||
import unicodedata
|
||||
|
||||
from django import forms
|
||||
@@ -16,6 +17,7 @@ from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
UserModel = get_user_model()
|
||||
+logger = logging.getLogger("django.contrib.auth")
|
||||
|
||||
|
||||
def _unicode_ci_compare(s1, s2):
|
||||
@@ -314,7 +316,12 @@ class PasswordResetForm(forms.Form):
|
||||
html_email = loader.render_to_string(html_email_template_name, context)
|
||||
email_message.attach_alternative(html_email, "text/html")
|
||||
|
||||
- email_message.send()
|
||||
+ try:
|
||||
+ email_message.send()
|
||||
+ except Exception:
|
||||
+ logger.exception(
|
||||
+ "Failed to send password reset email to %s:", context["user"].pk
|
||||
+ )
|
||||
|
||||
def get_users(self, email):
|
||||
"""Given an email, return matching user(s) who should receive a reset.
|
||||
diff --git a/docs/ref/logging.txt b/docs/ref/logging.txt
|
||||
index b11fb752f7..3d33e0af63 100644
|
||||
--- a/docs/ref/logging.txt
|
||||
+++ b/docs/ref/logging.txt
|
||||
@@ -204,6 +204,18 @@ all database queries.
|
||||
Support for logging transaction management queries (``BEGIN``, ``COMMIT``,
|
||||
and ``ROLLBACK``) was added.
|
||||
|
||||
+.. _django-contrib-auth-logger:
|
||||
+
|
||||
+``django.contrib.auth``
|
||||
+~~~~~~~~~~~~~~~~~~~~~~~
|
||||
+
|
||||
+.. versionadded:: 4.2.16
|
||||
+
|
||||
+Log messages related to :doc:`contrib/auth`, particularly ``ERROR`` messages
|
||||
+are generated when a :class:`~django.contrib.auth.forms.PasswordResetForm` is
|
||||
+successfully submitted but the password reset email cannot be delivered due to
|
||||
+a mail sending exception.
|
||||
+
|
||||
.. _django-security-logger:
|
||||
|
||||
``django.security.*``
|
||||
diff --git a/docs/releases/4.2.16.txt b/docs/releases/4.2.16.txt
|
||||
index 043041a97f..4e632d5d77 100644
|
||||
--- a/docs/releases/4.2.16.txt
|
||||
+++ b/docs/releases/4.2.16.txt
|
||||
@@ -13,3 +13,14 @@ CVE-2024-45230: Potential denial-of-service vulnerability in ``django.utils.html
|
||||
:tfilter:`urlize` and :tfilter:`urlizetrunc` were subject to a potential
|
||||
denial-of-service attack via very large inputs with a specific sequence of
|
||||
characters.
|
||||
+
|
||||
+CVE-2024-45231: Potential user email enumeration via response status on password reset
|
||||
+======================================================================================
|
||||
+
|
||||
+Due to unhandled email sending failures, the
|
||||
+:class:`~django.contrib.auth.forms.PasswordResetForm` class allowed remote
|
||||
+attackers to enumerate user emails by issuing password reset requests and
|
||||
+observing the outcomes.
|
||||
+
|
||||
+To mitigate this risk, exceptions occurring during password reset email sending
|
||||
+are now handled and logged using the :ref:`django-contrib-auth-logger` logger.
|
||||
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
|
||||
index 528902416d..ad840c5e57 100644
|
||||
--- a/docs/topics/auth/default.txt
|
||||
+++ b/docs/topics/auth/default.txt
|
||||
@@ -1661,7 +1661,9 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`:
|
||||
.. method:: send_mail(subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None)
|
||||
|
||||
Uses the arguments to send an ``EmailMultiAlternatives``.
|
||||
- Can be overridden to customize how the email is sent to the user.
|
||||
+ Can be overridden to customize how the email is sent to the user. If
|
||||
+ you choose to override this method, be mindful of handling potential
|
||||
+ exceptions raised due to email sending failures.
|
||||
|
||||
:param subject_template_name: the template for the subject.
|
||||
:param email_template_name: the template for the email body.
|
||||
diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py
|
||||
index 81c56a428e..ccb1a26a2b 100644
|
||||
--- a/tests/auth_tests/test_forms.py
|
||||
+++ b/tests/auth_tests/test_forms.py
|
||||
@@ -1245,6 +1245,27 @@ class PasswordResetFormTest(TestDataMixin, TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
+ @override_settings(EMAIL_BACKEND="mail.custombackend.FailingEmailBackend")
|
||||
+ def test_save_send_email_exceptions_are_catched_and_logged(self):
|
||||
+ (user, username, email) = self.create_dummy_user()
|
||||
+ form = PasswordResetForm({"email": email})
|
||||
+ self.assertTrue(form.is_valid())
|
||||
+
|
||||
+ with self.assertLogs("django.contrib.auth", level=0) as cm:
|
||||
+ form.save()
|
||||
+
|
||||
+ self.assertEqual(len(mail.outbox), 0)
|
||||
+ self.assertEqual(len(cm.output), 1)
|
||||
+ errors = cm.output[0].split("\n")
|
||||
+ pk = user.pk
|
||||
+ self.assertEqual(
|
||||
+ errors[0],
|
||||
+ f"ERROR:django.contrib.auth:Failed to send password reset email to {pk}:",
|
||||
+ )
|
||||
+ self.assertEqual(
|
||||
+ errors[-1], "ValueError: FailingEmailBackend is doomed to fail."
|
||||
+ )
|
||||
+
|
||||
@override_settings(AUTH_USER_MODEL="auth_tests.CustomEmailField")
|
||||
def test_custom_email_field(self):
|
||||
email = "test@mail.com"
|
||||
diff --git a/tests/mail/custombackend.py b/tests/mail/custombackend.py
|
||||
index 14e7f077ba..c6c567b642 100644
|
||||
--- a/tests/mail/custombackend.py
|
||||
+++ b/tests/mail/custombackend.py
|
||||
@@ -12,3 +12,8 @@ class EmailBackend(BaseEmailBackend):
|
||||
# Messages are stored in an instance variable for testing.
|
||||
self.test_outbox.extend(email_messages)
|
||||
return len(email_messages)
|
||||
+
|
||||
+
|
||||
+class FailingEmailBackend(BaseEmailBackend):
|
||||
+ def send_messages(self, email_messages):
|
||||
+ raise ValueError("FailingEmailBackend is doomed to fail.")
|
||||
--
|
||||
2.46.0
|
||||
|
67
Django-4.2.11.checksum.txt
Normal file
67
Django-4.2.11.checksum.txt
Normal file
@ -0,0 +1,67 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA256
|
||||
|
||||
This file contains MD5, SHA1, and SHA256 checksums for the source-code
|
||||
tarball and wheel files of Django 4.2.11, released March 4, 2024.
|
||||
|
||||
To use this file, you will need a working install of PGP or other
|
||||
compatible public-key encryption software. You will also need to have
|
||||
the Django release manager's public key in your keyring. This key has
|
||||
the ID ``2EF56372BA48CD1B`` and can be imported from the MIT
|
||||
keyserver, for example, if using the open-source GNU Privacy Guard
|
||||
implementation of PGP:
|
||||
|
||||
gpg --keyserver pgp.mit.edu --recv-key 2EF56372BA48CD1B
|
||||
|
||||
or via the GitHub API:
|
||||
|
||||
curl https://github.com/felixxm.gpg | gpg --import -
|
||||
|
||||
Once the key is imported, verify this file:
|
||||
|
||||
gpg --verify Django-4.2.11.checksum.txt
|
||||
|
||||
Once you have verified this file, you can use normal MD5, SHA1, or SHA256
|
||||
checksumming applications to generate the checksums of the Django
|
||||
package and compare them to the checksums listed below.
|
||||
|
||||
Release packages
|
||||
================
|
||||
|
||||
https://www.djangoproject.com/m/releases/4.2/Django-4.2.11-py3-none-any.whl
|
||||
https://www.djangoproject.com/m/releases/4.2/Django-4.2.11.tar.gz
|
||||
|
||||
MD5 checksums
|
||||
=============
|
||||
|
||||
5ac62cf0d75216275a8d5f3b9a87b7a1 Django-4.2.11-py3-none-any.whl
|
||||
33dc961e25b6ed54e22b62726b334d4d Django-4.2.11.tar.gz
|
||||
|
||||
SHA1 checksums
|
||||
==============
|
||||
|
||||
69943b2e90d352cd8d536f34a0cd38dc3d3026be Django-4.2.11-py3-none-any.whl
|
||||
fda76a55736054cb5aafb73d2caa3f2d47765f9f Django-4.2.11.tar.gz
|
||||
|
||||
SHA256 checksums
|
||||
================
|
||||
|
||||
ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3 Django-4.2.11-py3-none-any.whl
|
||||
6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4 Django-4.2.11.tar.gz
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQJPBAEBCAA5FiEEq7LCqM0B8WE2GLcNLvVjcrpIzRsFAmXle9IbHGZlbGlzaWFr
|
||||
Lm1hcml1c3pAZ21haWwuY29tAAoJEC71Y3K6SM0bYRAP/RaamVJZrHq8H1vXx0IF
|
||||
+H99BDF282S6rEjajxe4vhEz8JnWFUkALlvh9MQQ2GOH7M66EfYP5K0BBWZHJTki
|
||||
Sf8zFRSaOYkblFaKvKMKC8m4nQ4XI2S2y3Nvx7KaaJSBsanahgDFFFcEdx8LnZdY
|
||||
2Vj9S2hnm9eT/0GSbTO2nn1lWcrShoYm2ZVHgmrH1qkX24uBO7VXD3x6j2pzdplg
|
||||
mW7rW03seWUtf/FQCGVnbTblxX7N0E+5BeeqwJvom8ijFEpcoFHY6EDLooXoq0MQ
|
||||
aDKOU5xns4k6YnPIDWSlZKa/RhxLUhkAyyiMrS1ADZF8Ee7Xk+M8cAt6okv6EBul
|
||||
gEWVtVKGYV9DKlKBqTkWcgiFH4nKSl+ckVrTK8OTss3zIUxkXQr34Ee5rJ6ciC+8
|
||||
2FHq3S55ylBvXDW1U+tfknyi78GLywjySxhdSOnZIEAaWDnFpW3X+838FKRUXMlC
|
||||
rMvQJswtpPPx76E1RyzwSuBdpVkzHoC49GGeZfyPynlupZJ9Vcue7w2q8WvQ0GrX
|
||||
/qhPFU21AEvf2siOlFwSr9TopjIMFckHMuLrSrVyoYoDZq1DXyprEpkasPXOq9zM
|
||||
FTqWPscC7M2BI0mAAMcJTWPBlqmfwF0W7Jiqo7cZutmdSVhOxDrySr3zWYXBzfht
|
||||
ERfQPBvTEYmsXtBC+H3mk040
|
||||
=I96k
|
||||
-----END PGP SIGNATURE-----
|
BIN
Django-4.2.11.tar.gz
(Stored with Git LFS)
Normal file
BIN
Django-4.2.11.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1,67 +0,0 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA256
|
||||
|
||||
This file contains MD5, SHA1, and SHA256 checksums for the source-code
|
||||
tarball and wheel files of Django 4.2.6, released October 4, 2023.
|
||||
|
||||
To use this file, you will need a working install of PGP or other
|
||||
compatible public-key encryption software. You will also need to have
|
||||
the Django release manager's public key in your keyring. This key has
|
||||
the ID ``2EE82A8D9470983E`` and can be imported from the MIT
|
||||
keyserver, for example, if using the open-source GNU Privacy Guard
|
||||
implementation of PGP:
|
||||
|
||||
gpg --keyserver pgp.mit.edu --recv-key 2EE82A8D9470983E
|
||||
|
||||
or via the GitHub API:
|
||||
|
||||
curl https://github.com/nessita.gpg | gpg --import -
|
||||
|
||||
Once the key is imported, verify this file:
|
||||
|
||||
gpg --verify Django-4.2.6.checksum.txt
|
||||
|
||||
Once you have verified this file, you can use normal MD5, SHA1, or SHA256
|
||||
checksumming applications to generate the checksums of the Django
|
||||
package and compare them to the checksums listed below.
|
||||
|
||||
Release packages
|
||||
================
|
||||
|
||||
https://www.djangoproject.com/m/releases/4.2/Django-4.2.6-py3-none-any.whl
|
||||
https://www.djangoproject.com/m/releases/4.2/Django-4.2.6.tar.gz
|
||||
|
||||
MD5 checksums
|
||||
=============
|
||||
|
||||
db83d48600d6afff838e53f42f9ebebb Django-4.2.6-py3-none-any.whl
|
||||
ad84c2b9bbebaa26427a2a656fe5ceea Django-4.2.6.tar.gz
|
||||
|
||||
SHA1 checksums
|
||||
==============
|
||||
|
||||
36650eb323bd34afbe47936bd3e7bf62ed4d929c Django-4.2.6-py3-none-any.whl
|
||||
6e912eeabd1df0b652e0da44cd3a556a496a1811 Django-4.2.6.tar.gz
|
||||
|
||||
SHA256 checksums
|
||||
================
|
||||
|
||||
a64d2487cdb00ad7461434320ccc38e60af9c404773a2f95ab0093b4453a3215 Django-4.2.6-py3-none-any.whl
|
||||
08f41f468b63335aea0d904c5729e0250300f6a1907bf293a65499496cdbc68f Django-4.2.6.tar.gz
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQJcBAEBCABGFiEEW1sboQ2FrHxcduOPLugqjZRwmD4FAmUdYL4oHDEyNDMwNCtu
|
||||
ZXNzaXRhQHVzZXJzLm5vcmVwbHkuZ2l0aHViLmNvbQAKCRAu6CqNlHCYPsQzEACE
|
||||
1e0nWDjh2RkV0nLraeEOd8DkyeCAMhFsiWGVkNY7chpeoXnF0YksHg9z2MiTDDJ9
|
||||
12EyYLFZPMCzqt3gO1/4iWYu/zx7Pb8gPTeg5NTLUnezZt4QT6FSv3fY7ByubqXQ
|
||||
lUp0jJJd8B3uc5zdZNLyg9OGBOHG7lqv7Eg7H3YUwXFo7VOkerLLgASTScE22Guo
|
||||
jyQYlnnLtse70l/MTTdmJYwJxbNM7LP4RXSovHV34nL2HCI5vDWyNlOgVeU+MT9F
|
||||
AQCW8Lb0H+GvrhL6Hc1D8xQl7OOvpo/5/53J1i/M2Ml60qeYbjWkqEByPI5d/9oS
|
||||
oHMzZcbnhlWcePy7zEYfyzQ0qFv3m/qIIf2rcd3mnrusMScWGsCFSSjqWLdoT2eO
|
||||
Cvz5Q+FGH8g2ce+DyfEDjDTzceReNL81lArmSPqntByYfp8COUuqBwe5PZ7T0yx7
|
||||
w2LWWICVmCfjKgQ12Rk7ElxcliIILFgETJVuPtjx6SrkDEzNDpiTVQH2E9LXZYsV
|
||||
5Qd7QEfTh0oEBBTPxHtSskTnfP/mJWAk62uLWYEcbmHTTcw4wQdnncwJS01tG+BD
|
||||
sd4iY0UeL4cof3sxkwGkvC6Sr0H5fgYCJs4AgAmcWBCzwFvtUp/J3+/WEr9wExBH
|
||||
/Fveza/vFJifyN1FwiemueuOqG/tvy1XJL6jCRH3gQ==
|
||||
=cttz
|
||||
-----END PGP SIGNATURE-----
|
BIN
Django-4.2.6.tar.gz
(Stored with Git LFS)
BIN
Django-4.2.6.tar.gz
(Stored with Git LFS)
Binary file not shown.
25
dirty-hack-remove-assert.patch
Normal file
25
dirty-hack-remove-assert.patch
Normal file
@ -0,0 +1,25 @@
|
||||
From 36736edaf595d2bbf1fe881609b2a4c8e3bac68a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
|
||||
Date: Thu, 29 Jun 2023 12:29:21 +0200
|
||||
Subject: [PATCH] Dirty hack: Remove a failing assert, failure does not seem
|
||||
critical
|
||||
|
||||
---
|
||||
tests/settings_tests/tests.py | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py
|
||||
index 62cbffb..b7432d3 100644
|
||||
--- a/tests/settings_tests/tests.py
|
||||
+++ b/tests/settings_tests/tests.py
|
||||
@@ -397,7 +397,6 @@ class TestComplexSettingOverride(SimpleTestCase):
|
||||
with self.assertWarnsMessage(UserWarning, msg) as cm:
|
||||
with override_settings(TEST_WARN="override"):
|
||||
self.assertEqual(settings.TEST_WARN, "override")
|
||||
- self.assertEqual(cm.filename, __file__)
|
||||
|
||||
|
||||
class SecureProxySslHeaderTest(SimpleTestCase):
|
||||
--
|
||||
2.40.1
|
||||
|
14
fix-safemimetext-set_payload.patch
Normal file
14
fix-safemimetext-set_payload.patch
Normal file
@ -0,0 +1,14 @@
|
||||
Index: Django-4.2.11/django/core/mail/message.py
|
||||
===================================================================
|
||||
--- Django-4.2.11.orig/django/core/mail/message.py
|
||||
+++ Django-4.2.11/django/core/mail/message.py
|
||||
@@ -168,7 +168,8 @@ class SafeMIMEText(MIMEMixin, MIMEText):
|
||||
def set_payload(self, payload, charset=None):
|
||||
if charset == "utf-8" and not isinstance(charset, Charset.Charset):
|
||||
has_long_lines = any(
|
||||
- len(line.encode()) > RFC5322_EMAIL_LINE_LENGTH_LIMIT
|
||||
+ len(line.encode(errors="surrogateescape"))
|
||||
+ > RFC5322_EMAIL_LINE_LENGTH_LIMIT
|
||||
for line in payload.splitlines()
|
||||
)
|
||||
# Quoted-Printable encoding has the side effect of shortening long
|
@ -1,7 +1,112 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Feb 29 13:19:00 UTC 2024 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
Mon Sep 2 12:48:52 UTC 2024 - Markéta Machová <mmachova@suse.com>
|
||||
|
||||
- Add CVE-2024-27351.patch patch (CVE-2024-27351, bsc#1220358)
|
||||
- Add more security patches:
|
||||
* CVE-2024-45230.patch (bsc#1229823)
|
||||
* CVE-2024-45231.patch (bsc#1229824)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Aug 1 09:37:57 UTC 2024 - Markéta Machová <mmachova@suse.com>
|
||||
|
||||
- Add bunch of security patches:
|
||||
* CVE-2024-42005.patch (bsc#1228629)
|
||||
* CVE-2024-41989.patch (bsc#1228630)
|
||||
* CVE-2024-41990.patch (bsc#1228631)
|
||||
* CVE-2024-41991.patch (bsc#1228632)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jul 12 12:40:47 UTC 2024 - Nico Krapp <nico.krapp@suse.com>
|
||||
|
||||
- Add CVE-2024-38875.patch (bsc#1227590)
|
||||
* CVE-2024-38875: Potential denial-of-service attack via
|
||||
certain inputs with a very large number of brackets
|
||||
- Add CVE-2024-39329.patch (bsc#1227593)
|
||||
* CVE-2024-39329: Username enumeration through timing difference
|
||||
for users with unusable passwords
|
||||
- Add CVE-2024-39330.patch (bsc#1227594)
|
||||
* CVE-2024-39330: Potential directory traversal in
|
||||
django.core.files.storage.Storage.save()
|
||||
- Add CVE-2024-39614.patch (bsc#1227595)
|
||||
* CVE-2024-39614: Potential denial-of-service through
|
||||
django.utils.translation.get_supported_language_variant()
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Apr 18 06:39:36 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add fix-safemimetext-set_payload.patch, to support python 3.11.9+
|
||||
(gh#django/django@b231bcd19e57, bsc#1222880)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Mar 4 14:05:28 UTC 2024 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
|
||||
- Update to 4.2.11 (CVE-2024-27351, bsc#1220358)
|
||||
* CVE-2024-27351: Potential regular expression denial-of-service in
|
||||
django.utils.text.Truncator.words()
|
||||
* Fixed a regression in Django 4.2.10 where intcomma template filter
|
||||
could return a leading comma for string representation of floats
|
||||
- Remove python3122.patch, already upstream
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Feb 9 10:18:37 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add python3122.patch to fix tests with python 3.12.2
|
||||
gh#django/django#17843
|
||||
- Update to 4.2.10 (bsc#1219683, CVE-2024-24680):
|
||||
- Django 4.2.10 fixes a security issue with severity "moderate" in
|
||||
4.2.9.
|
||||
CVE-2024-24680: Potential denial-of-service in intcomma template
|
||||
filter The intcomma template filter was subject to a potential
|
||||
denial-of-service attack when used with very long strings.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 4 09:27:51 UTC 2024 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
|
||||
- Update to 4.2.9:
|
||||
* Fixed a regression in Django 4.2.8 where admin fields on the same
|
||||
line could overflow the page and become non-interactive
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Dec 4 10:21:00 UTC 2023 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
|
||||
- Update to 4.2.8
|
||||
* Fixed a regression in Django 4.2 that caused makemigrations
|
||||
--check to stop displaying pending migrations
|
||||
* Fixed a regression in Django 4.2 that caused a crash of
|
||||
QuerySet.aggregate() with aggregates referencing other aggregates
|
||||
or window functions through conditional expressions
|
||||
* Fixed a regression in Django 4.2 that caused a crash when
|
||||
annotating a QuerySet with a Window expressions composed of a
|
||||
partition_by clause mixing field types and aggregation expressions
|
||||
* Fixed a regression in Django 4.2 where the admin’s change list
|
||||
page had misaligned pagination links and inputs when using
|
||||
list_editable
|
||||
* Fixed a regression in Django 4.2 where checkboxes in the admin
|
||||
would be centered on narrower screen widths
|
||||
* Fixed a regression in Django 4.2 that caused a crash of querysets
|
||||
with aggregations on MariaDB when the ONLY_FULL_GROUP_BY SQL mode
|
||||
was enabled
|
||||
* Fixed a regression in Django 4.2 where the admin’s read-only
|
||||
password widget and some help texts were incorrectly aligned at
|
||||
tablet widths
|
||||
* Fixed a regression in Django 4.2 that caused a migration crash on
|
||||
SQLite when altering unsupported Meta.db_table_comment
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Nov 27 12:20:48 UTC 2023 - Dirk Müller <dmueller@suse.com>
|
||||
|
||||
- add dirty-hack-remove-assert.patch from fedora to fix
|
||||
minor test failure with python 3.12
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Nov 1 08:12:59 UTC 2023 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
|
||||
- Update to 4.2.7
|
||||
* Fixed a regression in Django 4.2 that caused a crash of
|
||||
QuerySet.aggregate() with aggregates referencing expressions
|
||||
containing subqueries
|
||||
* Restored, following a regression in Django 4.2, creating
|
||||
varchar/text_pattern_ops indexes on CharField and TextField with
|
||||
deterministic collations on PostgreSQL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Oct 16 08:33:05 UTC 2023 - Daniel Garcia Moreno <daniel.garcia@suse.com>
|
||||
@ -117,7 +222,8 @@ Tue Jun 6 06:35:28 UTC 2023 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
Thu May 4 07:02:58 UTC 2023 - Alberto Planas Dominguez <aplanas@suse.com>
|
||||
|
||||
- Update to 4.2.1
|
||||
+ CVE-2023-31047: Potential bypass of validation when uploading multiple files using one form field
|
||||
+ CVE-2023-31047: Potential bypass of validation when uploading
|
||||
multiple files using one form field (bsc#1210866)
|
||||
+ Bugfixes
|
||||
* Fixed a regression in Django 4.2 that caused a crash of
|
||||
QuerySet.defer() when deferring fields by attribute names
|
||||
|
@ -1,90 +1,121 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQGiBErcoVkRBACt1HBsLQQ9HeRHrqMbYfWEW3d0KoWzjKU9ZW59oq8ceVCYfhyT
|
||||
ZKxyLobyed+NhL3SJCE5e4hs5UfyBdS4c8I97MFDKCA5TBu3pMnYGxWje3fSwP6o
|
||||
RGcP8Ji4/tISclyGrkMruDNzpT93R8H/SixPGFcH7kCp4xQxPBc0esdU4wCg1azF
|
||||
kUuFijNryusT+i58hVE3dMkD/iAfCh4bcLyZ8aygLZxg3bn3YauJASEjuqVXUgTB
|
||||
diBdhXnldq0xs2IwQJY1paAajXf5FsjlTVQrQWMtTQ5qWKpQr0lAanufnEDNu6GW
|
||||
orWBzLaSWQWEkcRALmZS6MBkmVCx/JiIvt0sUxrG4boQ6qYlQYZsaHaAMUZT997v
|
||||
1ktqA/4kPUfV2gqJuVzWwbhrKhAyhSivmhhe+1lUFa7phRmoMNw7/jXi9OV1lmL2
|
||||
ty+0LkeCXUChrXarey4AnPI58aR0xshiAxGEI2jPi+vWkgGblOG3TBoZBH5jV+d2
|
||||
/5mmlCs/KkJkdsN+LXR3m5o/oFs7MgGD8pxa1jwK9xcu1xKIqrQyTmF0YWxpYSBC
|
||||
aWRhcnQgKG5lc3NpdGEpIDxuYXRhbGlhYmlkYXJ0QGdtYWlsLmNvbT6IYgQTEQIA
|
||||
IgUCTG1snwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQrlwdYDo57Zf7
|
||||
lQCeIHmWQQek0zboTqMuy60phrUIzowAn0ONlnzzL0oWiNUpbY8nDsernILWiGAE
|
||||
ExECACAFAkrcoVkCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRCuXB1gOjnt
|
||||
l5FdAKCSLwUJNZXs3WXqKabi2adRcdqZ8gCeLgbbqJ2Dqqaeb3tXK6zWC7ZO9CK0
|
||||
NE5hdGFsaWEgQmlkYXJ0IChuZXNzaXRhKSA8bmF0YWxpYS5iaWRhcnRAdWJ1bnR1
|
||||
LmNvbT6IZQQTEQIAJQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AFAk8oONcC
|
||||
GQEACgkQrlwdYDo57ZejrgCdFyBg4VipDYmoQ5eOpXe4Vegiwl4AoK00YytEeMvO
|
||||
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
|
||||
eAgTzM5bOOqtGR7VR2hlCM4I4k2D0Y/snh2HzA==
|
||||
=ul9f
|
||||
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
|
||||
mSNZUt/duetfr8yS4yBrC5IMCo5nvfBpu8E=
|
||||
=DZRV
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# spec file for package python-Django
|
||||
#
|
||||
# Copyright (c) 2023 SUSE LLC
|
||||
# Copyright (c) 2024 SUSE LLC
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
@ -24,7 +24,7 @@
|
||||
%{?sle15_python_module_pythons}
|
||||
Name: python-Django
|
||||
# We want support LTS versions of Django - numbered 2.2 -> 3.2 -> 4.2 etc
|
||||
Version: 4.2.6
|
||||
Version: 4.2.11
|
||||
Release: 0
|
||||
Summary: A high-level Python Web framework
|
||||
License: BSD-3-Clause
|
||||
@ -34,9 +34,32 @@ Source1: https://media.djangoproject.com/pgp/Django-%{version}.checksum.t
|
||||
Source2: %{name}.keyring
|
||||
Source99: python-Django-rpmlintrc
|
||||
# PATCH-FIX-UPSTREAM https://github.com/django/django/commit/da2f8e8257d1bea4215381684ca4abfcee333c43 Refs #34118 -- Improved sanitize_address() error message for tuple with empty strings.
|
||||
Patch: sanitize_address.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-27351.patch bsc#1220358
|
||||
Patch1: CVE-2024-27351.patch
|
||||
Patch0: sanitize_address.patch
|
||||
# PATCH-FIX-OPENSUSE: ignore minor failure on Python 3.12
|
||||
Patch1: dirty-hack-remove-assert.patch
|
||||
# PATCH-FIX-UPSTREAM: fix-safemimetext-set_payload.patch, gh#django/django@b231bcd19e57
|
||||
# Add support for python 3.11.9+
|
||||
Patch2: fix-safemimetext-set_payload.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-38875.patch bsc#1227590
|
||||
Patch3: CVE-2024-38875.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-39329.patch bsc#1227593
|
||||
Patch4: CVE-2024-39329.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-39330.patch bsc#1227594
|
||||
Patch5: CVE-2024-39330.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-39614.patch bsc#1227595
|
||||
Patch6: CVE-2024-39614.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-41989.patch bsc#1228629
|
||||
Patch7: CVE-2024-41989.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-41990.patch bsc#1228630
|
||||
Patch8: CVE-2024-41990.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-41991.patch bsc#1228631
|
||||
Patch9: CVE-2024-41991.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-42005.patch bsc#1228632
|
||||
Patch10: CVE-2024-42005.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-45230.patch bsc#1229823
|
||||
Patch11: CVE-2024-45230.patch
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-45231.patch bsc#1229824
|
||||
Patch12: CVE-2024-45231.patch
|
||||
BuildRequires: %{python_module Jinja2 >= 2.9.2}
|
||||
BuildRequires: %{python_module Pillow >= 6.2.0}
|
||||
BuildRequires: %{python_module PyYAML}
|
||||
@ -68,7 +91,7 @@ Requires: python-pytz
|
||||
Requires: python-setuptools
|
||||
Requires: python-sqlparse >= 0.3.1
|
||||
Requires(post): update-alternatives
|
||||
Requires(postun):update-alternatives
|
||||
Requires(postun): update-alternatives
|
||||
Recommends: python-Jinja2 >= 2.9.2
|
||||
Recommends: python-PyYAML
|
||||
Recommends: python-geoip2
|
||||
|
Loading…
Reference in New Issue
Block a user