0
0
mirror of https://github.com/django/django.git synced 2024-11-28 21:43:13 +01:00
django/tests/requests_tests/test_data_upload_settings.py
Markus Holtermann 85ac33591c Fixed CVE-2023-24580 -- Prevented DoS with too many uploaded files.
Thanks to Jakob Ackermann for the report.
2023-02-14 08:18:40 +01:00

271 lines
8.9 KiB
Python

from io import BytesIO
from django.core.exceptions import (
RequestDataTooBig,
TooManyFieldsSent,
TooManyFilesSent,
)
from django.core.handlers.wsgi import WSGIRequest
from django.test import SimpleTestCase
from django.test.client import FakePayload
TOO_MANY_FIELDS_MSG = (
"The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS."
)
TOO_MANY_FILES_MSG = (
"The number of files exceeded settings.DATA_UPLOAD_MAX_NUMBER_FILES."
)
TOO_MUCH_DATA_MSG = "Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE."
class DataUploadMaxMemorySizeFormPostTests(SimpleTestCase):
def setUp(self):
payload = FakePayload("a=1&a=2&a=3\r\n")
self.request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "application/x-www-form-urlencoded",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
def test_size_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=12):
with self.assertRaisesMessage(RequestDataTooBig, TOO_MUCH_DATA_MSG):
self.request._load_post_and_files()
def test_size_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=13):
self.request._load_post_and_files()
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=None):
self.request._load_post_and_files()
class DataUploadMaxMemorySizeMultipartPostTests(SimpleTestCase):
def setUp(self):
payload = FakePayload(
"\r\n".join(
[
"--boundary",
'Content-Disposition: form-data; name="name"',
"",
"value",
"--boundary--",
]
)
)
self.request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "multipart/form-data; boundary=boundary",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
def test_size_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=10):
with self.assertRaisesMessage(RequestDataTooBig, TOO_MUCH_DATA_MSG):
self.request._load_post_and_files()
def test_size_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=11):
self.request._load_post_and_files()
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=None):
self.request._load_post_and_files()
def test_file_passes(self):
payload = FakePayload(
"\r\n".join(
[
"--boundary",
'Content-Disposition: form-data; name="file1"; '
'filename="test.file"',
"",
"value",
"--boundary--",
]
)
)
request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "multipart/form-data; boundary=boundary",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=1):
request._load_post_and_files()
self.assertIn("file1", request.FILES, "Upload file not present")
class DataUploadMaxMemorySizeGetTests(SimpleTestCase):
def setUp(self):
self.request = WSGIRequest(
{
"REQUEST_METHOD": "GET",
"wsgi.input": BytesIO(b""),
"CONTENT_LENGTH": 3,
}
)
def test_data_upload_max_memory_size_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=2):
with self.assertRaisesMessage(RequestDataTooBig, TOO_MUCH_DATA_MSG):
self.request.body
def test_size_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=3):
self.request.body
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_MEMORY_SIZE=None):
self.request.body
def test_empty_content_length(self):
self.request.environ["CONTENT_LENGTH"] = ""
self.request.body
class DataUploadMaxNumberOfFieldsGet(SimpleTestCase):
def test_get_max_fields_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=1):
with self.assertRaisesMessage(TooManyFieldsSent, TOO_MANY_FIELDS_MSG):
request = WSGIRequest(
{
"REQUEST_METHOD": "GET",
"wsgi.input": BytesIO(b""),
"QUERY_STRING": "a=1&a=2&a=3",
}
)
request.GET["a"]
def test_get_max_fields_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=3):
request = WSGIRequest(
{
"REQUEST_METHOD": "GET",
"wsgi.input": BytesIO(b""),
"QUERY_STRING": "a=1&a=2&a=3",
}
)
request.GET["a"]
class DataUploadMaxNumberOfFieldsMultipartPost(SimpleTestCase):
def setUp(self):
payload = FakePayload(
"\r\n".join(
[
"--boundary",
'Content-Disposition: form-data; name="name1"',
"",
"value1",
"--boundary",
'Content-Disposition: form-data; name="name2"',
"",
"value2",
"--boundary--",
]
)
)
self.request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "multipart/form-data; boundary=boundary",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
def test_number_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=1):
with self.assertRaisesMessage(TooManyFieldsSent, TOO_MANY_FIELDS_MSG):
self.request._load_post_and_files()
def test_number_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=2):
self.request._load_post_and_files()
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=None):
self.request._load_post_and_files()
class DataUploadMaxNumberOfFilesMultipartPost(SimpleTestCase):
def setUp(self):
payload = FakePayload(
"\r\n".join(
[
"--boundary",
(
'Content-Disposition: form-data; name="name1"; '
'filename="name1.txt"'
),
"",
"value1",
"--boundary",
(
'Content-Disposition: form-data; name="name2"; '
'filename="name2.txt"'
),
"",
"value2",
"--boundary--",
]
)
)
self.request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "multipart/form-data; boundary=boundary",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
def test_number_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=1):
with self.assertRaisesMessage(TooManyFilesSent, TOO_MANY_FILES_MSG):
self.request._load_post_and_files()
def test_number_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=2):
self.request._load_post_and_files()
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FILES=None):
self.request._load_post_and_files()
class DataUploadMaxNumberOfFieldsFormPost(SimpleTestCase):
def setUp(self):
payload = FakePayload("\r\n".join(["a=1&a=2&a=3", ""]))
self.request = WSGIRequest(
{
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": "application/x-www-form-urlencoded",
"CONTENT_LENGTH": len(payload),
"wsgi.input": payload,
}
)
def test_number_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=2):
with self.assertRaisesMessage(TooManyFieldsSent, TOO_MANY_FIELDS_MSG):
self.request._load_post_and_files()
def test_number_not_exceeded(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=3):
self.request._load_post_and_files()
def test_no_limit(self):
with self.settings(DATA_UPLOAD_MAX_NUMBER_FIELDS=None):
self.request._load_post_and_files()