CVE-2022-1233: URL Redirection to Untrusted Site ('Open Redirect') (CWE-601)
value: URL Confusion When Scheme Not Supplied in GitHub repository medialize/uri.js prior to 1.19.11.
CWE description: A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
CVSS v3 base score: 6.1
Published: 2022-04-04T20:15Z
Repository: https://github.com/medialize/uri.js
Language: JavaScript
Files changed: CHANGELOG.md, src/URI.js, test/urls.js
Commit date: 2022-04-03 10:52:17 +0200
Commit message: fix(parse): handle excessive slashes in scheme-relative URLs

reported by @zeyu2001 via huntr.dev

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66e1927..d576563 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,13 @@
 # URI.js - Changelog #
 
 The release notes tracked in this document are also made available on the [releases page](https://github.com/medialize/URI.js/releases)
 
+### master ###
+
+* **SECURITY** fixing [`URI.parse()`](http://medialize.github.io/URI.js/docs.html#static-parse) handle excessive slashes in scheme-relative URLs - disclosed by [zeyu2001](https://github.com/zeyu2001) via https://huntr.dev/
+
 ### 1.19.10 (March 5th 2022) ###
 
 * **SECURITY** fixing [`URI.parse()`](http://medialize.github.io/URI.js/docs.html#static-parse) handle excessive colons in protocol delimiter - disclosed by [huydoppa](https://github.com/huydoppa) via https://huntr.dev/
 
 ### 1.19.9 (March 3rd 2022) ###
diff --git a/src/URI.js b/src/URI.js
index f488e8f..c46352b 100644
--- a/src/URI.js
+++ b/src/URI.js
@@ -516,10 +516,12 @@
       string = string.substring(0, pos);
     }
 
     // slashes and backslashes have lost all meaning for the web protocols (https, http, wss, ws)
     string = string.replace(/^(https?|ftp|wss?)?:+[/\\]*/i, '$1://');
+    // slashes and backslashes have lost all meaning for scheme relative URLs
+    string = string.replace(/^[/\\]{2,}/i, '//');
 
     // extract protocol
     if (string.substring(0, 2) === '//') {
       // relative-scheme
       parts.protocol = null;
diff --git a/test/urls.js b/test/urls.js
index ec6da7c..d8c32ef 100644
--- a/test/urls.js
+++ b/test/urls.js
@@ -579,10 +579,59 @@ var urls = [{
       ip4: false,
       ip6: false,
       idn: false,
       punycode: false
     }
+  }, {
+    name: 'ignoring scheme excessive slashes',
+    url: ':/\\//user:pass@example.org:123/some/directory/file.html?query=string#fragment',
+    _url: '//user:pass@example.org:123/some/directory/file.html?query=string#fragment',
+    parts: {
+      protocol: null,
+      username: 'user',
+      password: 'pass',
+      hostname: 'example.org',
+      port: '123',
+      path: '/some/directory/file.html',
+      query: 'query=string',
+      fragment: 'fragment'
+    },
+    accessors: {
+      protocol: '',
+      username: 'user',
+      password: 'pass',
+      port: '123',
+      path: '/some/directory/file.html',
+      query: 'query=string',
+      fragment: 'fragment',
+      resource: '/some/directory/file.html?query=string#fragment',
+      authority: 'user:pass@example.org:123',
+      origin: 'user:pass@example.org:123',
+      userinfo: 'user:pass',
+      subdomain: '',
+      domain: 'example.org',
+      tld: 'org',
+      directory: '/some/directory',
+      filename: 'file.html',
+      suffix: 'html',
+      hash: '#fragment',
+      search: '?query=string',
+      host: 'example.org:123',
+      hostname: 'example.org'
+    },
+    is: {
+      urn: false,
+      url: true,
+      relative: false,
+      name: true,
+      sld: false,
+      ip: false,
+      ip4: false,
+      ip6: false,
+      idn: false,
+      punycode: false
+    }
   }, {
     name: 'scheme-relative URL',
     url: '//www.example.org/',
     parts: {
       protocol: null,
@@ -627,10 +676,59 @@ var urls = [{
       ip4: false,
       ip6: false,
       idn: false,
       punycode: false
     }
+  }, {
+    name: 'scheme-relative URL excessive slashes',
+    url: '//\\/www.example.org/',
+    _url: '//www.example.org/',
+    parts: {
+      protocol: null,
+      username: null,
+      password: null,
+      hostname: 'www.example.org',
+      port: null,
+      path: '/',
+      query: null,
+      fragment: null
+    },
+    accessors: {
+      protocol: '',
+      username: '',
+      password: '',
+      port: '',
+      path: '/',
+      query: '',
+      fragment: '',
+      resource: '/',
+      authority: 'www.example.org',
+      origin: 'www.example.org',
+      userinfo: '',
+      subdomain: 'www',
+      domain: 'example.org',
+      tld: 'org',
+      directory: '/',
+      filename: '',
+      suffix: '',
+      hash: '',
+      search: '',
+      host: 'www.example.org',
+      hostname: 'www.example.org'
+    },
+    is: {
+      urn: false,
+      url: true,
+      relative: false,
+      name: true,
+      sld: false,
+      ip: false,
+      ip4: false,
+      ip6: false,
+      idn: false,
+      punycode: false
+    }
   }, {
     name: 'missing authority',
     url: 'food:///test/file.csv',
     parts: {
       protocol: 'food',

Fixed code: ### master ###

* **SECURITY** fixing [`URI.parse()`](http://medialize.github.io/URI.js/docs.html#static-parse) handle excessive slashes in scheme-relative URLs - disclosed by [zeyu2001](https://github.com/zeyu2001) via https://huntr.dev/

    // slashes and backslashes have lost all meaning for scheme relative URLs
    string = string.replace(/^[/\\]{2,}/i, '//');
  }, {
    name: 'ignoring scheme excessive slashes',
    url: ':/\\//user:pass@example.org:123/some/directory/file.html?query=string#fragment',
    _url: '//user:pass@example.org:123/some/directory/file.html?query=string#fragment',
    parts: {
      protocol: null,
      username: 'user',
      password: 'pass',
      hostname: 'example.org',
      port: '123',
      path: '/some/directory/file.html',
      query: 'query=string',
      fragment: 'fragment'
    },
    accessors: {
      protocol: '',
      username: 'user',
      password: 'pass',
      port: '123',
      path: '/some/directory/file.html',
      query: 'query=string',
      fragment: 'fragment',
      resource: '/some/directory/file.html?query=string#fragment',
      authority: 'user:pass@example.org:123',
      origin: 'user:pass@example.org:123',
      userinfo: 'user:pass',
      subdomain: '',
      domain: 'example.org',
      tld: 'org',
      directory: '/some/directory',
      filename: 'file.html',
      suffix: 'html',
      hash: '#fragment',
      search: '?query=string',
      host: 'example.org:123',
      hostname: 'example.org'
    },
    is: {
      urn: false,
      url: true,
      relative: false,
      name: true,
      sld: false,
      ip: false,
      ip4: false,
      ip6: false,
      idn: false,
      punycode: false
    }
  }, {
    name: 'scheme-relative URL excessive slashes',
    url: '//\\/www.example.org/',
    _url: '//www.example.org/',
    parts: {
      protocol: null,
      username: null,
      password: null,
      hostname: 'www.example.org',
      port: null,
      path: '/',
      query: null,
      fragment: null
    },
    accessors: {
      protocol: '',
      username: '',
      password: '',
      port: '',
      path: '/',
      query: '',
      fragment: '',
      resource: '/',
      authority: 'www.example.org',
      origin: 'www.example.org',
      userinfo: '',
      subdomain: 'www',
      domain: 'example.org',
      tld: 'org',
      directory: '/',
      filename: '',
      suffix: '',
      hash: '',
      search: '',
      host: 'www.example.org',
      hostname: 'www.example.org'
    },
    is: {
      urn: false,
      url: true,
      relative: false,
      name: true,
      sld: false,
      ip: false,
      ip4: false,
      ip6: false,
      idn: false,
      punycode: false
    }