From bf6b50f58e00d04d370caafea505df4b3c6031cc Mon Sep 17 00:00:00 2001 From: SilverFire - Dima Naumenko Date: Sat, 14 Nov 2015 00:39:12 +0200 Subject: [PATCH] #10142 Fixed EmailValidator to check email length properly --- framework/CHANGELOG.md | 1 + framework/assets/yii.validation.js | 25 ++++++++++---- framework/validators/EmailValidator.php | 33 ++++++++++++++----- .../validators/EmailValidatorTest.php | 6 ++++ 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 6536c63459..9e8a7c38bc 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -35,6 +35,7 @@ Yii Framework 2 Change Log - Bug #9999: Fixed `yii\web\UrlRule` to allow route parameter names with `-`, `_`, `.`characters (silverfire) - Bug #10029: Fixed MaskedInput not working with PJAX (martrix78, samdark) - Bug #10101: Fixed assignments saving on role removing in `\yii\rbac\PhpManager` (rezident1307) +- Bug #10142: Fixed `yii\validators\EmailValidator` to check the length of email properly (silverfire) - Bug: Fixed generation of canonical URLs for `ViewAction` pages (samdark) - Enh #3506: Added `\yii\validators\IpValidator` to perform validation of IP addresses and subnets (SilverFire, samdark) - Enh #5146: Added `\yii\i18n\Formatter::asDuration()` method (nineinchnick, SilverFire) diff --git a/framework/assets/yii.validation.js b/framework/assets/yii.validation.js index d5b495f089..69d5edf3a2 100644 --- a/framework/assets/yii.validation.js +++ b/framework/assets/yii.validation.js @@ -192,17 +192,30 @@ yii.validation = (function ($) { var valid = true; - if (options.enableIDN) { - var regexp = /^(.*?)$/, - matches = regexp.exec(value); - if (matches === null) { + + var regexp = /^((?:"?([^"]*)"?\s)?)(?:\s+)?(?:(]+))(>?))$/, + matches = regexp.exec(value); + + if (matches === null) { + valid = false + } else { + if (options.enableIDN) { + matches[5] = punycode.toASCII(matches[5]); + matches[6] = punycode.toASCII(matches[6]); + + value = matches[1] + matches[3] + matches[5] + '@' + matches[6] + matches[7]; + } + + if (matches[5].length > 64 || matches[1].length > 64) { + valid = false; + } else if ((matches[5] + '@' + matches[6]).length > 254) { valid = false; } else { - value = matches[1] + punycode.toASCII(matches[2]) + '@' + punycode.toASCII(matches[3]) + matches[4]; + valid = value.match(options.pattern) || (options.allowName && value.match(options.fullPattern)); } } - if (!valid || !(value.match(options.pattern) || (options.allowName && value.match(options.fullPattern)))) { + if (!valid) { pub.addMessage(messages, options.message, value); } }, diff --git a/framework/validators/EmailValidator.php b/framework/validators/EmailValidator.php index 8801716d48..69f8ad7c25 100644 --- a/framework/validators/EmailValidator.php +++ b/framework/validators/EmailValidator.php @@ -70,19 +70,34 @@ class EmailValidator extends Validator */ protected function validateValue($value) { - // make sure string length is limited to avoid DOS attacks - if (!is_string($value) || strlen($value) >= 320) { + if (!is_string($value)) { $valid = false; - } elseif (!preg_match('/^(.*?)$/', $value, $matches)) { + } elseif (!preg_match('/^(?P(?:"?([^"]*)"?\s)?)(?:\s+)?(?:(?P.+)@(?P[^>]+))(?P>?))$/i', $value, $matches)) { $valid = false; } else { - $domain = $matches[3]; if ($this->enableIDN) { - $value = $matches[1] . idn_to_ascii($matches[2]) . '@' . idn_to_ascii($domain) . $matches[4]; + $matches['local'] = idn_to_ascii($matches['local']); + $matches['domain'] = idn_to_ascii($matches['domain']); + $value = $matches['name'] . $matches['open'] . $matches['local'] . '@' . $matches['domain'] . $matches['close']; } - $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value); - if ($valid && $this->checkDNS) { - $valid = checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A'); + + if (strlen($matches['local']) > 64 || mb_strlen($matches['name']) > 64) { + // The maximum total length of a user name or other local-part is 64 octets. RFC 5322 section 4.5.3.1.1 + // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1 + $valid = false; + } elseif (strlen($matches['local'] . '@' . $matches['domain']) > 254) { + // There is a restriction in RFC 2821 on the length of an address in MAIL and RCPT commands + // of 254 characters. Since addresses that do not fit in those fields are not normally useful, the + // upper limit on address lengths should normally be considered to be 254. + // + // Dominic Sayers, RFC 3696 erratum 1690 + // http://www.rfc-editor.org/errata_search.php?eid=1690 + $valid = false; + } else { + $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value); + if ($valid && $this->checkDNS) { + $valid = checkdnsrr($matches['domain'], 'MX') || checkdnsrr($matches['domain'], 'A'); + } } } @@ -101,7 +116,7 @@ class EmailValidator extends Validator 'message' => Yii::$app->getI18n()->format($this->message, [ 'attribute' => $model->getAttributeLabel($attribute), ], Yii::$app->language), - 'enableIDN' => (bool) $this->enableIDN, + 'enableIDN' => (bool)$this->enableIDN, ]; if ($this->skipOnEmpty) { $options['skipOnEmpty'] = 1; diff --git a/tests/framework/validators/EmailValidatorTest.php b/tests/framework/validators/EmailValidatorTest.php index 4365a81350..af350ccd89 100644 --- a/tests/framework/validators/EmailValidatorTest.php +++ b/tests/framework/validators/EmailValidatorTest.php @@ -43,6 +43,9 @@ class EmailValidatorTest extends TestCase $this->assertTrue($validator->validate('test@example.com')); $this->assertTrue($validator->validate('John Smith ')); $this->assertFalse($validator->validate('John Smith ')); + $this->assertFalse($validator->validate('Short Name ')); + $this->assertFalse($validator->validate('Short Name ')); + $this->assertFalse($validator->validate('"This name is longer than 64 characters. Blah blah blah blah blah" ')); } public function testValidateValueIdn() @@ -82,6 +85,9 @@ class EmailValidatorTest extends TestCase $this->assertTrue($validator->validate('test@example.com')); $this->assertTrue($validator->validate('John Smith ')); $this->assertFalse($validator->validate('John Smith ')); + $this->assertFalse($validator->validate('Короткое имя <после-преобразования-в-idn-тут-будет-больше-чем-64-символа@пример.com>')); + $this->assertFalse($validator->validate('Короткое имя <тест@это-доменное-имя.после-преобразования-в-idn.будет-содержать-больше-254-символов.бла-бла-бла-бла-бла-бла-бла-бла.бла-бла-бла-бла-бла-бла.бла-бла-бла-бла-бла-бла.бла-бла-бла-бла-бла-бла.com>')); + $this->assertFalse($validator->validate('"Такое имя достаточно длинное, чтобы не допустить валидацию имейла" ')); } public function testValidateValueMx()