VYPR
Moderate severityNVD Advisory· Published Feb 14, 2022· Updated Aug 2, 2024

Authorization Bypass Through User-Controlled Key in unshiftio/url-parse

CVE-2022-0512

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.

PackageAffected versionsPatched versions
url-parsenpm
>= 0.1.0, < 1.5.61.5.6

Affected products

2
  • ghsa-coords
    Range: >= 0.1.0, < 1.5.6
  • unshiftio/unshiftio/url-parsev5
    Range: unspecified

Patches

1
9be7ee88afd2

[fix] Correctly handle userinfo containing the at sign

https://github.com/unshiftio/url-parseLuigi PincaJan 8, 2022via ghsa
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

News mentions

0

No linked articles in our index yet.