From 3a9058a7d767b969c587adb488592cad4781ebf5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 26 Jan 2015 21:06:31 +0100 Subject: [PATCH] [Validator] reject ill-formed strings --- .../Validator/Constraints/Length.php | 1 + .../Validator/Constraints/LengthValidator.php | 21 +++- .../Resources/translations/validators.en.xlf | 4 + .../Resources/translations/validators.fr.xlf | 4 + .../Tests/Constraints/LengthValidatorTest.php | 95 +++++++++++-------- 5 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/Length.php b/src/Symfony/Component/Validator/Constraints/Length.php index b353e9b24d..49f4890c81 100644 --- a/src/Symfony/Component/Validator/Constraints/Length.php +++ b/src/Symfony/Component/Validator/Constraints/Length.php @@ -27,6 +27,7 @@ class Length extends Constraint public $maxMessage = 'This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.'; public $minMessage = 'This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.'; public $exactMessage = 'This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.'; + public $charsetMessage = 'This value does not match the expected {{ charset }} charset.'; public $max; public $min; public $charset = 'UTF-8'; diff --git a/src/Symfony/Component/Validator/Constraints/LengthValidator.php b/src/Symfony/Component/Validator/Constraints/LengthValidator.php index 69e0ea76a9..a184883b92 100644 --- a/src/Symfony/Component/Validator/Constraints/LengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/LengthValidator.php @@ -34,23 +34,40 @@ class LengthValidator extends ConstraintValidator } $stringValue = (string) $value; + $invalidCharset = false; if ('UTF8' === $charset = strtoupper($constraint->charset)) { $charset = 'UTF-8'; } if (function_exists('iconv_strlen')) { - $length = iconv_strlen($stringValue, $constraint->charset); + $length = @iconv_strlen($stringValue, $constraint->charset); + $invalidCharset = false === $length; } elseif (function_exists('mb_strlen')) { - $length = mb_strlen($stringValue, $constraint->charset); + if (mb_check_encoding($stringValue, $constraint->charset)) { + $length = mb_strlen($stringValue, $constraint->charset); + } else { + $invalidCharset = true; + } } elseif ('UTF-8' !== $charset) { $length = strlen($stringValue); + } elseif (!preg_match('//u', $stringValue)) { + $invalidCharset = true; } elseif (function_exists('utf8_decode')) { $length = strlen(utf8_decode($stringValue)); } else { preg_replace('/./u', '', $stringValue, -1, $length); } + if ($invalidCharset) { + $this->context->addViolation($constraint->charsetMessage, array( + '{{ value }}' => $this->formatValue($stringValue), + '{{ charset }}' => $constraint->charset, + ), $value); + + return; + } + if ($constraint->min == $constraint->max && $length != $constraint->min) { $this->context->addViolation($constraint->exactMessage, array( '{{ value }}' => $this->formatValue($stringValue), diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index b4c8b60bd4..160e21dc9e 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -278,6 +278,10 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. This value should not be identical to {{ compared_value_type }} {{ compared_value }}. + + This value does not match the expected {{ charset }} charset. + This value does not match the expected {{ charset }} charset. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf index 0ad0592079..4108a8a48b 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf @@ -278,6 +278,10 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Cette valeur ne doit pas être identique à {{ compared_value_type }} {{ compared_value }}. + + This value does not match the expected {{ charset }} charset. + Cette valeur ne correspond pas au jeu de caractères {{ charset }} attendu. + diff --git a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php index 1d492a53e1..6147e424fe 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/LengthValidatorTest.php @@ -48,12 +48,12 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest return array( array(12), array('12'), - array('üü', true), - array('éé', true), + array('üü'), + array('éé'), array(123), array('123'), - array('üüü', true), - array('ééé', true), + array('üüü'), + array('ééé'), ); } @@ -62,8 +62,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest return array( array(1234), array('1234'), - array('üüüü', true), - array('éééé', true), + array('üüüü'), + array('éééé'), ); } @@ -80,24 +80,34 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest return array( array(12345), array('12345'), - array('üüüüü', true), - array('ééééé', true), + array('üüüüü'), + array('ééééé'), array(123456), array('123456'), - array('üüüüüü', true), - array('éééééé', true), + array('üüüüüü'), + array('éééééé'), + ); + } + + public function getOneCharset() + { + if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) { + $this->markTestSkipped('Mbstring or iconv is required for this test.'); + } + + return array( + array("é", "utf8", true), + array("\xE9", "CP1252", true), + array("\xE9", "XXX", false), + array("\xE9", "utf8", false), ); } /** * @dataProvider getFiveOrMoreCharacters */ - public function testValidValuesMin($value, $mbOnly = false) + public function testValidValuesMin($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(array('min' => 5)); $this->validator->validate($value, $constraint); @@ -107,12 +117,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest /** * @dataProvider getThreeOrLessCharacters */ - public function testValidValuesMax($value, $mbOnly = false) + public function testValidValuesMax($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(array('max' => 3)); $this->validator->validate($value, $constraint); @@ -122,12 +128,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest /** * @dataProvider getFourCharacters */ - public function testValidValuesExact($value, $mbOnly = false) + public function testValidValuesExact($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(4); $this->validator->validate($value, $constraint); @@ -137,12 +139,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest /** * @dataProvider getThreeOrLessCharacters */ - public function testInvalidValuesMin($value, $mbOnly = false) + public function testInvalidValuesMin($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(array( 'min' => 4, 'minMessage' => 'myMessage', @@ -161,12 +159,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest /** * @dataProvider getFiveOrMoreCharacters */ - public function testInvalidValuesMax($value, $mbOnly = false) + public function testInvalidValuesMax($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(array( 'max' => 4, 'maxMessage' => 'myMessage', @@ -185,12 +179,8 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest /** * @dataProvider getNotFourCharacters */ - public function testInvalidValuesExact($value, $mbOnly = false) + public function testInvalidValuesExact($value) { - if ($mbOnly && !function_exists('mb_strlen')) { - $this->markTestSkipped('mb_strlen does not exist'); - } - $constraint = new Length(array( 'min' => 4, 'max' => 4, @@ -207,6 +197,31 @@ class LengthValidatorTest extends AbstractConstraintValidatorTest ->assertRaised(); } + /** + * @dataProvider getOneCharset + */ + public function testOneCharset($value, $charset, $isValid) + { + $constraint = new Length(array( + 'min' => 1, + 'max' => 1, + 'charset' => $charset, + 'charsetMessage' => 'myMessage', + )); + + $this->validator->validate($value, $constraint); + + if ($isValid) { + $this->assertNoViolation(); + } else { + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$value.'"') + ->setParameter('{{ charset }}', $charset) + ->setInvalidValue($value) + ->assertRaised(); + } + } + public function testConstraintGetDefaultOption() { $constraint = new Length(5);