CVE-2020-8910: Insufficient Information (NVD-CWE-noinfo)
value: A URL parsing issue in goog.uri of the Google Closure Library versions up to and including v20200224 allows an attacker to send malicious URLs to be parsed by the library and return the wrong authority. Mitigation: update your library to version v20200315.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 6.5
Published: 2020-03-26T12:15Z
Repository: https://github.com/google/closure-library
Language: JavaScript
Files changed: closure/goog/uri/utils.js, closure/goog/uri/utils_test.js
Commit date: 2020-03-13 17:21:04 -0700
Commit message: Fix authority parsing in Closure URI parser.
RELNOTES: Fix authority parsing in Closure URI parser.

PiperOrigin-RevId: 300815722

Diff: diff --git a/closure/goog/uri/utils.js b/closure/goog/uri/utils.js
index c89d80362..48e42c2b6 100644
--- a/closure/goog/uri/utils.js
+++ b/closure/goog/uri/utils.js
@@ -171,29 +171,35 @@ goog.uri.utils.buildFromEncodedParts = function(
  *    $4 = <undefined>       port     -/
  *    $5 = /pub/ietf/uri/    path
  *    $6 = <undefined>       query without ?
  *    $7 = Related           fragment without #
  * </pre>
+ *
+ * TODO(user): separate out the authority terminating characters once this
+ * file is moved to ES6.
  * @type {!RegExp}
  * @private
  */
 goog.uri.utils.splitRe_ = new RegExp(
-    '^' +
+    '^' +  // Anchor against the entire string.
     '(?:' +
     '([^:/?#.]+)' +  // scheme - ignore special characters
                      // used by other URL parts such as :,
                      // ?, /, #, and .
     ':)?' +
     '(?://' +
-    '(?:([^/?#]*)@)?' +  // userInfo
-    '([^/#?]*?)' +       // domain
-    '(?::([0-9]+))?' +   // port
-    '(?=[/\\\\#?]|$)' +  // authority-terminating character
+    '(?:([^\\\\/?#]*)@)?' +  // userInfo
+    '([^\\\\/?#]*?)' +       // domain
+    '(?::([0-9]+))?' +       // port
+    '(?=[\\\\/?#]|$)' +      // authority-terminating character.
     ')?' +
     '([^?#]+)?' +          // path
     '(?:\\?([^#]*))?' +    // query
-    '(?:#([\\s\\S]*))?' +  // fragment
+    '(?:#([\\s\\S]*))?' +  // fragment. Can't use '.*' with 's' flag as Firefox
+                           // doesn't support the flag, and can't use an
+                           // "everything set" ([^]) as IE10 doesn't match any
+                           // characters with it.
     '$');
 
 
 /**
  * The index of each URI component in the return value of goog.uri.utils.split.
diff --git a/closure/goog/uri/utils_test.js b/closure/goog/uri/utils_test.js
index 6ebefa000..fbc1fb143 100644
--- a/closure/goog/uri/utils_test.js
+++ b/closure/goog/uri/utils_test.js
@@ -109,10 +109,23 @@ testSuite({
     assertNull(utils.getQueryData(uri));
     assertNull(utils.getFragmentEncoded(uri));
     assertNull(utils.getFragment(uri));
   },
 
+  testSplitMaliciousUriUsername() {
+    const uri = 'https://malicious.com\\@test.google.com';
+    assertEquals('https', utils.getScheme(uri));
+    assertEquals('malicious.com', utils.getDomain(uri));
+    assertEquals('malicious.com', utils.getDomainEncoded(uri));
+    assertNull(utils.getPort(uri));
+    assertEquals('\\@test.google.com', utils.getPathEncoded(uri));
+    assertEquals('\\@test.google.com', utils.getPath(uri));
+    assertNull(utils.getQueryData(uri));
+    assertNull(utils.getFragmentEncoded(uri));
+    assertNull(utils.getFragment(uri));
+  },
+
   testSplitBadAuthority() {
     // This URL has a syntax error per the RFC (port number must be digits, and
     // host cannot contain a colon except in [...]). This test is solely to
     // 'document' the current behavior, which may affect application handling
     // of erroneous URLs.

Vulnerable code:     '^' +
    '(?:([^/?#]*)@)?' +  // userInfo
    '([^/#?]*?)' +       // domain
    '(?::([0-9]+))?' +   // port
    '(?=[/\\\\#?]|$)' +  // authority-terminating character
    '(?:#([\\s\\S]*))?' +  // fragment
Fixed code:  *
 * TODO(user): separate out the authority terminating characters once this
 * file is moved to ES6.
    '^' +  // Anchor against the entire string.
    '(?:([^\\\\/?#]*)@)?' +  // userInfo
    '([^\\\\/?#]*?)' +       // domain
    '(?::([0-9]+))?' +       // port
    '(?=[\\\\/?#]|$)' +      // authority-terminating character.
    '(?:#([\\s\\S]*))?' +  // fragment. Can't use '.*' with 's' flag as Firefox
                           // doesn't support the flag, and can't use an
                           // "everything set" ([^]) as IE10 doesn't match any
                           // characters with it.
  testSplitMaliciousUriUsername() {
    const uri = 'https://malicious.com\\@test.google.com';
    assertEquals('https', utils.getScheme(uri));
    assertEquals('malicious.com', utils.getDomain(uri));
    assertEquals('malicious.com', utils.getDomainEncoded(uri));
    assertNull(utils.getPort(uri));
    assertEquals('\\@test.google.com', utils.getPathEncoded(uri));
    assertEquals('\\@test.google.com', utils.getPath(uri));
    assertNull(utils.getQueryData(uri));
    assertNull(utils.getFragmentEncoded(uri));
    assertNull(utils.getFragment(uri));
  },