CVE-2022-25839: Improper Input Validation (CWE-20)
value: The package url-js before 2.1.0 are vulnerable to Improper Input Validation due to improper parsing, which makes it is possible for the hostname to be spoofed. http://\\\\\\\\localhost and http://localhost are the same URL. However, the hostname is not parsed as localhost, and the backslash is reflected as it is.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 5.3
Published: 2022-03-11T20:15Z
Repository: https://github.com/duzun/URL.js
Language: JavaScript
Files changed: bower.json, package-lock.json, package.json, test.js, url.d.ts, url.js, url/URL.d.ts, url/URL.js, url/parseUrl.js
Commit date: 2022-02-28 16:57:51 +0200
Commit message: Fix potential vulnerability for "http://\\\\localhost"

Diff: diff --git a/bower.json b/bower.json
index 21ac8cb..0b970fc 100644
--- a/bower.json
+++ b/bower.json
@@ -1,8 +1,8 @@
 {
   "name": "url-js",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "description": "Simple URL parser, similar to DOM URL",
   "main": "url.js",
   "authors": [
     "Dumitru Uzun <contact@duzun.me> (https://DUzun.Me)"
   ],
diff --git a/package-lock.json b/package-lock.json
index a90e3c4..0c4dedb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,8 +1,8 @@
 {
   "name": "url-js",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "url-js",
diff --git a/package.json b/package.json
index f3d27f9..980bc8b 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "url-js",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "description": "Simple URL parser, similar to DOM URL",
   "main": "dist/url.js",
   "module": "url.js",
   "esnext": "url.js",
   "unpkg": "dist/url.min.js",
diff --git a/test.js b/test.js
index d82855e..ac8f28c 100644
--- a/test.js
+++ b/test.js
@@ -29,10 +29,22 @@ describe('URL', function() {
             expect(String(k)).toBe('http://www.example.com/');
             expect(String(l)).toBe('http://www.example.com/');
             expect(String(m)).toBe('https://www.example.com/path/xyz?var=123');
             expect(String(n)).toBe('https://www.example.com/path/zyx?u');
 
+            // Potential vulnerbility
+            var v = new URLJS('http://\\\\localhost\\');
+
+            expect(String(v)).toBe('http://localhost/');
+            expect(v.hostname).toBe('localhost');
+            expect(v.pathname).toBe('/');
+
+            v = new URLJS('http://\\\\localhost/\\');
+
+            expect(String(v)).toBe('http://localhost//');
+            expect(v.pathname).toBe('//');
+
             // // Rebuild .search from .query and .hostname from .host
             // delete m.search;
             // delete m.hostname;
             // expect(String(m)).toBe('https://www.example.com/path/xyz?var=123');
 
diff --git a/url.d.ts b/url.d.ts
index 9f4ecaa..32771d6 100644
--- a/url.d.ts
+++ b/url.d.ts
@@ -1,9 +1,9 @@
 /**
  *  URL parser module.
  *
  *  @license MIT
- *  @version 2.0.0
+ *  @version 2.1.0
  *  @author Dumitru Uzun (DUzun.Me)
  *  @umd AMD, Browser, CommonJs, noDeps
  */
 export { default } from "./url/URL";
diff --git a/url.js b/url.js
index 832f04e..27e930b 100644
--- a/url.js
+++ b/url.js
@@ -1,9 +1,9 @@
 /**
  *  URL parser module.
  *
  *  @license MIT
- *  @version 2.0.0
+ *  @version 2.1.0
  *  @author Dumitru Uzun (DUzun.Me)
  *  @umd AMD, Browser, CommonJs, noDeps
  */
 export { default } from './url/URL';
diff --git a/url/URL.d.ts b/url/URL.d.ts
index 1d2a5e0..b8cf3d3 100644
--- a/url/URL.d.ts
+++ b/url/URL.d.ts
@@ -1,10 +1,10 @@
 /**
  *  URL parser.
  *
  *  @license MIT
- *  @version 2.0.0
+ *  @version 2.1.0
  *  @author Dumitru Uzun (DUzun.Me)
  *  @umd AMD, Browser, CommonJs, noDeps
  */
 
 import parseUrl from "./parseUrl";
diff --git a/url/URL.js b/url/URL.js
index 4b43b1b..678ad3a 100644
--- a/url/URL.js
+++ b/url/URL.js
@@ -6,11 +6,11 @@ import { NIL, defProp, is_domain, is_url, getDomainName } from './helpers';
 
 /**
  *  URL parser.
  *
  *  @license MIT
- *  @version 2.0.0
+ *  @version 2.1.0
  *  @author Dumitru Uzun (DUzun.Me)
  *  @umd AMD, Browser, CommonJs, noDeps
  */
 export default function URLJS(url, baseURL, parseQuery) {
     if (url != undefined) {
@@ -32,11 +32,11 @@ export default function URLJS(url, baseURL, parseQuery) {
                 _path[_path.length - 1] = url;
                 _path = _path.join('/');
             }
             return parseUrl.call(URLJS, _url.origin + _path, undefined, parseQuery);
         }
-        throw new SyntaxError(`Failed to construct 'URL': Invalid URL`);
+        throw new SyntaxError(`Failed to construct 'URLJS': Invalid URL`);
     }
 }
 
 // ---------------------------------------------------------------------------
 URLJS.parseUrl = parseUrl;
diff --git a/url/parseUrl.js b/url/parseUrl.js
index b0064ae..b990c6e 100644
--- a/url/parseUrl.js
+++ b/url/parseUrl.js
@@ -1,15 +1,18 @@
 import { NIL, getDomainName } from './helpers';
 import toObject from './toObject';
 
 /*globals URL*/
 
+// const _hostname_norm_exp = /^\\/g;
+const _pathname_norm_exp = /\\+/;
+
 const _parse_url_exp = new RegExp([
     '^([\\w.+\\-\\*]+:)//'          // protocol
     , '(([^:/?#]*)(?::([^/?#]*))?@|)' // username:password
-    , '(([^:/?#]*)(?::(\\d+))?)'      // host == hostname:port
-    , '(/[^?#]*|)'                    // pathname
+    , '\\\\*(([^:/\\\\?#]*)(?::(\\d+))?)' // host == hostname:port
+    , '([/\\\\][^?#]*|)'              // pathname
     , '(\\?([^#]*)|)'                 // search & query
     , '(#.*|)$'                       // hash
 ].join(NIL));
 
 const _parse_url_map = {
@@ -30,17 +33,29 @@ export default function parseUrl(href, part, parseQuery) {
     var match = href.match(_parse_url_exp)
         , map = _parse_url_map
         , i, ret = false
         ;
     if (match) {
+        // if (i = match[map.hostname]) {
+        //     match[map.hostname] = i.replace(_hostname_norm_exp, '');
+        // }
+        if (i = match[map.pathname]) {
+            match[map.pathname] = i.replace(_pathname_norm_exp, '/');
+        }
+
         if (part && part in map) {
             ret = match[map[part]] || NIL;
-            if (part == 'pathname') {
-                if (!ret) ret = '/';
-            }
-            if (parseQuery && part == 'query') {
-                ret = toObject(ret || NIL);
+            switch (part) {
+                case 'pathname':
+                    if (!ret) ret = '/';
+                break;
+
+                case 'query':
+                    if (parseQuery) {
+                        ret = toObject(ret || NIL);
+                    }
+                break;
             }
         }
         else {
             let _ = this;
             if(typeof _ != 'function') {

Vulnerable code:   "version": "2.0.0",
  "version": "2.0.0",
  "version": "2.0.0",
 *  @version 2.0.0
 *  @version 2.0.0
 *  @version 2.0.0
 *  @version 2.0.0
        throw new SyntaxError(`Failed to construct 'URL': Invalid URL`);
    , '(([^:/?#]*)(?::(\\d+))?)'      // host == hostname:port
    , '(/[^?#]*|)'                    // pathname
            if (part == 'pathname') {
                if (!ret) ret = '/';
            }
            if (parseQuery && part == 'query') {
                ret = toObject(ret || NIL);
Fixed code:   "version": "2.1.0",
  "version": "2.1.0",
  "version": "2.1.0",
            // Potential vulnerbility
            var v = new URLJS('http://\\\\localhost\\');

            expect(String(v)).toBe('http://localhost/');
            expect(v.hostname).toBe('localhost');
            expect(v.pathname).toBe('/');

            v = new URLJS('http://\\\\localhost/\\');

            expect(String(v)).toBe('http://localhost//');
            expect(v.pathname).toBe('//');

 *  @version 2.1.0
 *  @version 2.1.0
 *  @version 2.1.0
 *  @version 2.1.0
        throw new SyntaxError(`Failed to construct 'URLJS': Invalid URL`);
// const _hostname_norm_exp = /^\\/g;
const _pathname_norm_exp = /\\+/;

    , '\\\\*(([^:/\\\\?#]*)(?::(\\d+))?)' // host == hostname:port
    , '([/\\\\][^?#]*|)'              // pathname
        // if (i = match[map.hostname]) {
        //     match[map.hostname] = i.replace(_hostname_norm_exp, '');
        // }
        if (i = match[map.pathname]) {
            match[map.pathname] = i.replace(_pathname_norm_exp, '/');
        }

            switch (part) {
                case 'pathname':
                    if (!ret) ret = '/';
                break;

                case 'query':
                    if (parseQuery) {
                        ret = toObject(ret || NIL);
                    }
                break;