diff --git a/0001-fix-CVE-2020-13596.patch b/0001-fix-CVE-2020-13596.patch new file mode 100644 index 0000000000000000000000000000000000000000..4f6dde3323672756a8585b2c5e670708dff31d10 --- /dev/null +++ b/0001-fix-CVE-2020-13596.patch @@ -0,0 +1,85 @@ +From 6d61860b22875f358fac83d903dc629897934815 Mon Sep 17 00:00:00 2001 +From: Jon Dufresne <jon.dufresne@gmail.com> +Date: Wed, 18 Sep 2024 20:37:30 +0800 +Subject: [PATCH] [2.0.x] Fixed CVE-2020-13596 -- Fixed potential XSS in admin + ForeignKeyRawIdWidget. + +--- + django/contrib/admin/widgets.py | 2 +- + docs/releases/2.2.13.txt | 8 ++++++++ + tests/admin_widgets/models.py | 6 ++++++ + tests/admin_widgets/tests.py | 9 +++++++++ + 4 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py +index 3d11a40..40dba16 100644 +--- a/django/contrib/admin/widgets.py ++++ b/django/contrib/admin/widgets.py +@@ -11,7 +11,7 @@ from django.core.validators import URLValidator + from django.db.models import CASCADE, UUIDField + from django.urls import reverse + from django.urls.exceptions import NoReverseMatch +-from django.utils.html import smart_urlquote ++from django.utils.http import urlencode + from django.utils.http import urlencode + from django.utils.text import Truncator + from django.utils.translation import get_language +diff --git a/docs/releases/2.2.13.txt b/docs/releases/2.2.13.txt +index 3e455e7..d4a17d8 100644 +--- a/docs/releases/2.2.13.txt ++++ b/docs/releases/2.2.13.txt +@@ -6,6 +6,14 @@ Django 2.2.13 release notes + + Django 2.2.13 fixes two security issues and a regression in 2.2.12. + ++CVE-2020-13596: Possible XSS via admin ``ForeignKeyRawIdWidget`` ++================================================================ ++ ++Query parameters for the admin ``ForeignKeyRawIdWidget`` were not properly URL ++encoded, posing an XSS attack vector. ``ForeignKeyRawIdWidget`` now ++ensures query parameters are correctly URL encoded. ++ ++ + CVE-2020-13254: Potential data leakage via malformed memcached keys + =================================================================== + +diff --git a/tests/admin_widgets/models.py b/tests/admin_widgets/models.py +index 0113ecb..093ff5a 100644 +--- a/tests/admin_widgets/models.py ++++ b/tests/admin_widgets/models.py +@@ -29,6 +29,12 @@ class Member(models.Model): + def __str__(self): + return self.name + ++class UnsafeLimitChoicesTo(models.Model): ++ band = models.ForeignKey( ++ Band, ++ models.CASCADE, ++ limit_choices_to={'name': '"&><escapeme'}, ++ ) + + class Artist(models.Model): + pass +diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py +index 4281ed0..368e421 100644 +--- a/tests/admin_widgets/tests.py ++++ b/tests/admin_widgets/tests.py +@@ -649,6 +649,15 @@ class AdminFileWidgetTests(TestDataMixin, TestCase): + html=True, + ) + ++ def test_render_unsafe_limit_choices_to(self): ++ rel = UnsafeLimitChoicesTo._meta.get_field('band').remote_field ++ w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) ++ self.assertHTMLEqual( ++ w.render('test', None), ++ '<input type="text" name="test" class="vForeignKeyRawIdAdminField">\n' ++ '<a href="/admin_widgets/band/?name=%22%26%3E%3Cescapeme&_to_field=id" ' ++ 'class="related-lookup" id="lookup_id_test" title="Lookup"></a>' ++ ) + + @override_settings(ROOT_URLCONF="admin_widgets.urls") + class ForeignKeyRawIdWidgetTest(TestCase): +-- +2.43.0 + diff --git a/0002-fix-CVE-2024-45231.patch b/0002-fix-CVE-2024-45231.patch new file mode 100644 index 0000000000000000000000000000000000000000..a95699e113fc76b9c7d452ddb84ee51dc0623c21 --- /dev/null +++ b/0002-fix-CVE-2024-45231.patch @@ -0,0 +1,159 @@ +From bf4888d317ba4506d091eeac6e8b4f1fcc731199 Mon Sep 17 00:00:00 2001 +From: Natalia <124304+nessita@users.noreply.github.com> +Date: Tue, 8 Oct 2024 18:15:00 +0800 +Subject: [PATCH] fix 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. + + +--- + django/contrib/auth/forms.py | 9 ++++++++- + docs/ref/logging.txt | 13 +++++++++++++ + docs/releases/4.2.15.txt | 12 ++++++++++++ + docs/topics/auth/default.txt | 4 +++- + tests/auth_tests/test_forms.py | 21 +++++++++++++++++++++ + tests/mail/custombackend.py | 4 ++++ + 6 files changed, 61 insertions(+), 2 deletions(-) + +diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py +index 061dc81..7f85787 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 b11fb75..dd089e6 100644 +--- a/docs/ref/logging.txt ++++ b/docs/ref/logging.txt +@@ -204,6 +204,19 @@ 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.15.txt b/docs/releases/4.2.15.txt +index b1d4684..1b327d1 100644 +--- a/docs/releases/4.2.15.txt ++++ b/docs/releases/4.2.15.txt +@@ -43,3 +43,15 @@ Bugfixes + * Fixed a regression in Django 4.2.14 that caused a crash in + ``LocaleMiddleware`` when processing a language code over 500 characters + (:ticket:`35627`). ++ ++ ++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 5289024..ad840c5 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 81c56a4..d7470bc 100644 +--- a/tests/auth_tests/test_forms.py ++++ b/tests/auth_tests/test_forms.py +@@ -560,6 +560,27 @@ class AuthenticationFormTest(TestDataMixin, TestCase): + self.assertTrue(form.is_valid()) + self.assertEqual(form.non_field_errors(), []) + ++ @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_username_field_max_length_matches_user_model(self): + self.assertEqual(CustomEmailField._meta.get_field("username").max_length, 255) +diff --git a/tests/mail/custombackend.py b/tests/mail/custombackend.py +index 14e7f07..50d6eb5 100644 +--- a/tests/mail/custombackend.py ++++ b/tests/mail/custombackend.py +@@ -12,3 +12,7 @@ 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.43.0 + diff --git a/python-django.spec b/python-django.spec index ac31c22ebd5ede1bdfe9bf33ac003ebef19403a6..28914b159dd9767bf0a125f79e5451f9d4012988 100644 --- a/python-django.spec +++ b/python-django.spec @@ -1,12 +1,13 @@ %global _empty_manifest_terminate_build 0 Name: python-django Version: 4.2.15 -Release: 1 +Release: 3 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design. License: Apache-2.0 and Python-2.0 and BSD-3-Clause URL: https://www.djangoproject.com/ Source0: https://files.pythonhosted.org/packages/source/d/Django/Django-%{version}.tar.gz - +Patch0001: 0001-fix-CVE-2020-13596.patch +Patch0002: 0002-fix-CVE-2024-45231.patch BuildArch: noarch %description A high-level Python Web framework that encourages rapid development and clean, pragmatic design. @@ -72,6 +73,18 @@ mv %{buildroot}/doclist.lst . %{_docdir}/* %changelog +* Wed Oct 09 2024 changtao <changtao@kylinos..cn> - 4.2.15-3 +- Type: bugfix +- CVE: CVE-2024-45231 +- SUG: NA +- DESC: Fix CVE-2024-45231 + +* Thu Sep 19 2024 changtao <changtao@kylinos..cn> - 4.2.15-2 +- Type: bugfix +- CVE: CVE-2020-13596 +- SUG: NA +- DESC: Fix CVE-2020-13596 + * Thu Aug 08 2024 yaoxin <yao_xin001@hoperun.com> - 4.2.15-1 - Update to 4.2.15 * CVE-2024-41989: Memory exhaustion in ``django.utils.numberformat.floatformat()``