Loading django/core/validators.py +15 −12 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ class URLValidator(RegexValidator): r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) r'(?:/?|[/?]\S+)\Z', re.IGNORECASE) message = _('Enter a valid URL.') schemes = ['http', 'https', 'ftp', 'ftps'] Loading Loading @@ -107,12 +107,15 @@ class URLValidator(RegexValidator): else: url = value integer_validator = RegexValidator( re.compile('^-?\d+\Z'), message=_('Enter a valid integer.'), code='invalid', ) def validate_integer(value): try: int(value) except (ValueError, TypeError): raise ValidationError(_('Enter a valid integer.'), code='invalid') return integer_validator(value) @deconstructible Loading @@ -120,15 +123,15 @@ class EmailValidator(object): message = _('Enter a valid email address.') code = 'invalid' user_regex = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)', # quoted-string r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string re.IGNORECASE) domain_regex = re.compile( r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$', r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z', re.IGNORECASE) literal_regex = re.compile( # literal form, ipv4 or ipv6 address (SMTP 4.1.3) r'\[([A-f0-9:\.]+)\]$', r'\[([A-f0-9:\.]+)\]\Z', re.IGNORECASE) domain_whitelist = ['localhost'] Loading Loading @@ -181,10 +184,10 @@ class EmailValidator(object): validate_email = EmailValidator() slug_re = re.compile(r'^[-a-zA-Z0-9_]+$') slug_re = re.compile(r'^[-a-zA-Z0-9_]+\Z') validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z') validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 address.'), 'invalid') Loading Loading @@ -225,7 +228,7 @@ def ip_address_validators(protocol, unpack_ipv4): raise ValueError("The protocol '%s' is unknown. Supported: %s" % (protocol, list(ip_address_validator_map))) comma_separated_int_list_re = re.compile('^[\d,]+$') comma_separated_int_list_re = re.compile('^[\d,]+\Z') validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid') Loading docs/releases/1.4.21.txt +26 −0 Original line number Diff line number Diff line Loading @@ -26,3 +26,29 @@ As each built-in session backend was fixed separately (rather than a fix in the core sessions framework), maintainers of third-party session backends should check whether the same vulnerability is present in their backend and correct it if so. Header injection possibility since validators accept newlines in input ====================================================================== Some of Django's built-in validators (``django.core.validators.EmailValidator``, most seriously) didn't prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the regular expressions). If you use values with newlines in HTTP response or email headers, you can suffer from header injection attacks. Django itself isn't vulnerable because :class:`~django.http.HttpResponse` and the mail sending utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP headers, respectively. While the validators have been fixed in Django, if you're creating HTTP responses or email messages in other ways, it's a good idea to ensure that those methods prohibit newlines as well. You might also want to validate that any existing data in your application doesn't contain unexpected newlines. :func:`~django.core.validators.validate_ipv4_address`, :func:`~django.core.validators.validate_slug`, and :class:`~django.core.validators.URLValidator` and their usage in the corresponding form fields ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``, and ``URLField`` are also affected. The undocumented, internally unused ``validate_integer()`` function is now stricter as it validates using a regular expression instead of simply casting the value using ``int()`` and checking if an exception was raised. docs/releases/1.7.9.txt +28 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,34 @@ core sessions framework), maintainers of third-party session backends should check whether the same vulnerability is present in their backend and correct it if so. Header injection possibility since validators accept newlines in input ====================================================================== Some of Django's built-in validators (``django.core.validators.EmailValidator``, most seriously) didn't prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the regular expressions). If you use values with newlines in HTTP response or email headers, you can suffer from header injection attacks. Django itself isn't vulnerable because :class:`~django.http.HttpResponse` and the mail sending utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP headers, respectively. While the validators have been fixed in Django, if you're creating HTTP responses or email messages in other ways, it's a good idea to ensure that those methods prohibit newlines as well. You might also want to validate that any existing data in your application doesn't contain unexpected newlines. :func:`~django.core.validators.validate_ipv4_address`, :func:`~django.core.validators.validate_slug`, and :class:`~django.core.validators.URLValidator` are also affected, however, as of Django 1.6 the ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``, and ``URLField`` form fields which use these validators all strip the input, so the possibility of newlines entering your data only exists if you are using these validators outside of the form fields. The undocumented, internally unused ``validate_integer()`` function is now stricter as it validates using a regular expression instead of simply casting the value using ``int()`` and checking if an exception was raised. Bugfixes ======== Loading tests/validators/tests.py +14 −1 Original line number Diff line number Diff line Loading @@ -25,10 +25,12 @@ TEST_DATA = ( (validate_integer, '42', None), (validate_integer, '-42', None), (validate_integer, -42, None), (validate_integer, -42.5, None), (validate_integer, -42.5, ValidationError), (validate_integer, None, ValidationError), (validate_integer, 'a', ValidationError), (validate_integer, '\n42', ValidationError), (validate_integer, '42\n', ValidationError), (validate_email, 'email@here.com', None), (validate_email, 'weirder-email@here.and.there.com', None), Loading Loading @@ -66,6 +68,11 @@ TEST_DATA = ( (validate_email, '"\\\011"@here.com', None), (validate_email, '"\\\012"@here.com', ValidationError), (validate_email, 'trailingdot@shouldfail.com.', ValidationError), # Trailing newlines in username or domain not allowed (validate_email, 'a@b.com\n', ValidationError), (validate_email, 'a\n@b.com', ValidationError), (validate_email, '"test@test"\n@example.com', ValidationError), (validate_email, 'a@[127.0.0.1]\n', ValidationError), (validate_slug, 'slug-ok', None), (validate_slug, 'longer-slug-still-ok', None), Loading @@ -78,6 +85,7 @@ TEST_DATA = ( (validate_slug, 'some@mail.com', ValidationError), (validate_slug, '你好', ValidationError), (validate_slug, '\n', ValidationError), (validate_slug, 'trailing-newline\n', ValidationError), (validate_ipv4_address, '1.1.1.1', None), (validate_ipv4_address, '255.0.0.0', None), Loading @@ -87,6 +95,7 @@ TEST_DATA = ( (validate_ipv4_address, '25.1.1.', ValidationError), (validate_ipv4_address, '25,1,1,1', ValidationError), (validate_ipv4_address, '25.1 .1.1', ValidationError), (validate_ipv4_address, '1.1.1.1\n', ValidationError), # validate_ipv6_address uses django.utils.ipv6, which # is tested in much greater detail in its own testcase Loading Loading @@ -120,6 +129,7 @@ TEST_DATA = ( (validate_comma_separated_integer_list, '', ValidationError), (validate_comma_separated_integer_list, 'a,b,c', ValidationError), (validate_comma_separated_integer_list, '1, 2, 3', ValidationError), (validate_comma_separated_integer_list, '1,2,3\n', ValidationError), (MaxValueValidator(10), 10, None), (MaxValueValidator(10), -10, None), Loading Loading @@ -181,6 +191,9 @@ TEST_DATA = ( (URLValidator(), 'file://localhost/path', ValidationError), (URLValidator(), 'git://example.com/', ValidationError), (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError), # Trailing newlines not accepted (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError), (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError), (BaseValidator(True), True, None), (BaseValidator(True), False, ValidationError), Loading Loading
django/core/validators.py +15 −12 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ class URLValidator(RegexValidator): r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) r'(?:/?|[/?]\S+)\Z', re.IGNORECASE) message = _('Enter a valid URL.') schemes = ['http', 'https', 'ftp', 'ftps'] Loading Loading @@ -107,12 +107,15 @@ class URLValidator(RegexValidator): else: url = value integer_validator = RegexValidator( re.compile('^-?\d+\Z'), message=_('Enter a valid integer.'), code='invalid', ) def validate_integer(value): try: int(value) except (ValueError, TypeError): raise ValidationError(_('Enter a valid integer.'), code='invalid') return integer_validator(value) @deconstructible Loading @@ -120,15 +123,15 @@ class EmailValidator(object): message = _('Enter a valid email address.') code = 'invalid' user_regex = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)', # quoted-string r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string re.IGNORECASE) domain_regex = re.compile( r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$', r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z', re.IGNORECASE) literal_regex = re.compile( # literal form, ipv4 or ipv6 address (SMTP 4.1.3) r'\[([A-f0-9:\.]+)\]$', r'\[([A-f0-9:\.]+)\]\Z', re.IGNORECASE) domain_whitelist = ['localhost'] Loading Loading @@ -181,10 +184,10 @@ class EmailValidator(object): validate_email = EmailValidator() slug_re = re.compile(r'^[-a-zA-Z0-9_]+$') slug_re = re.compile(r'^[-a-zA-Z0-9_]+\Z') validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z') validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 address.'), 'invalid') Loading Loading @@ -225,7 +228,7 @@ def ip_address_validators(protocol, unpack_ipv4): raise ValueError("The protocol '%s' is unknown. Supported: %s" % (protocol, list(ip_address_validator_map))) comma_separated_int_list_re = re.compile('^[\d,]+$') comma_separated_int_list_re = re.compile('^[\d,]+\Z') validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid') Loading
docs/releases/1.4.21.txt +26 −0 Original line number Diff line number Diff line Loading @@ -26,3 +26,29 @@ As each built-in session backend was fixed separately (rather than a fix in the core sessions framework), maintainers of third-party session backends should check whether the same vulnerability is present in their backend and correct it if so. Header injection possibility since validators accept newlines in input ====================================================================== Some of Django's built-in validators (``django.core.validators.EmailValidator``, most seriously) didn't prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the regular expressions). If you use values with newlines in HTTP response or email headers, you can suffer from header injection attacks. Django itself isn't vulnerable because :class:`~django.http.HttpResponse` and the mail sending utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP headers, respectively. While the validators have been fixed in Django, if you're creating HTTP responses or email messages in other ways, it's a good idea to ensure that those methods prohibit newlines as well. You might also want to validate that any existing data in your application doesn't contain unexpected newlines. :func:`~django.core.validators.validate_ipv4_address`, :func:`~django.core.validators.validate_slug`, and :class:`~django.core.validators.URLValidator` and their usage in the corresponding form fields ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``, and ``URLField`` are also affected. The undocumented, internally unused ``validate_integer()`` function is now stricter as it validates using a regular expression instead of simply casting the value using ``int()`` and checking if an exception was raised.
docs/releases/1.7.9.txt +28 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,34 @@ core sessions framework), maintainers of third-party session backends should check whether the same vulnerability is present in their backend and correct it if so. Header injection possibility since validators accept newlines in input ====================================================================== Some of Django's built-in validators (``django.core.validators.EmailValidator``, most seriously) didn't prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the regular expressions). If you use values with newlines in HTTP response or email headers, you can suffer from header injection attacks. Django itself isn't vulnerable because :class:`~django.http.HttpResponse` and the mail sending utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP headers, respectively. While the validators have been fixed in Django, if you're creating HTTP responses or email messages in other ways, it's a good idea to ensure that those methods prohibit newlines as well. You might also want to validate that any existing data in your application doesn't contain unexpected newlines. :func:`~django.core.validators.validate_ipv4_address`, :func:`~django.core.validators.validate_slug`, and :class:`~django.core.validators.URLValidator` are also affected, however, as of Django 1.6 the ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``, and ``URLField`` form fields which use these validators all strip the input, so the possibility of newlines entering your data only exists if you are using these validators outside of the form fields. The undocumented, internally unused ``validate_integer()`` function is now stricter as it validates using a regular expression instead of simply casting the value using ``int()`` and checking if an exception was raised. Bugfixes ======== Loading
tests/validators/tests.py +14 −1 Original line number Diff line number Diff line Loading @@ -25,10 +25,12 @@ TEST_DATA = ( (validate_integer, '42', None), (validate_integer, '-42', None), (validate_integer, -42, None), (validate_integer, -42.5, None), (validate_integer, -42.5, ValidationError), (validate_integer, None, ValidationError), (validate_integer, 'a', ValidationError), (validate_integer, '\n42', ValidationError), (validate_integer, '42\n', ValidationError), (validate_email, 'email@here.com', None), (validate_email, 'weirder-email@here.and.there.com', None), Loading Loading @@ -66,6 +68,11 @@ TEST_DATA = ( (validate_email, '"\\\011"@here.com', None), (validate_email, '"\\\012"@here.com', ValidationError), (validate_email, 'trailingdot@shouldfail.com.', ValidationError), # Trailing newlines in username or domain not allowed (validate_email, 'a@b.com\n', ValidationError), (validate_email, 'a\n@b.com', ValidationError), (validate_email, '"test@test"\n@example.com', ValidationError), (validate_email, 'a@[127.0.0.1]\n', ValidationError), (validate_slug, 'slug-ok', None), (validate_slug, 'longer-slug-still-ok', None), Loading @@ -78,6 +85,7 @@ TEST_DATA = ( (validate_slug, 'some@mail.com', ValidationError), (validate_slug, '你好', ValidationError), (validate_slug, '\n', ValidationError), (validate_slug, 'trailing-newline\n', ValidationError), (validate_ipv4_address, '1.1.1.1', None), (validate_ipv4_address, '255.0.0.0', None), Loading @@ -87,6 +95,7 @@ TEST_DATA = ( (validate_ipv4_address, '25.1.1.', ValidationError), (validate_ipv4_address, '25,1,1,1', ValidationError), (validate_ipv4_address, '25.1 .1.1', ValidationError), (validate_ipv4_address, '1.1.1.1\n', ValidationError), # validate_ipv6_address uses django.utils.ipv6, which # is tested in much greater detail in its own testcase Loading Loading @@ -120,6 +129,7 @@ TEST_DATA = ( (validate_comma_separated_integer_list, '', ValidationError), (validate_comma_separated_integer_list, 'a,b,c', ValidationError), (validate_comma_separated_integer_list, '1, 2, 3', ValidationError), (validate_comma_separated_integer_list, '1,2,3\n', ValidationError), (MaxValueValidator(10), 10, None), (MaxValueValidator(10), -10, None), Loading Loading @@ -181,6 +191,9 @@ TEST_DATA = ( (URLValidator(), 'file://localhost/path', ValidationError), (URLValidator(), 'git://example.com/', ValidationError), (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError), # Trailing newlines not accepted (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError), (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError), (BaseValidator(True), True, None), (BaseValidator(True), False, ValidationError), Loading