Authorization Bypass Through User-Controlled Key in unshiftio/url-parse
Description
Authorization Bypass Through User-Controlled Key in NPM url-parse prior to 1.5.6.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Authorization bypass in url-parse before 1.5.6 via crafted URL with @ in userinfo, allowing host manipulation.
Vulnerability
The url-parse library versions prior to 1.5.6 contain an authorization bypass vulnerability through a user-controlled key [1][2]. The library incorrectly parses the userinfo component (username and password) when it contains the @ character. Under certain configurations, an attacker can craft a URL where the @ in the userinfo is not properly handled, leading to misinterpretation of the hostname. The official description states: "Authorization Bypass Through User-Controlled Key" [2].
Exploitation
An attacker only needs the ability to supply a crafted URL to an application that uses url-parse to parse it. For example, a URL like http://user@evil.com@victim.com/ could cause the parser to extract user@evil.com as the username and victim.com as the host, whereas the intended host might be evil.com [3]. The attacker controls the key (userinfo) that influences authorization decisions. No special privileges or user interaction beyond providing the URL is required.
Impact
Successful exploitation allows an attacker to bypass authorization checks. If the application relies on the parsed hostname for access control, the attacker can make the application believe the request is destined for a different (permitted) host while actually the intended host is attacker-controlled. This can lead to unauthorized access to resources, data exposure, or other security breaches depending on the application context [2].
Mitigation
The vulnerability is fixed in url-parse version 1.5.6, released with commit 9be7ee88afd2bb04e4d5a1a8da9a389ac13f8c40 [3]. Users should upgrade to at least version 1.5.6. As of the publication date (2022-02-14), no workaround is documented; upgrading is the recommended action. The library is widely used in Node.js and browser environments [1].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
url-parsenpm | >= 0.1.0, < 1.5.6 | 1.5.6 |
Affected products
2- unshiftio/unshiftio/url-parsev5Range: unspecified
Patches
19be7ee88afd2[fix] Correctly handle userinfo containing the at sign
2 files changed · +98 −7
index.js+30 −7 modified@@ -304,7 +304,11 @@ function Url(address, location, parser) { if (parse !== parse) { url[key] = address; } else if ('string' === typeof parse) { - if (~(index = address.indexOf(parse))) { + index = parse === '@' + ? address.lastIndexOf(parse) + : address.indexOf(parse); + + if (~index) { if ('number' === typeof instruction[2]) { url[key] = address.slice(0, index); address = address.slice(index + instruction[2]); @@ -370,10 +374,21 @@ function Url(address, location, parser) { // Parse down the `auth` for the username and password. // url.username = url.password = ''; + if (url.auth) { - instruction = url.auth.split(':'); - url.username = instruction[0]; - url.password = instruction[1] || ''; + index = url.auth.indexOf(':'); + + if (~index) { + url.username = url.auth.slice(0, index); + url.username = encodeURIComponent(decodeURIComponent(url.username)); + + url.password = url.auth.slice(index + 1); + url.password = encodeURIComponent(decodeURIComponent(url.password)) + } else { + url.username = encodeURIComponent(decodeURIComponent(url.auth)); + } + + url.auth = url.password ? url.username +':'+ url.password : url.username; } url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host @@ -465,9 +480,17 @@ function set(part, value, fn) { break; case 'auth': - var splits = value.split(':'); - url.username = splits[0]; - url.password = splits.length === 2 ? splits[1] : ''; + var index = value.indexOf(':'); + + if (~index) { + url.username = value.slice(0, index); + url.username = encodeURIComponent(decodeURIComponent(url.username)); + + url.password = value.slice(index + 1); + url.password = encodeURIComponent(decodeURIComponent(url.password)); + } else { + url.username = encodeURIComponent(decodeURIComponent(value)); + } } for (var i = 0; i < rules.length; i++) {
test/test.js+68 −0 modified@@ -689,6 +689,54 @@ describe('url-parse', function () { assume(parsed.hostname).equals('www.example.com'); assume(parsed.href).equals(url); }); + + it('handles @ in username', function () { + var url = 'http://user@@www.example.com/' + , parsed = parse(url); + + assume(parsed.protocol).equals('http:'); + assume(parsed.auth).equals('user%40'); + assume(parsed.username).equals('user%40'); + assume(parsed.password).equals(''); + assume(parsed.hostname).equals('www.example.com'); + assume(parsed.pathname).equals('/'); + assume(parsed.href).equals('http://user%40@www.example.com/'); + + url = 'http://user%40@www.example.com/'; + parsed = parse(url); + + assume(parsed.protocol).equals('http:'); + assume(parsed.auth).equals('user%40'); + assume(parsed.username).equals('user%40'); + assume(parsed.password).equals(''); + assume(parsed.hostname).equals('www.example.com'); + assume(parsed.pathname).equals('/'); + assume(parsed.href).equals('http://user%40@www.example.com/'); + }); + + it('handles @ in password', function () { + var url = 'http://user@:pas:s@@www.example.com/' + , parsed = parse(url); + + assume(parsed.protocol).equals('http:'); + assume(parsed.auth).equals('user%40:pas%3As%40'); + assume(parsed.username).equals('user%40'); + assume(parsed.password).equals('pas%3As%40'); + assume(parsed.hostname).equals('www.example.com'); + assume(parsed.pathname).equals('/'); + assume(parsed.href).equals('http://user%40:pas%3As%40@www.example.com/'); + + url = 'http://user%40:pas%3As%40@www.example.com/' + parsed = parse(url); + + assume(parsed.protocol).equals('http:'); + assume(parsed.auth).equals('user%40:pas%3As%40'); + assume(parsed.username).equals('user%40'); + assume(parsed.password).equals('pas%3As%40'); + assume(parsed.hostname).equals('www.example.com'); + assume(parsed.pathname).equals('/'); + assume(parsed.href).equals('http://user%40:pas%3As%40@www.example.com/'); + }); }); it('accepts multiple ???', function () { @@ -1124,6 +1172,26 @@ describe('url-parse', function () { assume(data.username).equals(''); assume(data.password).equals('quux'); assume(data.href).equals('https://:quux@example.com/'); + + assume(data.set('auth', 'user@:pass@')).equals(data); + assume(data.username).equals('user%40'); + assume(data.password).equals('pass%40'); + assume(data.href).equals('https://user%40:pass%40@example.com/'); + + assume(data.set('auth', 'user%40:pass%40')).equals(data); + assume(data.username).equals('user%40'); + assume(data.password).equals('pass%40'); + assume(data.href).equals('https://user%40:pass%40@example.com/'); + + assume(data.set('auth', 'user:pass:word')).equals(data); + assume(data.username).equals('user'); + assume(data.password).equals('pass%3Aword'); + assume(data.href).equals('https://user:pass%3Aword@example.com/'); + + assume(data.set('auth', 'user:pass%3Aword')).equals(data); + assume(data.username).equals('user'); + assume(data.password).equals('pass%3Aword'); + assume(data.href).equals('https://user:pass%3Aword@example.com/'); }); it('updates other values', function () {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-rqff-837h-mm52ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-0512ghsaADVISORY
- github.com/unshiftio/url-parse/commit/9be7ee88afd2bb04e4d5a1a8da9a389ac13f8c40ghsaWEB
- huntr.dev/bounties/6d1bc51f-1876-4f5b-a2c2-734e09e8e05bghsaWEB
- lists.debian.org/debian-lts-announce/2023/02/msg00030.htmlghsamailing-listWEB
News mentions
0No linked articles in our index yet.