CVE-2021-3794: Other (NVD-CWE-Other)
value: vuelidate is vulnerable to Inefficient Regular Expression Complexity
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 7.5
Published: 2021-09-15T13:15Z
Repository: https://github.com/vuelidate/vuelidate
Language: JavaScript
Files changed: packages/validators/src/raw/__tests__/url.spec.js, packages/validators/src/raw/url.js
Commit date: 2021-09-10 10:20:41 +0300
Commit message: fix(validators): update the url validator, to reduce potential ReDos attacks (#933)


Diff: diff --git a/packages/validators/src/raw/__tests__/url.spec.js b/packages/validators/src/raw/__tests__/url.spec.js
index a5ab50e..6535ce0 100644
--- a/packages/validators/src/raw/__tests__/url.spec.js
+++ b/packages/validators/src/raw/__tests__/url.spec.js
@@ -8,10 +8,15 @@ describe('url validator', () => {
   const correctUrls = [
     'http://foo.com/blah_blah',
     'HTTP://FOO.COM/BLAH_BLAH',
     'HTTP://FOO.COM/blah_blah',
     'http://foo.com/blah_blah/',
+    /** domains ending with a dot at the end are valid, {@see http://www.dns-sd.org/trailingdotsindomainnames.html} */
+    'http://www.foo.bar./',
+    'http://www.foo.bar.',
+    'http://foo.bar.',
+    'http://foo.bar./',
     'http://foo.com/blah_blah_(wikipedia)',
     'http://foo.com/blah_blah_(wikipedia)_(again)',
     'http://www.example.com/wpstyle/?p=364',
     'https://www.example.com/foo/?bar=baz&inga=42&quux',
     'http://✪df.ws/123',
@@ -38,11 +43,11 @@ describe('url validator', () => {
     'ftp://foo.bar/baz',
     'http://foo.bar/?q=Test%20URL-encoded%20stuff',
     'http://مثال.إختبار',
     'http://例子.测试',
     'http://उदाहरण.परीक्षा',
-    "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+    'http://-.~_!$&\'()*+,;=:%40:80%2f::::::@example.com',
     'http://1337.net',
     'http://a.b-c.de',
     'http://223.255.255.254'
   ]
   const incorrectUrls = [
@@ -78,23 +83,18 @@ describe('url validator', () => {
     'http://224.1.1.1',
     'http://1.1.1.1.1',
     'http://123.123.123',
     'http://3628126748',
     'http://.www.foo.bar/',
-    'http://www.foo.bar./',
     'http://.www.foo.bar./',
     'http://10.1.1.1',
     'http://10.1.1.254'
   ]
 
-  correctUrls.forEach((urlString) => {
-    it(`should validate correct url ${urlString}`, () => {
-      expect(url(urlString)).toBe(true)
-    })
+  it.each(correctUrls)('should validate correct url %s', (urlString) => {
+    expect(url(urlString)).toBe(true)
   })
 
-  incorrectUrls.forEach((urlString) => {
-    it(`should not validate incorrect url ${urlString}`, () => {
-      expect(url(urlString)).toBe(false)
-    })
+  it.each(incorrectUrls)('should not validate incorrect url %s', (urlString) => {
+    expect(url(urlString)).toBe(false)
   })
 })
diff --git a/packages/validators/src/raw/url.js b/packages/validators/src/raw/url.js
index b112e4c..adf55fb 100644
--- a/packages/validators/src/raw/url.js
+++ b/packages/validators/src/raw/url.js
@@ -1,5 +1,8 @@
 import { regex } from '../common'
 
-const urlRegex = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i
+/**
+ * Regex taken from {@link https://gist.github.com/dperini/729294}
+ */
+const urlRegex = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i
 
 export default regex(urlRegex)

Vulnerable code:     "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
    'http://www.foo.bar./',
  correctUrls.forEach((urlString) => {
    it(`should validate correct url ${urlString}`, () => {
      expect(url(urlString)).toBe(true)
    })
  incorrectUrls.forEach((urlString) => {
    it(`should not validate incorrect url ${urlString}`, () => {
      expect(url(urlString)).toBe(false)
    })
const urlRegex = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i
Fixed code:     /** domains ending with a dot at the end are valid, {@see http://www.dns-sd.org/trailingdotsindomainnames.html} */
    'http://www.foo.bar./',
    'http://www.foo.bar.',
    'http://foo.bar.',
    'http://foo.bar./',
    'http://-.~_!$&\'()*+,;=:%40:80%2f::::::@example.com',
  it.each(correctUrls)('should validate correct url %s', (urlString) => {
    expect(url(urlString)).toBe(true)
  it.each(incorrectUrls)('should not validate incorrect url %s', (urlString) => {
    expect(url(urlString)).toBe(false)
/**
 * Regex taken from {@link https://gist.github.com/dperini/729294}
 */
const urlRegex = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i