File CVE-2025-48432-followup.patch of Package python-Django.40375

From b597d46bb19c8567615e62029210dab16c70db7d Mon Sep 17 00:00:00 2001
From: Jake Howard <git@theorangeone.net>
Date: Wed, 4 Jun 2025 16:08:46 +0100
Subject: [PATCH] [4.2.x] Refs CVE-2025-48432 -- Prevented log injection in
 remaining response logging.

Migrated remaining response-related logging to use the `log_response()`
helper to avoid potential log injection, to ensure untrusted values like
request paths are safely escaped.

Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>

Backport of 957951755259b412d5113333b32bf85871d29814 from main.
---
 django/views/generic/base.py     | 15 ++++++------
 docs/releases/4.2.23.txt         | 14 +++++++++++
 docs/releases/index.txt          |  1 +
 tests/generic_views/test_base.py | 40 ++++++++++++++++++++++++++++++--
 4 files changed, 61 insertions(+), 9 deletions(-)
 create mode 100644 docs/releases/4.2.23.txt

diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index 8f8f9397e8c0..8412288be1ce 100644
--- a/django/views/generic/base.py
+++ b/django/views/generic/base.py
@@ -14,6 +14,7 @@
 from django.urls import reverse
 from django.utils.decorators import classonlymethod
 from django.utils.functional import classproperty
+from django.utils.log import log_response
 
 logger = logging.getLogger("django.request")
 
@@ -143,13 +144,14 @@ def dispatch(self, request, *args, **kwargs):
         return handler(request, *args, **kwargs)
 
     def http_method_not_allowed(self, request, *args, **kwargs):
-        logger.warning(
+        response = HttpResponseNotAllowed(self._allowed_methods())
+        log_response(
             "Method Not Allowed (%s): %s",
             request.method,
             request.path,
-            extra={"status_code": 405, "request": request},
+            response=response,
+            request=request,
         )
-        response = HttpResponseNotAllowed(self._allowed_methods())
 
         if self.view_is_async:
 
@@ -261,10 +263,9 @@ def get(self, request, *args, **kwargs):
             else:
                 return HttpResponseRedirect(url)
         else:
-            logger.warning(
-                "Gone: %s", request.path, extra={"status_code": 410, "request": request}
-            )
-            return HttpResponseGone()
+            response = HttpResponseGone()
+            log_response("Gone: %s", request.path, response=response, request=request)
+            return response
 
     def head(self, request, *args, **kwargs):
         return self.get(request, *args, **kwargs)
diff --git a/docs/releases/4.2.23.txt b/docs/releases/4.2.23.txt
new file mode 100644
index 000000000000..e4232f9beaad
--- /dev/null
+++ b/docs/releases/4.2.23.txt
@@ -0,0 +1,14 @@
+===========================
+Django 4.2.23 release notes
+===========================
+
+*June 10, 2025*
+
+Django 4.2.23 fixes a potential log injection issue in 4.2.22.
+
+Bugfixes
+========
+
+* Fixed a log injection possibility by migrating remaining response logging
+  to ``django.utils.log.log_response()``, which safely escapes arguments such
+  as the request path to prevent unsafe log output (:cve:`2025-48432`).
diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py
index add485245a87..3cd1ae6a5c18 100644
--- a/tests/generic_views/test_base.py
+++ b/tests/generic_views/test_base.py
@@ -1,5 +1,8 @@
+import logging
 import time
 
+from logging_tests.tests import LoggingAssertionMixin
+
 from django.core.exceptions import ImproperlyConfigured
 from django.http import HttpResponse
 from django.test import RequestFactory, SimpleTestCase, override_settings
@@ -63,7 +66,7 @@ def get(self, request):
         return self
 
 
-class ViewTest(SimpleTestCase):
+class ViewTest(LoggingAssertionMixin, SimpleTestCase):
     rf = RequestFactory()
 
     def _assert_simple(self, response):
@@ -297,6 +300,25 @@ def test_direct_instantiation(self):
         response = view.dispatch(self.rf.head("/"))
         self.assertEqual(response.status_code, 405)
 
+    def test_method_not_allowed_response_logged(self):
+        for path, escaped in [
+            ("/foo/", "/foo/"),
+            (r"/%1B[1;31mNOW IN RED!!!1B[0m/", r"/\x1b[1;31mNOW IN RED!!!1B[0m/"),
+        ]:
+            with self.subTest(path=path):
+                request = self.rf.get(path, REQUEST_METHOD="BOGUS")
+                with self.assertLogs("django.request", "WARNING") as handler:
+                    response = SimpleView.as_view()(request)
+
+                self.assertLogRecord(
+                    handler,
+                    f"Method Not Allowed (BOGUS): {escaped}",
+                    logging.WARNING,
+                    405,
+                    request,
+                )
+                self.assertEqual(response.status_code, 405)
+
 
 @override_settings(ROOT_URLCONF="generic_views.urls")
 class TemplateViewTest(SimpleTestCase):
@@ -425,7 +447,7 @@ def test_extra_context(self):
 
 
 @override_settings(ROOT_URLCONF="generic_views.urls")
-class RedirectViewTest(SimpleTestCase):
+class RedirectViewTest(LoggingAssertionMixin, SimpleTestCase):
     rf = RequestFactory()
 
     def test_no_url(self):
@@ -549,6 +571,20 @@ def test_direct_instantiation(self):
         response = view.dispatch(self.rf.head("/foo/"))
         self.assertEqual(response.status_code, 410)
 
+    def test_gone_response_logged(self):
+        for path, escaped in [
+            ("/foo/", "/foo/"),
+            (r"/%1B[1;31mNOW IN RED!!!1B[0m/", r"/\x1b[1;31mNOW IN RED!!!1B[0m/"),
+        ]:
+            with self.subTest(path=path):
+                request = self.rf.get(path)
+                with self.assertLogs("django.request", "WARNING") as handler:
+                    RedirectView().dispatch(request)
+
+                self.assertLogRecord(
+                    handler, f"Gone: {escaped}", logging.WARNING, 410, request
+                )
+
 
 class GetContextDataTest(SimpleTestCase):
     def test_get_context_data_super(self):
openSUSE Build Service is sponsored by