summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2023-08-25 21:27:22 +0200
committerGitHub <noreply@github.com>2023-08-25 21:27:22 +0200
commit11920e77959deaa65eb86ccc5d39da903fd3dd41 (patch)
tree10a328aff153fc7057fccb26cbc2dac1719349c9
parent9c37103a98d030be110eb9ba8b7ed32a47240b28 (diff)
Fixed #34709 -- Raised BadRequest for non-UTF-8 requests with the application/x-www-form-urlencoded content type.
Thanks Eki Xu for the report.
-rw-r--r--django/http/request.py15
-rw-r--r--docs/releases/5.0.txt4
-rw-r--r--tests/requests_tests/tests.py51
3 files changed, 57 insertions, 13 deletions
diff --git a/django/http/request.py b/django/http/request.py
index 86ea68c20c..fe15a173be 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -7,6 +7,7 @@ from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit
from django.conf import settings
from django.core import signing
from django.core.exceptions import (
+ BadRequest,
DisallowedHost,
ImproperlyConfigured,
RequestDataTooBig,
@@ -377,10 +378,16 @@ class HttpRequest:
self._mark_post_parse_error()
raise
elif self.content_type == "application/x-www-form-urlencoded":
- self._post, self._files = (
- QueryDict(self.body, encoding=self._encoding),
- MultiValueDict(),
- )
+ # According to RFC 1866, the "application/x-www-form-urlencoded"
+ # content type does not have a charset and should be always treated
+ # as UTF-8.
+ if self._encoding is not None and self._encoding.lower() != "utf-8":
+ raise BadRequest(
+ "HTTP requests with the 'application/x-www-form-urlencoded' "
+ "content type must be UTF-8 encoded."
+ )
+ self._post = QueryDict(self.body, encoding="utf-8")
+ self._files = MultiValueDict()
else:
self._post, self._files = (
QueryDict(encoding=self._encoding),
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index 0b65ee189a..7379289e42 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -580,6 +580,10 @@ Miscellaneous
* Executing SQL queries before the app registry has been fully populated now
raises :exc:`RuntimeWarning`.
+* :exc:`~django.core.exceptions.BadRequest` is raised for non-UTF-8 encoded
+ requests with the :mimetype:`application/x-www-form-urlencoded` content type.
+ See :rfc:`1866` for more details.
+
.. _deprecated-features-5.0:
Features deprecated in 5.0
diff --git a/tests/requests_tests/tests.py b/tests/requests_tests/tests.py
index 099f600867..228198ae8a 100644
--- a/tests/requests_tests/tests.py
+++ b/tests/requests_tests/tests.py
@@ -3,7 +3,7 @@ from io import BytesIO
from itertools import chain
from urllib.parse import urlencode
-from django.core.exceptions import DisallowedHost
+from django.core.exceptions import BadRequest, DisallowedHost
from django.core.handlers.wsgi import LimitedStream, WSGIRequest
from django.http import (
HttpHeaders,
@@ -369,10 +369,7 @@ class RequestsTests(SimpleTestCase):
)
self.assertEqual(request.POST, {"key": ["España"]})
- def test_alternate_charset_POST(self):
- """
- Test a POST with non-utf-8 payload encoding.
- """
+ def test_non_utf8_charset_POST_bad_request(self):
payload = FakePayload(urlencode({"key": "España".encode("latin-1")}))
request = WSGIRequest(
{
@@ -382,7 +379,30 @@ class RequestsTests(SimpleTestCase):
"wsgi.input": payload,
}
)
- self.assertEqual(request.POST, {"key": ["España"]})
+ msg = (
+ "HTTP requests with the 'application/x-www-form-urlencoded' content type "
+ "must be UTF-8 encoded."
+ )
+ with self.assertRaisesMessage(BadRequest, msg):
+ request.POST
+ with self.assertRaisesMessage(BadRequest, msg):
+ request.FILES
+
+ def test_utf8_charset_POST(self):
+ for charset in ["utf-8", "UTF-8"]:
+ with self.subTest(charset=charset):
+ payload = FakePayload(urlencode({"key": "España"}))
+ request = WSGIRequest(
+ {
+ "REQUEST_METHOD": "POST",
+ "CONTENT_LENGTH": len(payload),
+ "CONTENT_TYPE": (
+ f"application/x-www-form-urlencoded; charset={charset}"
+ ),
+ "wsgi.input": payload,
+ }
+ )
+ self.assertEqual(request.POST, {"key": ["España"]})
def test_body_after_POST_multipart_form_data(self):
"""
@@ -694,18 +714,31 @@ class RequestsTests(SimpleTestCase):
request.body
def test_set_encoding_clears_POST(self):
- payload = FakePayload("name=Hello Günter")
+ payload = FakePayload(
+ "\r\n".join(
+ [
+ f"--{BOUNDARY}",
+ 'Content-Disposition: form-data; name="name"',
+ "",
+ "Hello Günter",
+ f"--{BOUNDARY}--",
+ "",
+ ]
+ )
+ )
request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
- "CONTENT_TYPE": "application/x-www-form-urlencoded",
+ "CONTENT_TYPE": MULTIPART_CONTENT,
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
self.assertEqual(request.POST, {"name": ["Hello Günter"]})
request.encoding = "iso-8859-16"
- self.assertEqual(request.POST, {"name": ["Hello GĂŒnter"]})
+ # FIXME: POST should be accessible after changing the encoding
+ # (refs #14035).
+ # self.assertEqual(request.POST, {"name": ["Hello GĂŒnter"]})
def test_set_encoding_clears_GET(self):
payload = FakePayload("")