From 2772a341c48e419042a8c33c879b420e7cb26e64 Mon Sep 17 00:00:00 2001 From: Nicolas Morel Date: Tue, 23 Apr 2024 10:23:20 +0200 Subject: [PATCH] feat: support encoding uri (follow-up to #3027) --- API.md | 2 +- lib/types/string.js | 16 ++++++++-------- test/types/string.js | 42 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/API.md b/API.md index b74d8782..90345432 100755 --- a/API.md +++ b/API.md @@ -3085,7 +3085,7 @@ Requires the string value to be a valid [RFC 3986](http://tools.ietf.org/html/rf - `relativeOnly` - Restrict only relative URIs. Defaults to `false`. - `allowQuerySquareBrackets` - Allows unencoded square brackets inside the query string. This is **NOT** RFC 3986 compliant but query strings like `abc[]=123&abc[]=456` are very common these days. Defaults to `false`. - `domain` - Validate the domain component using the options specified in [`string.domain()`](#stringdomainoptions). - - `encodeUri` - Encodes the uri with non-alphabetical characters. Defaults to `false`. + - `encodeUri` - When `convert` is true, if the validation fails, attempts to encode the URI using [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI) before validating it again. This allows to provide, for example, unicode URIs, and have it encoded for you. Defaults to `false`. ```js // Accept git or git http/https diff --git a/lib/types/string.js b/lib/types/string.js index 5ddbe6d5..7ce018c6 100755 --- a/lib/types/string.js +++ b/lib/types/string.js @@ -665,11 +665,16 @@ module.exports = Any.extend({ return helpers.error('string.uri'); } - if (helpers.prefs.convert && options.encodeUri) { - value = encodeURI(value); + let match = regex.exec(value); + + if (!match && helpers.prefs.convert && options.encodeUri) { + const encoded = encodeURI(value); + match = regex.exec(encoded); + if (match) { + value = encoded; + } } - const match = regex.exec(value); if (match) { const matched = match[1] || match[2]; if (domain && @@ -690,10 +695,6 @@ module.exports = Any.extend({ return helpers.error('string.uriCustomScheme', { scheme, value }); } - if (options.encodeUri) { - return helpers.error('string.uriEncoding'); - } - return helpers.error('string.uri'); } } @@ -744,7 +745,6 @@ module.exports = Any.extend({ 'string.uri': '{{#label}} must be a valid uri', 'string.uriCustomScheme': '{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern', 'string.uriRelativeOnly': '{{#label}} must be a valid relative uri', - 'string.uriEncoding': '{{#label}} must contain only valid characters or "convert" must be allowed', 'string.uppercase': '{{#label}} must only contain uppercase characters' } }); diff --git a/test/types/string.js b/test/types/string.js index ac96dae3..483a4233 100755 --- a/test/types/string.js +++ b/test/types/string.js @@ -9017,7 +9017,43 @@ describe('string', () => { const schema = Joi.string().uri({ encodeUri: true }); Helper.validate(schema, { convert: true }, [ - ['https://linkedin.com/in/aïssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'] + ['https://linkedin.com/in/aïssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'], + ['https://linkedin.com/in/a%C3%AFssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'], + ['/#.domain.com/', false, { + message: '"value" must be a valid uri', + path: [], + type: 'string.uri', + context: { label: 'value', value: '/#.domain.com/' } + }] + ]); + }); + + it('validates relative uri with accented characters with encoding', () => { + + const schema = Joi.string().uri({ encodeUri: true, allowRelative: true }); + + Helper.validate(schema, { convert: true }, [ + ['/in/aïssa/', true, '/in/a%C3%AFssa/'], + ['/in/a%C3%AFssa/', true, '/in/a%C3%AFssa/'] + ]); + }); + + it('validates uri with encodeUri and scheme', () => { + + const schema = Joi.string().uri({ encodeUri: true, scheme: 'https' }); + + Helper.validate(schema, { convert: true }, [ + ['https://linkedin.com/in/aïssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'], + ['http://linkedin.com/in/aïssa/', false, { + message: '"value" must be a valid uri with a scheme matching the https pattern', + path: [], + type: 'string.uriCustomScheme', + context: { + scheme: 'https', + value: 'http://linkedin.com/in/aïssa/', + label: 'value' + } + }] ]); }); @@ -9027,9 +9063,9 @@ describe('string', () => { Helper.validate(schema, { convert: false }, [ ['https://linkedin.com/in/aïssa/', false, { - message: '"value" must contain only valid characters or "convert" must be allowed', + message: '"value" must be a valid uri', path: [], - type: 'string.uriEncoding', + type: 'string.uri', context: { value: 'https://linkedin.com/in/aïssa/', label: 'value' } }] ]);