summaryrefslogtreecommitdiff
path: root/django/core/validators.py
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-05-06 08:45:23 +0200
committerGitHub <noreply@github.com>2021-05-06 08:45:23 +0200
commite1e81aa1c4427411e3c68facdd761229ffea6f6f (patch)
tree6afff14e0eba2452db3add415e60fe0219aec75e /django/core/validators.py
parenta708f39ce67af174df90c5b5e50ad1976cec7cb8 (diff)
downloaddjango-e1e81aa1c4427411e3c68facdd761229ffea6f6f.tar.gz
Fixed #32713, Fixed CVE-2021-32052 -- Prevented newlines and tabs from being accepted in URLValidator on Python 3.9.5+.
In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines and tabs from URLs [1, 2]. Unfortunately it created an issue in the URLValidator. URLValidator uses urllib.urlsplit() and urllib.urlunsplit() for creating a URL variant with Punycode which no longer contains newlines and tabs in Python 3.9.5+. As a consequence, the regular expression matched the URL (without unsafe characters) and the source value (with unsafe characters) was considered valid. [1] https://bugs.python.org/issue43882 and [2] https://github.com/python/cpython/commit/76cd81d60310d65d01f9d7b48a8985d8ab89c8b4
Diffstat (limited to 'django/core/validators.py')
-rw-r--r--django/core/validators.py3
1 files changed, 3 insertions, 0 deletions
diff --git a/django/core/validators.py b/django/core/validators.py
index a385819510..f9abec602c 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -92,6 +92,7 @@ class URLValidator(RegexValidator):
r'\Z', re.IGNORECASE)
message = _('Enter a valid URL.')
schemes = ['http', 'https', 'ftp', 'ftps']
+ unsafe_chars = frozenset('\t\r\n')
def __init__(self, schemes=None, **kwargs):
super().__init__(**kwargs)
@@ -101,6 +102,8 @@ class URLValidator(RegexValidator):
def __call__(self, value):
if not isinstance(value, str):
raise ValidationError(self.message, code=self.code, params={'value': value})
+ if self.unsafe_chars.intersection(value):
+ raise ValidationError(self.message, code=self.code, params={'value': value})
# Check if the scheme is valid.
scheme = value.split('://')[0].lower()
if scheme not in self.schemes: