Repository: validatorjs/validator.js
Pull request #2437 (2024-08-09 17:55:11)

fixed issue #2432
an email of the type "example*@gmail.com was accepted due to an errant if statement in isEmail.js file which was only checking for the first double quote (user[0] === '"'). Fixed it by adding a secondary "and" condition along with it, to ensure the local part is completely enclosed in the double quotes (&& user[user.length - 1] === '"'). 

## Checklist

- [X] PR contains only changes related; no stray files, etc.
- [ ] README updated (where applicable)
- [x] Tests written (where applicable)
- [ ] References provided in PR (where applicable)


rubiin: could you also add tests for this specific scenario
code0emperor: In which file should I add them? I wasn't sure of that

code0emperor: added few tests for this issue in validators.test.js
code0emperor: yep, am on it rn
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/validatorjs/validator.js/pull/2437?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.96%. Comparing base [(`f81d857`)](https://app.codecov.io/gh/validatorjs/validator.js/commit/f81d85702b06ba1e0a624d4d345cacdde46ed3e0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) to head [(`1250755`)](https://app.codecov.io/gh/validatorjs/validator.js/commit/1250755b831e482078094f9e8b57115f2106b160?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).
> Report is 2 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##            master    #2437      +/-   ##
===========================================
- Coverage   100.00%   99.96%   -0.04%     
===========================================
  Files          110      111       +1     
  Lines         2510     2517       +7     
  Branches       633      634       +1     
===========================================
+ Hits          2510     2516       +6     
- Partials         0        1       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/validatorjs/validator.js/pull/2437?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).

code0emperor: Done, i put the test in the wrong area


Patch:
diff --git a/README.md b/README.md
index 4c0d22c..d92d8eb 100644
--- a/README.md
+++ b/README.md
@@ -166,7 +166,7 @@ Validator                               | Description
 **isStrongPassword(str [, options])**   | check if the string can be considered a strong password 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 }`
 **isTime(str [, options])**             | check if the string is a valid time e.g. [`23:01:59`, new Date().toLocaleTimeString()].<br/><br/> `options` is an object which can contain the keys `hourFormat` or `mode`.<br/><br/>`hourFormat` is a key and defaults to `'hour24'`.<br/><br/>`mode` is a key and defaults to `'default'`. <br/><br/>`hourFormat` can contain the values `'hour12'` or `'hour24'`, `'hour24'` will validate hours in 24 format and `'hour12'` will validate hours in 12 format. <br/><br/>`mode` can contain the values `'default'` or `'withSeconds'`, `'default'` will validate `HH:MM` format, `'withSeconds'` will validate the `HH:MM:SS` format.
 **isTaxID(str, locale)**                | check if the string 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-CA', 'en-GB', 'en-IE', 'en-US', 'es-AR', 'es-ES', 'et-EE', 'fi-FI', 'fr-BE', 'fr-CA', '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', 'uk-UA']`.
