1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
From 1ba1cdce7d58e6740fe51955d945b56ae51d072a Mon Sep 17 00:00:00 2001
From: Tim Graham <timograham@gmail.com>
Date: Fri, 12 Jun 2015 13:49:31 -0400
Subject: [PATCH] [1.4.x] Prevented newlines from being accepted in some
validators.
This is a security fix; disclosure to follow shortly.
Thanks to Sjoerd Job Postmus for the report and draft patch.
---
django/core/validators.py | 26 +++++++++++++++-----------
docs/releases/1.4.21.txt | 26 ++++++++++++++++++++++++++
tests/modeltests/validators/tests.py | 16 +++++++++++++++-
3 files changed, 56 insertions(+), 12 deletions(-)
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -45,7 +45,7 @@ class URLValidator(RegexValidator):
r'localhost|' #localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
- r'(?:/?|[/?]\S+)$', re.IGNORECASE)
+ r'(?:/?|[/?]\S+)\Z', re.IGNORECASE)
def __init__(self, verify_exists=False, validator_user_agent=URL_VALIDATOR_USER_AGENT):
super(URLValidator, self).__init__()
@@ -89,11 +89,16 @@ class URLValidator(RegexValidator):
raise ValidationError(_(u'This URL appears to be a broken link.'), code='invalid_link')
+integer_validator = RegexValidator(
+ re.compile('^-?\d+\Z'),
+ message=_('Enter a valid integer.'),
+ code='invalid',
+)
+
+
def validate_integer(value):
- try:
- int(value)
- except (ValueError, TypeError), e:
- raise ValidationError('')
+ return integer_validator(value)
+
class EmailValidator(RegexValidator):
@@ -116,16 +121,16 @@ class EmailValidator(RegexValidator):
email_re = 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')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE) # domain
+ r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$\Z', re.IGNORECASE) # domain
validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
-slug_re = re.compile(r'^[-\w]+$')
+slug_re = re.compile(r'^[-\w]+\Z')
validate_slug = RegexValidator(slug_re, _(u"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, _(u'Enter a valid IPv4 address.'), 'invalid')
-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, _(u'Enter only digits separated by commas.'), 'invalid')
--- a/tests/modeltests/validators/tests.py
+++ b/tests/modeltests/validators/tests.py
@@ -10,13 +10,16 @@ NOW = datetime.now()
TEST_DATA = (
# (validator, value, expected),
+ # (validator, value, expected),
(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),
@@ -26,6 +29,10 @@ TEST_DATA = (
(validate_email, 'abc', ValidationError),
(validate_email, 'a @x.cz', ValidationError),
(validate_email, 'something@@somewhere.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_slug, 'slug-ok', None),
(validate_slug, 'longer-slug-still-ok', None),
@@ -38,6 +45,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),
@@ -47,6 +55,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_comma_separated_integer_list, '1', None),
(validate_comma_separated_integer_list, '1,2,3', None),
@@ -55,6 +64,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),
@@ -106,6 +116,9 @@ TEST_DATA = (
(URLValidator(), 'http://-invalid.com', ValidationError),
(URLValidator(), 'http://inv-.alid-.com', ValidationError),
(URLValidator(), 'http://inv-.-alid.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),
|