Skip to content

Commit 6c9dead

Browse files
authored
Merge pull request #3082 from ben-walters/feature/fail-bracketed-uuid
Added wrapper option to uuid function
2 parents 703c12c + d8d8434 commit 6c9dead

File tree

3 files changed

+124
-15
lines changed

3 files changed

+124
-15
lines changed

API.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2801,24 +2801,32 @@ Possible validation errors: [`string.email`](#stringemail)
28012801

28022802
Requires the string value to be a valid GUID.
28032803

2804-
- `options` - optional settings:
2805-
- `version` - specifies one or more acceptable versions. Can be an Array or String with the following values:
2806-
`uuidv1`, `uuidv2`, `uuidv3`, `uuidv4`, `uuidv5`, `uuidv6`, `uuidv7` or `uuidv8`. If no `version` is specified then it is assumed to be a generic `guid`
2807-
which will not validate the version or variant of the guid and just check for general structure format.
2808-
- `separator` - defines the allowed or required GUID separator where:
2809-
- `true` - a separator is required, can be either `:` or `-`.
2810-
- `false` - separator is not allowed.
2811-
- `'-'` - a dash separator is required.
2812-
- `':'` - a colon separator is required.
2813-
- defaults to optional `:` or `-` separator.
2804+
- `options` - optional settings:
2805+
- `version` - specifies one or more acceptable versions. Can be an Array or String with the following values:
2806+
`uuidv1`, `uuidv2`, `uuidv3`, `uuidv4`, `uuidv5`, `uuidv6`, `uuidv7` or `uuidv8`. If no `version` is specified then it is assumed to be a generic `guid`
2807+
which will not validate the version or variant of the guid and just check for general structure format.
2808+
- `separator` - defines the allowed or required GUID separator where:
2809+
- `true` - a separator is required, can be either `:` or `-`.
2810+
- `false` - separator is not allowed.
2811+
- `'-'` - a dash separator is required.
2812+
- `':'` - a colon separator is required.
2813+
- defaults to optional `:` or `-` separator.
2814+
- `wrapper` - defines the allowed or required GUID wrapper characters where:
2815+
- `undefined` - (default) the GUID can be optionally wrapped with `{}`, `[]`, or `()`. The opening and closing characters must be a matching pair.
2816+
- `true` - the GUID must be wrapped with `{}`, `[]`, or `()`. The opening and closing characters must be a matching pair.
2817+
- `false` - wrapper characters are not allowed.
2818+
- `'['`, `'{'`, or `'('` - a specific wrapper is required (e.g., if `wrapper` is `'['`, the GUID must be enclosed in square brackets).
2819+
28142820

28152821
```js
28162822
const schema = Joi.string().guid({
28172823
version: [
28182824
'uuidv4',
28192825
'uuidv5'
2820-
]
2826+
],
2827+
wrapper: false
28212828
});
2829+
28222830
```
28232831

28242832
Possible validation errors: [`string.guid`](#stringguid)

lib/types/string.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,14 @@ module.exports = Any.extend({
316316
alias: 'uuid',
317317
method(options = {}) {
318318

319-
Common.assertOptions(options, ['version', 'separator']);
319+
Common.assertOptions(options, ['version', 'separator', 'wrapper']);
320+
321+
assert(
322+
options.wrapper === undefined ||
323+
typeof options.wrapper === 'boolean' ||
324+
(typeof options.wrapper === 'string' && typeof internals.guidBrackets[options.wrapper] === 'string'),
325+
`"wrapper" must be true, false, or one of "${Object.keys(internals.guidBrackets).filter(Boolean).join('", "')}"`
326+
);
320327

321328
let versionNumbers = '';
322329

@@ -343,10 +350,38 @@ module.exports = Any.extend({
343350
options.separator === true ? '[:-]' :
344351
options.separator === false ? '[]?' : `\\${options.separator}`;
345352

346-
const regex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}(${separator})[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
353+
let wrapperStart;
354+
let wrapperEnd;
355+
356+
if (options.wrapper === undefined) {
357+
wrapperStart = '[\\[{\\(]?';
358+
wrapperEnd = '[\\]}\\)]?';
359+
}
360+
else if (options.wrapper === true) {
361+
wrapperStart = '[\\[{\\(]';
362+
wrapperEnd = '[\\]}\\)]';
363+
}
364+
else if (options.wrapper === false) {
365+
wrapperStart = '';
366+
wrapperEnd = '';
367+
}
368+
else {
369+
wrapperStart = escapeRegex(options.wrapper);
370+
wrapperEnd = escapeRegex(internals.guidBrackets[options.wrapper]);
371+
}
372+
373+
const regex = new RegExp(
374+
`^(${wrapperStart})[0-9A-F]{8}(${separator})[0-9A-F]{4}\\2?[${
375+
versionNumbers || '0-9A-F'
376+
}][0-9A-F]{3}\\2?[${
377+
versionNumbers ? '89AB' : '0-9A-F'
378+
}][0-9A-F]{3}\\2?[0-9A-F]{12}(${wrapperEnd})$`,
379+
'i'
380+
);
347381

348382
return this.$_addRule({ name: 'guid', args: { options }, regex });
349383
},
384+
350385
validate(value, helpers, args, { regex }) {
351386

352387
const results = regex.exec(value);
@@ -355,9 +390,10 @@ module.exports = Any.extend({
355390
return helpers.error('string.guid');
356391
}
357392

358-
// Matching braces
393+
const open = results[1];
394+
const close = results[results.length - 1];
359395

360-
if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
396+
if ((open || close) && internals.guidBrackets[open] !== close) {
361397
return helpers.error('string.guid');
362398
}
363399

test/types/string.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,6 +3196,71 @@ describe('string', () => {
31963196
]);
31973197
});
31983198

3199+
it('validates wrapper', () => {
3200+
3201+
// Wthout wrapper all are valid GUIDs
3202+
Helper.validate(Joi.string().guid(), [
3203+
['69593D62-71EA-4548-85E4-04FC71357423', true],
3204+
['{69593D62-71EA-4548-85E4-04FC71357423}', true],
3205+
['[69593D62-71EA-4548-85E4-04FC71357423]', true],
3206+
['(69593D62-71EA-4548-85E4-04FC71357423)', true]
3207+
]);
3208+
3209+
// Wrapper false means no wrapper is allowed
3210+
Helper.validate(Joi.string().guid({ wrapper: false }), [
3211+
['69593D62-71EA-4548-85E4-04FC71357423', true],
3212+
['{69593D62-71EA-4548-85E4-04FC71357423}', false, '"value" must be a valid GUID'],
3213+
['[69593D62-71EA-4548-85E4-04FC71357423]', false, '"value" must be a valid GUID'],
3214+
['(69593D62-71EA-4548-85E4-04FC71357423)', false, '"value" must be a valid GUID']
3215+
]);
3216+
3217+
// Wrapper true means a wrapper is enforced
3218+
Helper.validate(Joi.string().guid({ wrapper: true }), [
3219+
['69593D62-71EA-4548-85E4-04FC71357423', false, '"value" must be a valid GUID'],
3220+
['{69593D62-71EA-4548-85E4-04FC71357423}', true],
3221+
['[69593D62-71EA-4548-85E4-04FC71357423]', true],
3222+
['(69593D62-71EA-4548-85E4-04FC71357423)', true]
3223+
]);
3224+
3225+
// Wrapper can be set to a specific value
3226+
Helper.validate(Joi.string().guid({ wrapper: '{' }), [
3227+
['69593D62-71EA-4548-85E4-04FC71357423', false, '"value" must be a valid GUID'],
3228+
['{69593D62-71EA-4548-85E4-04FC71357423}', true],
3229+
['[69593D62-71EA-4548-85E4-04FC71357423]', false, '"value" must be a valid GUID'],
3230+
['(69593D62-71EA-4548-85E4-04FC71357423)', false, '"value" must be a valid GUID']
3231+
]);
3232+
3233+
// Wrapper can be set to a specific value
3234+
Helper.validate(Joi.string().guid({ wrapper: '(' }), [
3235+
['69593D62-71EA-4548-85E4-04FC71357423', false, '"value" must be a valid GUID'],
3236+
['{69593D62-71EA-4548-85E4-04FC71357423}', false, '"value" must be a valid GUID'],
3237+
['[69593D62-71EA-4548-85E4-04FC71357423]', false, '"value" must be a valid GUID'],
3238+
['(69593D62-71EA-4548-85E4-04FC71357423)', true]
3239+
]);
3240+
3241+
// Wrapper can be set to a specific value
3242+
Helper.validate(Joi.string().guid({ wrapper: '[' }), [
3243+
['69593D62-71EA-4548-85E4-04FC71357423', false, '"value" must be a valid GUID'],
3244+
['{69593D62-71EA-4548-85E4-04FC71357423}', false, '"value" must be a valid GUID'],
3245+
['[69593D62-71EA-4548-85E4-04FC71357423]', true],
3246+
['(69593D62-71EA-4548-85E4-04FC71357423)', false, '"value" must be a valid GUID']
3247+
]);
3248+
3249+
});
3250+
3251+
it('errors on invalid wrapper', () => {
3252+
3253+
expect(() => {
3254+
3255+
Joi.string().guid({ wrapper: 1 });
3256+
}).to.throw('"wrapper" must be true, false, or one of "{", "[", "("');
3257+
3258+
expect(() => {
3259+
3260+
Joi.string().guid({ wrapper: '|' });
3261+
}).to.throw('"wrapper" must be true, false, or one of "{", "[", "("');
3262+
});
3263+
31993264
it('validates combination of guid and min', () => {
32003265

32013266
const rule = Joi.string().guid().min(36);

0 commit comments

Comments
 (0)