-**isURL(str [, options])**              | check if the string is a 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, allow_fragments: true, allow_query_components: true, disallow_auth: false, validate_length: true }`.<br/><br/>`require_protocol` - if set to 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 to false isURL will not check if host is present in the URL.<br/>`require_port` - if set to true isURL will check if port is present in the URL.<br/>`allow_protocol_relative_urls` - if set to true protocol relative URLs will be allowed.<br/>`allow_fragments` - if set to false isURL will return false if fragments are present.<br/>`allow_query_components` - if set to false isURL will return false if query components are present.<br/>`validate_length` - if set to false isURL will skip string length validation (2083 characters is IE max URL length).
+**isURL(str [, options])**              | check if the string is a 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, allow_fragments: true, allow_query_components: true, disallow_auth: false, validate_length: true }`.<br/><br/>`require_protocol` - if set to 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 to false isURL will not check if host is present in the URL.<br/>`require_port` - if set to true isURL will check if port is present in the URL.<br/>`allow_protocol_relative_urls` - if set to true protocol relative URLs will be allowed.<br/>`allow_fragments` - if set to false isURL will return false if fragments are present.<br/>`allow_query_components` - if set to false isURL will return false if query components are present.<br/>`validate_length` - if set to false isURL will skip string length validation. `max_allowed_length` will be ignored if this is set as `false`.<br/>`max_allowed_length` - if set isURL will not allow URLs longer than the specified value (default is 2084 that IE maximum URL length).
 **isULID(str)**                         | check if the string is a [ULID](https://github.com/ulid/spec).
 **isUUID(str [, version])**             | check if the string is an RFC9562 UUID.<br/>`version` is one of `'1'`-`'8'`, `'nil'`, `'max'`, or `'all'`.
 **isVariableWidth(str)**                | check if the string contains a mixture of full and half-width chars.
diff --git a/src/lib/isEmail.js b/src/lib/isEmail.js
index 9d89f8d..1aceca3 100644
--- a/src/lib/isEmail.js
+++ b/src/lib/isEmail.js
@@ -109,11 +109,11 @@ export default function isEmail(str, options) {
 
   if (options.domain_specific_validation && (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com')) {
     /*
-      Previously we removed dots for gmail addresses before validating.
-      This was removed because it allows `multiple..dots@gmail.com`
-      to be reported as valid, but it is not.
-      Gmail only normalizes single dots, removing them from here is pointless,
-      should be done in normalizeEmail
+    Previously we removed dots for gmail addresses before validating.
+    This was removed because it allows `multiple..dots@gmail.com`
+    to be reported as valid, but it is not.
+    Gmail only normalizes single dots, removing them from here is pointless,
+    should be done in normalizeEmail
     */
     user = user.toLowerCase();
 
@@ -166,7 +166,7 @@ export default function isEmail(str, options) {
     if (user.search(new RegExp(`[${options.blacklisted_chars}]+`, 'g')) !== -1) return false;
   }
 
-  if (user[0] === '"') {
+  if (user[0] === '"' && user[user.length - 1] === '"') {
     user = user.slice(1, user.length - 1);
     return options.allow_utf8_local_part ?
       quotedEmailUserUtf8.test(user) :
diff --git a/src/lib/isURL.js b/src/lib/isURL.js
index 3d2b1df..7529f4b 100644
--- a/src/lib/isURL.js
+++ b/src/lib/isURL.js
@@ -13,8 +13,10 @@ protocols - valid protocols can be modified with this option
 require_host - if set as false isURL will not check if host is present in the URL
 require_port - if set as true isURL will check if port is present in the URL
 allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed
-validate_length - if set as false isURL will skip string length validation (IE maximum is 2083)
-
+validate_length - if set as false isURL will skip string length validation
+  max_allowed_length will be ignored if this is set as false
+max_allowed_length - if set isURL will not allow URLs longer than max_allowed_length
+  default is 2084 that IE maximum URL length
 */
 
 
@@ -31,6 +33,7 @@ const default_url_options = {
   allow_fragments: true,
   allow_query_components: true,
   validate_length: true,
+  max_allowed_length: 2084,
 };
 
 const wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
@@ -59,7 +62,7 @@ export default function isURL(url, options) {
   }
   options = merge(options, default_url_options);
 
-  if (options.validate_length && url.length >= 2083) {
+  if (options.validate_length && url.length > options.max_allowed_length) {
     return false;
   }
 


Test patch:
diff --git a/test/validators.test.js b/test/validators.test.js
index f9a2960..a2fd10f 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -71,6 +71,9 @@ describe('Validators', () => {
         'nbsp test@test.com',
         'nbsp_test@te st.com',
         'nbsp_test@test.co m',
+        '"foobar@gmail.com',
+        '"foo"bar@gmail.com',
+        'foo"bar"@gmail.com',
       ],
     });
   });
@@ -787,6 +790,21 @@ describe('Validators', () => {
     });
   });
 
+  it('should allow user to configure the maximum URL length', () => {
+    test({
+      validator: 'isURL',
+      args: [{ max_allowed_length: 20 }],
+      valid: [
+        'http://foobar.com/12', // 20 characters
+        'http://foobar.com/',
+      ],
+      invalid: [
+        'http://foobar.com/123', // 21 characters
+        'http://foobar.com/1234567890',
+      ],
+    });
+  });
+
   it('should validate URLs with port present', () => {
     test({
       validator: 'isURL',