Skip to content

Commit

Permalink
isURL(): Allow URLs to have only a username in the userinfo subcompon…
Browse files Browse the repository at this point in the history
…ent (#1644)

* Added some missing options to the isURL() docs

* Allow URLs to have a userinfo section with only a username

The 'userinfo' part of a URL may, according to RFC 1738, contain only
a username followed by an '@' sign. The previous behavior of the
isURL() function would return false if the userinfo section did not
have a colon.

In addition to the change in the function, tests have been added to
ensure the following exmaples are considered valid:

 - http://user@example.com
 - http://user:@example.com
 - http://user:pass@example.com

The following are considered not valid:

 - http://@example.com
 - http://:@example.com
 - http://:example.com

As a practical example, Sentry (https://github.com/getsentry/sentry)
uses a format like http://9b9cd2ef993c1fd9c14cbb88466@example.com/10
for it's DSNs (which are just URLs).
  • Loading branch information
jbuchmann-coosto committed Apr 17, 2021
1 parent 9ee1b6b commit 05ceb18
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 7 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Here is a list of the validators currently available.

Validator | Description
--------------------------------------- | --------------------------------------
**contains(str, seed [, options ])** | check if the string contains the seed.<br/><br/>`options` is an object that defaults to `{ ignoreCase: false}`.<br/>`ignoreCase` specified whether the case of the substring be same or not.
**contains(str, seed [, options ])** | check if the string contains the seed.<br/><br/>`options` is an object that defaults to `{ ignoreCase: false}`.<br/>`ignoreCase` specified whether the case of the substring be same or not.
**equals(str, comparison)** | check if the string matches the comparison.
**isAfter(str [, date])** | check if the string is a date that's after the specified date (defaults to now).
**isAlpha(str [, locale, options])** | check if the string contains only letters (a-zA-Z).<br/><br/>Locale is one of `['ar', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', 'ar-JO', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-QA', 'ar-QM', 'ar-SA', 'ar-SD', 'ar-SY', 'ar-TN', 'ar-YE', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'en-AU', 'en-GB', 'en-HK', 'en-IN', 'en-NZ', 'en-US', 'en-ZA', 'en-ZM', 'es-ES', 'fa-IR', 'fr-CA', 'fr-FR', 'he', 'hu-HU', 'it-IT', 'ku-IQ', 'nb-NO', 'nl-NL', 'nn-NO', 'pl-PL', 'pt-BR', 'pt-PT', 'ru-RU', 'sl-SI', 'sk-SK', 'sr-RS', 'sr-RS@latin', 'sv-SE', 'tr-TR', 'uk-UA']`) and defaults to `en-US`. Locale list is `validator.isAlphaLocales`. options is an optional object that can be supplied with the following key(s): ignore which can either be a String or RegExp of characters to be ignored e.g. " -" will ignore spaces and -'s.
Expand Down Expand Up @@ -130,9 +130,9 @@ Validator | Description
**isISSN(str [, options])** | check if the string is an [ISSN](https://en.wikipedia.org/wiki/International_Standard_Serial_Number).<br/><br/>`options` is an object which defaults to `{ case_sensitive: false, require_hyphen: false }`. If `case_sensitive` is true, ISSNs with a lowercase `'x'` as the check digit are rejected.
**isJSON(str [, options])** | check if the string is valid JSON (note: uses JSON.parse).<br/><br/>`options` is an object which defaults to `{ allow_primitives: false }`. If `allow_primitives` is true, the primitives 'true', 'false' and 'null' are accepted as valid JSON values.
**isJWT(str)** | check if the string is valid JWT token.
**isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.<br/><br/>`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
**isLatLong(str [, options])** | check if the string is a valid latitude-longitude coordinate in the format `lat,long` or `lat, long`.<br/><br/>`options` is an object that defaults to `{ checkDMS: false }`. Pass `checkDMS` as `true` to validate DMS(degrees, minutes, and seconds) latitude-longitude format.
**isLength(str [, options])** | check if the string's length falls in a range.<br/><br/>`options` is an object which defaults to `{min:0, max: undefined}`. Note: this function takes into account surrogate pairs.
**isLicensePlate(str [, locale])** | check if string matches the format of a country's license plate.<br/><br/>(locale is one of `['de-DE', 'de-LI', 'pt-PT', 'sq-AL', 'pt-BR'']` or `any`).
**isLicensePlate(str [, locale])** | check if string matches the format of a country's license plate.<br/><br/>(locale is one of `['de-DE', 'de-LI', 'pt-PT', 'sq-AL', 'pt-BR'']` or `any`).
**isLocale(str)** | check if the string is a locale
**isLowercase(str)** | check if the string is lowercase.
**isMACAddress(str)** | check if the string is a MAC address.<br/><br/>`options` is an object which defaults to `{no_separators: false}`. If `no_separators` is true, the validator will allow MAC addresses without separators. Also, it allows the use of hyphens, spaces or dots e.g '01 02 03 04 05 ab', '01-02-03-04-05-ab' or '0102.0304.05ab'.
Expand All @@ -155,7 +155,7 @@ Validator | Description
**isSlug** | Check if the string is of type slug. `Options` allow a single hyphen between string. e.g. [`cn-cn`, `cn-c-c`]
**isStrongPassword(str [, options])** | Check if a password is strong or not. Allows for custom requirements or scoring rules. If `returnScore` is true, then the function returns an integer score for the password rather than a boolean.<br/>Default options: <br/>`{ minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1, returnScore: false, pointsPerUnique: 1, pointsPerRepeat: 0.5, pointsForContainingLower: 10, pointsForContainingUpper: 10, pointsForContainingNumber: 10, pointsForContainingSymbol: 10 }`
**isTaxID(str, locale)** | Check if the given value is a valid Tax Identification Number. Default locale is `en-US`.<br/><br/>More info about exact TIN support can be found in `src/lib/isTaxID.js`<br/><br/>Supported locales: `[ 'bg-BG', 'cs-CZ', 'de-AT', 'de-DE', 'dk-DK', 'el-CY', 'el-GR', 'en-GB', 'en-IE', 'en-US', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-FR', 'fr-LU', 'hr-HR', 'hu-HU', 'it-IT', 'lb-LU', 'lt-LT', 'lv-LV' 'mt-MT', 'nl-BE', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE' ]`
**isURL(str [, options])** | check if the string is an URL.<br/><br/>`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, disallow_auth: false }`.<br/><br/>require_protocol - if set as true isURL will return false if protocol is not present in the URL.<br/>require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option.<br/>protocols - valid protocols can be modified with this option.<br/>require_host - if set as false isURL will not check if host is present in the URL.<br/>require_port - if set as true isURL will check if port is present in the URL.<br/>allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed.<br/>validate_length - if set as false isURL will skip string length validation (2083 characters is IE max URL length).
**isURL(str [, options])** | check if the string is an URL.<br/><br/>`options` is an object which defaults to `{ protocols: ['http','https','ftp'], require_tld: true, require_protocol: false, require_host: true, require_port: false, require_valid_protocol: true, allow_underscores: false, host_whitelist: false, host_blacklist: false, allow_trailing_dot: false, allow_protocol_relative_urls: false, disallow_auth: false, validate_length: true }`.<br/><br/>require_protocol - if set as true isURL will return false if protocol is not present in the URL.<br/>require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option.<br/>protocols - valid protocols can be modified with this option.<br/>require_host - if set as false isURL will not check if host is present in the URL.<br/>require_port - if set as true isURL will check if port is present in the URL.<br/>allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed.<br/>validate_length - if set as false isURL will skip string length validation (2083 characters is IE max URL length).
**isUUID(str [, version])** | check if the string is a UUID (version 3, 4 or 5).
**isVariableWidth(str)** | check if the string contains a mixture of full and half-width chars.
**isVAT(str, countryCode)** | checks that the string is a [valid VAT number](https://en.wikipedia.org/wiki/VAT_identification_number) if validation is available for the given country code matching [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). <br/><br/>Available country codes: `[ 'GB', 'IT' ]`.
Expand Down
5 changes: 4 additions & 1 deletion src/lib/isURL.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ export default function isURL(url, options) {
if (options.disallow_auth) {
return false;
}
if (split[0] === '' || split[0].substr(0, 1) === ':') {
return false;
}
auth = split.shift();
if (auth.indexOf(':') === -1 || (auth.indexOf(':') >= 0 && auth.split(':').length > 2)) {
if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
return false;
}
}
Expand Down
23 changes: 21 additions & 2 deletions test/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ describe('Validators', () => {
'http://www.foobar.com/~foobar',
'http://user:pass@www.foobar.com/',
'http://user:@www.foobar.com/',
'http://user@www.foobar.com',
'http://127.0.0.1/',
'http://10.0.0.0/',
'http://189.123.14.13/',
Expand All @@ -374,7 +375,6 @@ describe('Validators', () => {
'http://[::FFFF:129.144.52.38]:80/index.html',
'http://[2010:836B:4179::836B:4179]',
'http://example.com/example.json#/foo/bar',
'http://user:@www.foobar.com',
'http://1337.com',
],
invalid: [
Expand Down Expand Up @@ -405,6 +405,8 @@ describe('Validators', () => {
'http://lol: @foobar.com/',
'http://www.foo_bar.com/',
'http://www.foobar.com/\t',
'http://@foobar.com',
'http://:@foobar.com',
'http://\n@www.foobar.com/',
'',
`http://foobar.com/${new Array(2083).join('f')}`,
Expand All @@ -416,7 +418,6 @@ describe('Validators', () => {
'////foobar.com',
'http:////foobar.com',
'https://example.com/foo/<script>alert(\'XSS\')</script>/',
'myemail@mail.com',
],
});
});
Expand Down Expand Up @@ -668,6 +669,24 @@ describe('Validators', () => {
});
});

it('should accept urls containing authentication information', () => {
test({
validator: 'isURL',
args: [{ disallow_auth: false }],
valid: [
'user@example.com',
'user:@example.com',
'user:password@example.com',
],
invalid: [
'user:user:password@example.com',
'@example.com',
':@example.com',
':example.com',
],
});
});

it('should allow user to skip URL length validation', () => {
test({
validator: 'isURL',
Expand Down

0 comments on commit 05ceb18

Please sign in to comment.