8000 [Validator] Strict validation of UUIDs can be disabled for legacy data · symfony/symfony@7c0c462 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7c0c462

Browse files
committed
[Validator] Strict validation of UUIDs can be disabled for legacy data
1 parent 6ffa383 commit 7c0c462

File tree

3 files changed

+96
-18
lines changed

3 files changed

+96
-18
lines changed

src/Symfony/Component/Validator/Constraints/Uuid.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class Uuid extends Constraint
3030
// Message to display when validation fails
3131
public $message = 'This is not a valid UUID.';
3232

33+
// Strict mode only allows UUIDs that meet the formal definition and formatting per RFC 4122
34+
// Set this to `false` to allow legacy formats with different dash positioning or wrapping characters
35+
public $strict = true;
36+
3337
// Array of allowed versions (see version constants above)
3438
// All UUID versions are allowed by default
3539
public $versions = array(

src/Symfony/Component/Validator/Constraints/UuidValidator.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@
2626
class UuidValidator extends ConstraintValidator
2727
{
2828
// Regular expression which verifies allowed characters and the proper format
29-
const PATTERN = '/^[a-f0-9]{8}-[a-f0-9]{4}-[%s][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i';
29+
// The strict pattern matches UUIDs like this: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
30+
// Roughly speaking: x = any hexadecimal character, M = any allowed version, N = any allowed variant
31+
const STRICT_PATTERN = '/^[a-f0-9]{8}-[a-f0-9]{4}-[%s][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i';
32+
// The loose pattern is similar, except it doesn't care about the dashes, and the whole thing can be wrapped
33+
// with characters like []{} for backwards-compatibility with non-compliant UUIDs.
34+
const LOOSE_PATTERN = '/^[a-f0-9]{4}(?:-?[a-f0-9]{4}){7}$/i';
35+
3036
// Properly-formatted UUIDs contain 32 hex digits, separated by 4 dashes
31-
const UUID_LENGTH = 36;
37+
// We can use this to avoid performing a preg_match on strings of other sizes
38+
const STRICT_UUID_LENGTH = 36;
3239

3340
/**
3441
* {@inheritDoc}
@@ -45,10 +52,20 @@ public function validate($value, Constraint $constraint)
4552

4653
$value = (string) $value;
4754

48-
$pattern = sprintf(static::PATTERN, implode('', $constraint->versions));
55+
if ($constraint->strict) {
56+
// Insert the allowed versions into the regular expression
57+
$pattern = sprintf(static::STRICT_PATTERN, implode('', $constraint->versions));
58+
59+
if (strlen($value) !== static::STRICT_UUID_LENGTH || !preg_match($pattern, $value)) {
60+
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
61+
}
62+
} else {
63+
// Trim any wrapping characters like [] or {} used by some legacy systems
64+
$value = trim($value, '[]{}');
4965

50-
if (strlen($value) !== static::UUID_LENGTH || !preg_match($pattern, $value)) {
51-
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
66+
if (!preg_match(static::LOOSE_PATTERN, $value)) {
67+
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
68+
}
5269
}
5370
}
5471
}

src/Symfony/Component/Validator/Tests/Constraints/UuidValidatorTest.php

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ public function testExpectsStringCompatibleType()
6060
}
6161

6262
/**
63-
* @dataProvider getValidUuids
63+
* @dataProvider getValidStrictUuids
6464
*/
65-
public function testValidUuids($uuid)
65+
public function testValidStrictUuids($uuid)
6666
{
6767
$this->context->expects($this->never())
6868
->method('addViolation');
6969

7070
$this->validator->validate($uuid, new Uuid());
7171
}
7272

73-
public function getValidUuids()
73+
public function getValidStrictUuids()
7474
{
7575
return array(
7676
array('216fff40-98d9-11e3-a5e2-0800200c9a66'), // Version 1 UUID in lowercase
@@ -81,9 +81,9 @@ public function getValidUuids()
8181
}
8282

8383
/**
84-
* @dataProvider getInvalidUuids
84+
* @dataProvider getInvalidStrictUuids
8585
*/
86-
public function testInvalidUuids($uuid)
86+
public function testInvalidStrictUuids($uuid)
8787
{
8888
$constraint = new Uuid(array(
8989
'message' => 'testMessage'
@@ -98,19 +98,22 @@ public function testInvalidUuids($uuid)
9898
$this->validator->validate($uuid, $constraint);
9999
}
100100

101-
public function getInvalidUuids()
101+
public function getInvalidStrictUuids()
102102
{
103103
return array(
104-
array('216fff40-98d9-11e3-a5e2-0800200c9a6'), // Too few characters
105-
array('216fff40-98d9-11e3-a5e2-0800200c9a666'), // Too many characters
106-
array('V16fff40-98d9-11e3-a5e2-0800200c9a66'), // Invalid character 'V'
107-
array('216fff40-98d9-11e3-a5e20800-200c9a66'), // Invalid dash position
108-
array('216fff4098d911e3a5e20800200c9a66'), // Missing dashes
104+
array('216fff40-98d9-11e3-a5e2-0800200c9a6'), // Too few characters
105+
array('216fff40-98d9-11e3-a5e2-0800200c9a666'), // Too many characters
106+
array('V16fff40-98d9-11e3-a5e2-0800200c9a66'), // Invalid character 'V'
107+
108+
// Non-standard UUIDs allowed by some other systems
109+
array('216fff40-98d911e3-a5e20800-200c9a66'), // Non-standard dash positions
110+
array('216fff4098d911e3a5e20800200c9a66'), // No dashes at all
111+
array('{216fff40-98d9-11e3-a5e2-0800200c9a66}'), // Wrapped with curly braces
109112
);
110113
}
111114

112115
/**
113-
* @dataProvider getValidUuids
116+
* @dataProvider getValidStrictUuids
114117
*/
115118
public function testVersionConstraintIsValid($uuid)
116119
{
@@ -125,7 +128,7 @@ public function testVersionConstraintIsValid($uuid)
125128
}
126129

127130
/**
128-
* @dataProvider getValidUuids
131+
* @dataProvider getValidStrictUuids
129132
*/
130133
public function testVersionConstraintIsInvalid($uuid)
131134
{
@@ -138,4 +141,58 @@ public function testVersionConstraintIsInvalid($uuid)
138141

139142
$this->validator->validate($uuid, $constraint);
140143
}
144+
145+
/**
146+
* @dataProvider getValidNonStrictUuids
147+
*/
148+
public function testValidNonStrictUuids($uuid)
149+
{
150+
$constraint = new Uuid(array(
151+
'strict' => false
152+
));
153+
154+
$this->context->expects($this->never())
155+
->method('addViolation');
156+
157+
$this->validator->validate($uuid, $constraint);
158+
}
159+
160+
public function getValidNonStrictUuids()
161+
{
162+
return array(
163+
array('216fff40-98d9-11e3-a5e2-0800200c9a66'), // Version 1 UUID in lowercase
164+
array('216FFF40-98D9-11E3-A5E2-0800200C9A66'), // Version 1 UUID in UPPERCASE
165+
array('456daefb-5aa6-41b5-8dbc-068b05a8b201'), // Version 4 UUID in lowercase
166+
array('456DAEFb-5AA6-41B5-8DBC-068B05A8B201'), // Version 4 UUID in UPPERCASE
167+
168+
// Non-standard UUIDs allowed by some other systems
169+
array('216fff40-98d911e3-a5e20800-200c9a66'), // Non-standard dash positions
170+
array('216fff4098d911e3a5e20800200c9a66'), // No dashes at all
171+
array('{216fff40-98d9-11e3-a5e2-0800200c9a66}'), // Wrapped with curly braces
172+
);
173+
}
174+
175+
/**
176+
* @dataProvider getInvalidNonStrictUuids
177+
*/
178+
public function testInvalidNonStrictUuids($uuid)
179+
{
180+
$constraint = new Uuid(array(
181+
'strict' => false
182+
));
183+
184+
$this->context->expects($this->once())
185+
->method('addViolation');
186+
187+
$this->validator->validate($uuid, $constraint);
188+
}
189+
190+
public function getInvalidNonStrictUuids()
191+
{
192+
return array(
193+
array('216fff40-98d9-11e3-a5e2-0800200c9a6'), // Too few characters
194+
array('216fff40-98d9-11e3-a5e2-0800200c9a666'), // Too many characters
195+
array('V16fff40-98d9-11e3-a5e2-0800200c9a66'), // Invalid character 'V'
196+
);
197+
}
141198
}

0 commit comments

Comments
 (0)
0