Skip to content

Commit 31f4bd3

Browse files
RealOrangeOnenessita
authored andcommitted
[5.1.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 9579517 from main.
1 parent 363d256 commit 31f4bd3

File tree

5 files changed

+76
-9
lines changed

5 files changed

+76
-9
lines changed

django/views/generic/base.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.urls import reverse
1515
from django.utils.decorators import classonlymethod
1616
from django.utils.functional import classproperty
17+
from django.utils.log import log_response
1718

1819
logger = logging.getLogger("django.request")
1920

@@ -143,13 +144,14 @@ def dispatch(self, request, *args, **kwargs):
143144
return handler(request, *args, **kwargs)
144145

145146
def http_method_not_allowed(self, request, *args, **kwargs):
146-
logger.warning(
147+
response = HttpResponseNotAllowed(self._allowed_methods())
148+
log_response(
147149
"Method Not Allowed (%s): %s",
148150
request.method,
149151
request.path,
150-
extra={"status_code": 405, "request": request},
152+
response=response,
153+
request=request,
151154
)
152-
response = HttpResponseNotAllowed(self._allowed_methods())
153155

154156
if self.view_is_async:
155157

@@ -261,10 +263,9 @@ def get(self, request, *args, **kwargs):
261263
else:
262264
return HttpResponseRedirect(url)
263265
else:
264-
logger.warning(
265-
"Gone: %s", request.path, extra={"status_code": 410, "request": request}
266-
)
267-
return HttpResponseGone()
266+
response = HttpResponseGone()
267+
log_response("Gone: %s", request.path, response=response, request=request)
268+
return response
268269

269270
def head(self, request, *args, **kwargs):
270271
return self.get(request, *args, **kwargs)

docs/releases/4.2.23.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
===========================
2+
Django 4.2.23 release notes
3+
===========================
4+
5+
*June 10, 2025*
6+
7+
Django 4.2.23 fixes a potential log injection issue in 4.2.22.
8+
9+
Bugfixes
10+
========
11+
12+
* Fixed a log injection possibility by migrating remaining response logging
13+
to ``django.utils.log.log_response()``, which safely escapes arguments such
14+
as the request path to prevent unsafe log output (:cve:`2025-48432`).

docs/releases/5.1.11.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
===========================
2+
Django 5.1.11 release notes
3+
===========================
4+
5+
*June 10, 2025*
6+
7+
Django 5.1.11 fixes a potential log injection issue in 5.1.10.
8+
9+
Bugfixes
10+
========
11+
12+
* Fixed a log injection possibility by migrating remaining response logging
13+
to ``django.utils.log.log_response()``, which safely escapes arguments such
14+
as the request path to prevent unsafe log output (:cve:`2025-48432`).

docs/releases/index.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
2525
.. toctree::
2626
:maxdepth: 1
2727

28+
5.1.11
2829
5.1.10
2930
5.1.9
3031
5.1.8
@@ -64,6 +65,7 @@ versions of the documentation contain the release notes for any later releases.
6465
.. toctree::
6566
:maxdepth: 1
6667

68+
4.2.23
6769
4.2.22
6870
4.2.21
6971
4.2.20

tests/generic_views/test_base.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import logging
12
import time
23

4+
from logging_tests.tests import LoggingAssertionMixin
5+
36
from django.core.exceptions import ImproperlyConfigured
47
from django.http import HttpResponse
58
from django.test import RequestFactory, SimpleTestCase, override_settings
@@ -63,7 +66,7 @@ def get(self, request):
6366
return self
6467

6568

66-
class ViewTest(SimpleTestCase):
69+
class ViewTest(LoggingAssertionMixin, SimpleTestCase):
6770
rf = RequestFactory()
6871

6972
def _assert_simple(self, response):
@@ -297,6 +300,25 @@ def test_direct_instantiation(self):
297300
response = view.dispatch(self.rf.head("/"))
298301
self.assertEqual(response.status_code, 405)
299302

303+
def test_method_not_allowed_response_logged(self):
304+
for path, escaped in [
305+
("/foo/", "/foo/"),
306+
(r"/%1B[1;31mNOW IN RED!!!1B[0m/", r"/\x1b[1;31mNOW IN RED!!!1B[0m/"),
307+
]:
308+
with self.subTest(path=path):
309+
request = self.rf.get(path, REQUEST_METHOD="BOGUS")
310+
with self.assertLogs("django.request", "WARNING") as handler:
311+
response = SimpleView.as_view()(request)
312+
313+
self.assertLogRecord(
314+
handler,
315+
f"Method Not Allowed (BOGUS): {escaped}",
316+
logging.WARNING,
317+
405,
318+
request,
319+
)
320+
self.assertEqual(response.status_code, 405)
321+
300322

301323
@override_settings(ROOT_URLCONF="generic_views.urls")
302324
class TemplateViewTest(SimpleTestCase):
@@ -425,7 +447,7 @@ def test_extra_context(self):
425447

426448

427449
@override_settings(ROOT_URLCONF="generic_views.urls")
428-
class RedirectViewTest(SimpleTestCase):
450+
class RedirectViewTest(LoggingAssertionMixin, SimpleTestCase):
429451
rf = RequestFactory()
430452

431453
def test_no_url(self):
@@ -549,6 +571,20 @@ def test_direct_instantiation(self):
549571
response = view.dispatch(self.rf.head("/foo/"))
550572
self.assertEqual(response.status_code, 410)
551573

574+
def test_gone_response_logged(self):
575+
for path, escaped in [
576+
("/foo/", "/foo/"),
577+
(r"/%1B[1;31mNOW IN RED!!!1B[0m/", r"/\x1b[1;31mNOW IN RED!!!1B[0m/"),
578+
]:
579+
with self.subTest(path=path):
580+
request = self.rf.get(path)
581+
with self.assertLogs("django.request", "WARNING") as handler:
582+
RedirectView().dispatch(request)
583+
584+
self.assertLogRecord(
585+
handler, f"Gone: {escaped}", logging.WARNING, 410, request
586+
)
587+
552588

553589
class GetContextDataTest(SimpleTestCase):
554590
def test_get_context_data_super(self):

0 commit comments

Comments
 (0)