diff --git a/lib/types/string.js b/lib/types/string.js index 37ab98ff..42abe8b0 100755 --- a/lib/types/string.js +++ b/lib/types/string.js @@ -23,7 +23,10 @@ const internals = { } }, dataUriRegex: /^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/, - hexRegex: /^[a-f0-9]+$/i, + hexRegex: { + withPrefix: /^(0x)?[0-9a-f]+$/i, + withoutPrefix: /^[0-9a-f]+$/i + }, ipRegex: ipRegex({ cidr: 'forbidden' }).regex, isoDurationRegex: /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/, @@ -364,16 +367,18 @@ module.exports = Any.extend({ hex: { method(options = {}) { - Common.assertOptions(options, ['byteAligned']); + Common.assertOptions(options, ['byteAligned', 'withPrefix']); - options = { byteAligned: false, ...options }; + options = { byteAligned: false, withPrefix: false, ...options }; assert(typeof options.byteAligned === 'boolean', 'byteAligned must be boolean'); + assert(typeof options.withPrefix === 'boolean', 'withPrefix must be boolean'); return this.$_addRule({ name: 'hex', args: { options } }); }, validate(value, helpers, { options }) { - if (!internals.hexRegex.test(value)) { + const re = options.withPrefix ? internals.hexRegex.withPrefix : internals.hexRegex.withoutPrefix; + if (!re.test(value)) { return helpers.error('string.hex'); } diff --git a/test/types/string.js b/test/types/string.js index 17b9c454..8ab5db48 100755 --- a/test/types/string.js +++ b/test/types/string.js @@ -4518,6 +4518,21 @@ describe('string', () => { }] ]); }); + + it('validates an hexadecimal string with prefix explicitly required', () => { + + const rule = Joi.string().hex({ withPrefix: true }).strict(); + Helper.validate(rule, [ + ['0x0123456789abcdef', true], + ['123456789abcdef', true], + ['0123afg', false, { + message: '"value" must only contain hexadecimal characters', + path: [], + type: 'string.hex', + context: { value: '0123afg', label: 'value' } + }] + ]); + }); }); describe('hostname()', () => {