From 02766aa3ec9fcd65a7384b3b37f93eae23dee437 Mon Sep 17 00:00:00 2001
From: alan
Date: Mon, 24 Jul 2023 16:14:42 -0700
Subject: [PATCH] Apply all patches up to CVE-2023-36053
---
django/core/validators.py | 10 +++++++++-
django/db/backends/sqlite3/schema.py | 2 ++
django/forms/fields.py | 6 ++++++
tests/forms_tests/tests/test_fields.py | 5 ++++-
tests/forms_tests/tests/test_forms.py | 18 ++++++++++++------
tests/requests/tests.py | 2 ++
tests/validators/tests.py | 6 ++++++
7 files changed, 41 insertions(+), 8 deletions(-)
diff --git a/django/core/validators.py b/django/core/validators.py
index 852ff49358..94ec34ea03 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -104,6 +104,8 @@ class URLValidator(RegexValidator):
r'\Z', re.IGNORECASE)
message = _('Enter a valid URL.')
schemes = ['http', 'https', 'ftp', 'ftps']
+ unsafe_chars = frozenset('\t\r\n')
+ max_length = 2048
def __init__(self, schemes=None, **kwargs):
super(URLValidator, self).__init__(**kwargs)
@@ -112,6 +114,10 @@ class URLValidator(RegexValidator):
def __call__(self, value):
value = force_text(value)
+ if not isinstance(value, str) or len(value) > self.max_length:
+ raise ValidationError(self.message, code=self.code, params={'value': value})
+ if self.unsafe_chars.intersection(value):
+ raise ValidationError(self.message, code=self.code)
# Check first if the scheme is valid
scheme = value.split('://')[0].lower()
if scheme not in self.schemes:
@@ -183,7 +189,9 @@ class EmailValidator(object):
def __call__(self, value):
value = force_text(value)
- if not value or '@' not in value:
+ # The maximum length of an email is 320 characters per RFC 3696
+ # section 3.
+ if not value or '@' not in value or len(value) > 320:
raise ValidationError(self.message, code=self.code)
user_part, domain_part = value.rsplit('@', 1)
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py
index 9d9eb3c3ba..d7e10d817e 100644
--- a/django/db/backends/sqlite3/schema.py
+++ b/django/db/backends/sqlite3/schema.py
@@ -24,10 +24,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
c.execute('PRAGMA foreign_keys')
self._initial_pragma_fk = c.fetchone()[0]
c.execute('PRAGMA foreign_keys = 0')
+ self.connection.cursor().execute('PRAGMA legacy_alter_table = ON')
return super(DatabaseSchemaEditor, self).__enter__()
def __exit__(self, exc_type, exc_value, traceback):
super(DatabaseSchemaEditor, self).__exit__(exc_type, exc_value, traceback)
+ self.connection.cursor().execute('PRAGMA legacy_alter_table = OFF')
with self.connection.cursor() as c:
# Restore initial FK setting - PRAGMA values can't be parametrized
c.execute('PRAGMA foreign_keys = %s' % int(self._initial_pragma_fk))
diff --git a/django/forms/fields.py b/django/forms/fields.py
index e59cf81890..d5040bc376 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -546,6 +546,12 @@ class EmailField(CharField):
widget = EmailInput
default_validators = [validators.validate_email]
+ def __init__(self, *args, **kwargs):
+ # The default maximum length of an email is 320 characters per RFC 3696
+ # section 3.
+ kwargs.setdefault("max_length", 320)
+ super(EmailField, self).__init__(*args, strip=True, **kwargs)
+
def clean(self, value):
value = self.to_python(value).strip()
return super(EmailField, self).clean(value)
diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py
index 17b8756ddb..93b078bb6e 100644
--- a/tests/forms_tests/tests/test_fields.py
+++ b/tests/forms_tests/tests/test_fields.py
@@ -805,7 +805,10 @@ class FieldsTests(SimpleTestCase):
def test_emailfield_1(self):
f = EmailField()
- self.assertWidgetRendersTo(f, '')
+ self.assertEqual(f.max_length, 320)
+ self.assertWidgetRendersTo(
+ f, ''
+ )
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual('person@example.com', f.clean('person@example.com'))
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index 6492b1eda0..dd3c6b7682 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -416,11 +416,17 @@ class FormsTestCase(SimpleTestCase):
get_spam = BooleanField()
f = SignupForm(auto_id=False)
- self.assertHTMLEqual(str(f['email']), '')
+ self.assertHTMLEqual(
+ str(f["email"]),
+ '',
+ )
self.assertHTMLEqual(str(f['get_spam']), '')
f = SignupForm({'email': 'test@example.com', 'get_spam': True}, auto_id=False)
- self.assertHTMLEqual(str(f['email']), '')
+ self.assertHTMLEqual(
+ str(f["email"]),
+ '',
+ )
self.assertHTMLEqual(str(f['get_spam']), '')
# 'True' or 'true' should be rendered without a value attribute
@@ -2595,7 +2601,7 @@ Good luck picking a username that doesn't already exist.
-
+
"""
)
@@ -2611,7 +2617,7 @@ Good luck picking a username that doesn't already exist.
-
+
"""
@@ -2630,7 +2636,7 @@ Good luck picking a username that doesn't already exist.
|
- |
+
|
|
"""
@@ -3216,7 +3222,7 @@ Good luck picking a username that doesn't already exist.
f = CommentForm(data, auto_id=False, error_class=DivErrorList)
self.assertHTMLEqual(f.as_p(), """Name:
Enter a valid email address.
-Email:
+Email:
Comment:
""")
diff --git a/tests/requests/tests.py b/tests/requests/tests.py
index 772ddc51f7..8deecd5c7c 100644
--- a/tests/requests/tests.py
+++ b/tests/requests/tests.py
@@ -192,11 +192,13 @@ class RequestsTests(SimpleTestCase):
"Cookie will expire when an distant expiration time is provided"
response = HttpResponse()
response.set_cookie('datetime', expires=datetime(2028, 1, 1, 4, 5, 6))
+ response.set_cookie('datetime', expires=datetime(2038, 1, 1, 4, 5, 6))
datetime_cookie = response.cookies['datetime']
self.assertIn(
datetime_cookie['expires'],
# Slight time dependency; refs #23450
('Sat, 01-Jan-2028 04:05:06 GMT', 'Sat, 01-Jan-2028 04:05:07 GMT')
+ ('Fri, 01-Jan-2038 04:05:06 GMT', 'Fri, 01-Jan-2038 04:05:07 GMT')
)
def test_max_age_expiration(self):
diff --git a/tests/validators/tests.py b/tests/validators/tests.py
index f696f8e573..6e774cedd7 100644
--- a/tests/validators/tests.py
+++ b/tests/validators/tests.py
@@ -54,6 +54,7 @@ TEST_DATA = [
(validate_email, 'example@atm.%s' % ('a' * 64), ValidationError),
(validate_email, 'example@%s.atm.%s' % ('b' * 64, 'a' * 63), ValidationError),
+ (validate_email, "example@%scom" % (("a" * 63 + ".") * 100), ValidationError),
(validate_email, None, ValidationError),
(validate_email, '', ValidationError),
(validate_email, 'abc', ValidationError),
@@ -207,6 +208,11 @@ TEST_DATA = [
(URLValidator(EXTENDED_SCHEMES), 'git://example.com/', None),
(URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError),
+ (
+ URLValidator(),
+ "http://example." + ("a" * 63 + ".") * 1000 + "com",
+ ValidationError,
+ ),
# Trailing newlines not accepted
(URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
(URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),