Sync from SUSE:ALP:Source:Standard:1.0 python-Django revision 97369dd51bd1a21aa7bbf35c864aa360

This commit is contained in:
Adrian Schröter 2024-11-15 10:48:15 +01:00
parent 7d09936fd7
commit 572e03dca0
6 changed files with 590 additions and 0 deletions

164
CVE-2024-38875.patch Normal file
View 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
View 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
View 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
View 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 MDNs 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 MDNs
+# 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

View File

@ -1,3 +1,19 @@
-------------------------------------------------------------------
Fri Jul 12 13:41:03 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 Feb 29 13:19:00 UTC 2024 - Alberto Planas Dominguez <aplanas@suse.com> Thu Feb 29 13:19:00 UTC 2024 - Alberto Planas Dominguez <aplanas@suse.com>

View File

@ -37,6 +37,14 @@ Source99: python-Django-rpmlintrc
Patch: sanitize_address.patch Patch: sanitize_address.patch
# PATCH-FIX-UPSTREAM CVE-2024-27351.patch bsc#1220358 # PATCH-FIX-UPSTREAM CVE-2024-27351.patch bsc#1220358
Patch1: CVE-2024-27351.patch Patch1: CVE-2024-27351.patch
# PATCH-FIX-UPSTREAM CVE-2024-38875.patch bsc#1227590
Patch2: CVE-2024-38875.patch
# PATCH-FIX-UPSTREAM CVE-2024-39329.patch bsc#1227593
Patch3: CVE-2024-39329.patch
# PATCH-FIX-UPSTREAM CVE-2024-39330.patch bsc#1227594
Patch4: CVE-2024-39330.patch
# PATCH-FIX-UPSTREAM CVE-2024-39614.patch bsc#1227595
Patch5: CVE-2024-39614.patch
BuildRequires: %{python_module Jinja2 >= 2.9.2} BuildRequires: %{python_module Jinja2 >= 2.9.2}
BuildRequires: %{python_module Pillow >= 6.2.0} BuildRequires: %{python_module Pillow >= 6.2.0}
BuildRequires: %{python_module PyYAML} BuildRequires: %{python_module PyYAML}