+61
-9
lines changedFilter options
+61
-9
lines changed Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
14
14
from django.urls import reverse
15
15
from django.utils.decorators import classonlymethod
16
16
from django.utils.functional import classproperty
17
+
from django.utils.log import log_response
17
18
18
19
logger = logging.getLogger("django.request")
19
20
@@ -143,13 +144,14 @@ def dispatch(self, request, *args, **kwargs):
143
144
return handler(request, *args, **kwargs)
144
145
145
146
def http_method_not_allowed(self, request, *args, **kwargs):
146
-
logger.warning(
147
+
response = HttpResponseNotAllowed(self._allowed_methods())
148
+
log_response(
147
149
"Method Not Allowed (%s): %s",
148
150
request.method,
149
151
request.path,
150
-
extra={"status_code": 405, "request": request},
152
+
response=response,
153
+
request=request,
151
154
)
152
-
response = HttpResponseNotAllowed(self._allowed_methods())
153
155
154
156
if self.view_is_async:
155
157
@@ -261,10 +263,9 @@ def get(self, request, *args, **kwargs):
261
263
else:
262
264
return HttpResponseRedirect(url)
263
265
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
268
269
269
270
def head(self, request, *args, **kwargs):
270
271
return self.get(request, *args, **kwargs)
Original file line number Diff line number Diff 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`).
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ versions of the documentation contain the release notes for any later releases.
26
26
.. toctree::
27
27
:maxdepth: 1
28
28
29
+
4.2.23
29
30
4.2.22
30
31
4.2.21
31
32
4.2.20
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
1
+
import logging
1
2
import time
2
3
4
+
from logging_tests.tests import LoggingAssertionMixin
5
+
3
6
from django.core.exceptions import ImproperlyConfigured
4
7
from django.http import HttpResponse
5
8
from django.test import RequestFactory, SimpleTestCase, override_settings
@@ -63,7 +66,7 @@ def get(self, request):
63
66
return self
64
67
65
68
66
-
class ViewTest(SimpleTestCase):
69
+
class ViewTest(LoggingAssertionMixin, SimpleTestCase):
67
70
rf = RequestFactory()
68
71
69
72
def _assert_simple(self, response):
@@ -297,6 +300,25 @@ def test_direct_instantiation(self):
297
300
response = view.dispatch(self.rf.head("/"))
298
301
self.assertEqual(response.status_code, 405)
299
302
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
+
300
322
301
323
@override_settings(ROOT_URLCONF="generic_views.urls")
302
324
class TemplateViewTest(SimpleTestCase):
@@ -425,7 +447,7 @@ def test_extra_context(self):
425
447
426
448
427
449
@override_settings(ROOT_URLCONF="generic_views.urls")
428
-
class RedirectViewTest(SimpleTestCase):
450
+
class RedirectViewTest(LoggingAssertionMixin, SimpleTestCase):
429
451
rf = RequestFactory()
430
452
431
453
def test_no_url(self):
@@ -549,6 +571,20 @@ def test_direct_instantiation(self):
549
571
response = view.dispatch(self.rf.head("/foo/"))
550
572
self.assertEqual(response.status_code, 410)
551
573
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
+
552
588
553
589
class GetContextDataTest(SimpleTestCase):
554
590
def test_get_context_data_super(self):
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4