VYPR
Critical severityNVD Advisory· Published Aug 12, 2018· Updated Aug 5, 2024

CVE-2018-3774

CVE-2018-3774

Description

Incorrect parsing in url-parse <1.4.3 returns wrong hostname which leads to multiple vulnerabilities such as SSRF, Open Redirect, Bypass Authentication Protocol.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

url-parse prior to 1.4.3 incorrectly parses hostnames from URLs, enabling SSRF, open redirect, and authentication bypass attacks.

Vulnerability

The url-parse library for Node.js and browsers prior to version 1.4.3 contains a hostname parsing flaw [1]. The parser does not correctly extract the hostname component from maliciously crafted URL strings, which can lead to misinterpretation of the target host [1]. All versions before 1.4.3 are affected.

Exploitation

An attacker can craft a URL with an embedded hostname that differs from the one actually parsed by url-parse. No special network position or authentication is required; the victim application merely needs to process the attacker-supplied URL using the vulnerable library [1]. The attacker simply supplies a specially formatted URL string to the parsing function.

Impact

Successful exploitation can lead to Server-Side Request Forgery (SSRF), Open Redirect, or Authentication Bypass, depending on how the application uses the parsed hostname [1]. The attacker controls which host the application believes it is communicating with, potentially gaining access to internal resources or tricking users into visiting malicious sites.

Mitigation

Update url-parse to version 1.4.3 or later, which corrects the parsing logic [2][1]. The fix was committed in revision 209c296d302317268afbe19700a70c63ecbeb2d2 [2]. No workaround is provided; upgrading is the recommended action.

AI Insight generated on May 22, 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
>= 1.0.0, < 1.4.31.4.3

Affected products

2
  • ghsa-coords
    Range: >= 1.0.0, < 1.4.3
  • HackerOne/url-parsev5
    Range: 1.4.3

Patches

3
d7b582ec1243

[security] Added missing SECURITY.md

https://github.com/unshiftio/url-parseArnout KazemierJul 29, 2018via ghsa
1 file changed · +47 0
  • SECURITY.md+47 0 added
    @@ -0,0 +1,47 @@
    +# Security Guidelines
    +
    +Please contact us directly at **security@3rd-Eden.com** for any bug that might
    +impact the security of this project. Please prefix the subject of your email
    +with `[security]` in lowercase and square brackets. Our email filters will
    +automatically prevent these messages from being moved to our spam box. All
    +emails that do not include security vulnerabilities will be removed and blocked
    +instantly.
    +
    +In addition to a dedicated email address to receive security related reports,
    +we also have a [Hacker1 account][hacker1] that can be used be used for
    +communicating security related issues.
    +
    +You will receive an acknowledgement of your report within **24 hours** of
    +notification.
    +
    +## Exceptions
    +
    +If you do not receive an acknowledgement within the said time frame please give
    +us the benefit of the doubt as it's possible that we haven't seen it yet. In
    +this case please send us a message **without details** using one of the
    +following methods:
    +
    +- Give a poke on Twitter [@3rdEden](https://twitter.com/3rdEden)
    +- Contact the lead developers of this project on their personal e-mails. You
    +  can find the e-mails in the git logs, for example using the following command:
    +  `git --no-pager show -s --format='%an <%ae>' <gitsha>` where `<gitsha>` is the
    +  SHA1 of their latest commit in the project.
    +
    +Once we have acknowledged receipt of your report and confirmed the bug
    +ourselves we will work with you to fix the vulnerability and publicly
    +acknowledge your responsible disclosure, if you wish.
    +
    +## History
    +
    +> url-parse returns wrong hostname which leads to multiple vulnerabilities such
    +> as SSRF, Open Redirect, Bypass Authentication Protocol.
    +
    +- Hacker1 report: https://hackerone.com/reports/384029
    +- Reported by [lolwaleet](https://hackerone.com/lolwalee)
    +- Triaged by [Liran Tal](https://hackerone.com/lirantal)
    +- Fixed in: 1.4.3
    +
    +---
    +
    +[twitter]: https://twitter.com/3rdEden
    +[hacker1]: https://hackerone.com/3rdeden
    
53b1794e54d0

[security] Sanitize paths, hosts before parsing.

https://github.com/unshiftio/url-parseArnout KazemierJul 29, 2018via ghsa
2 files changed · +50 9
  • index.js+21 9 modified
    @@ -20,6 +20,9 @@ var required = require('requires-port')
     var rules = [
       ['#', 'hash'],                        // Extract from the back.
       ['?', 'query'],                       // Extract from the back.
    +  function sanitize(address) {          // Sanitize what is left of the address
    +    return address.replace('\\', '/');
    +  },
       ['/', 'pathname'],                    // Extract from the back.
       ['@', 'auth', 1],                     // Extract from the front.
       [NaN, 'host', undefined, 1, 1],       // Set left over value.
    @@ -47,7 +50,7 @@ var ignore = { hash: 1, query: 1 };
      *
      * @param {Object|String} loc Optional default location object.
      * @returns {Object} lolcation object.
    - * @api public
    + * @public
      */
     function lolcation(loc) {
       var location = global && global.location || {};
    @@ -89,7 +92,7 @@ function lolcation(loc) {
      *
      * @param {String} address URL we want to extract from.
      * @return {ProtocolExtract} Extracted information.
    - * @api private
    + * @private
      */
     function extractProtocol(address) {
       var match = protocolre.exec(address);
    @@ -107,7 +110,7 @@ function extractProtocol(address) {
      * @param {String} relative Pathname of the relative URL.
      * @param {String} base Pathname of the base URL.
      * @return {String} Resolved pathname.
    - * @api private
    + * @private
      */
     function resolve(relative, base) {
       var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/'))
    @@ -140,11 +143,14 @@ function resolve(relative, base) {
      * create an actual constructor as it's much more memory efficient and
      * faster and it pleases my OCD.
      *
    + * It is worth noting that we should not use `URL` as class name to prevent
    + * clashes with the global URL instance that got introduced in browsers.
    + *
      * @constructor
      * @param {String} address URL we want to parse.
      * @param {Object|String} location Location defaults for relative paths.
      * @param {Boolean|Function} parser Parser for the query string.
    - * @api public
    + * @private
      */
     function Url(address, location, parser) {
       if (!(this instanceof Url)) {
    @@ -190,10 +196,16 @@ function Url(address, location, parser) {
       // When the authority component is absent the URL starts with a path
       // component.
       //
    -  if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname'];
    +  if (!extracted.slashes) instructions[3] = [/(.*)/, 'pathname'];
     
       for (; i < instructions.length; i++) {
         instruction = instructions[i];
    +
    +    if (typeof instruction === 'function') {
    +      address = instruction(address);
    +      continue;
    +    }
    +
         parse = instruction[0];
         key = instruction[1];
     
    @@ -284,8 +296,8 @@ function Url(address, location, parser) {
      *                               used to parse the query.
      *                               When setting the protocol, double slash will be
      *                               removed from the final url if it is true.
    - * @returns {URL}
    - * @api public
    + * @returns {URL} URL instance for chaining.
    + * @public
      */
     function set(part, value, fn) {
       var url = this;
    @@ -370,8 +382,8 @@ function set(part, value, fn) {
      * Transform the properties back in to a valid and full URL string.
      *
      * @param {Function} stringify Optional query stringify function.
    - * @returns {String}
    - * @api public
    + * @returns {String} Compiled version of the URL.
    + * @public
      */
     function toString(stringify) {
       if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;
    
  • test/test.js+29 0 modified
    @@ -192,6 +192,28 @@ describe('url-parse', function () {
         assume(parsed.pathname).equals('/b/c');
       });
     
    +  it('ignores \\ in pathnames', function () {
    +    var url = 'http://google.com:80\\@yahoo.com/#what\\is going on'
    +      , parsed = parse(url);
    +
    +    assume(parsed.port).equals('');
    +    assume(parsed.username).equals('');
    +    assume(parsed.password).equals('');
    +    assume(parsed.hostname).equals('google.com');
    +    assume(parsed.hash).equals('#what\\is going on');
    +
    +    parsed = parse('//\\what-is-up.com');
    +    assume(parsed.pathname).equals('/what-is-up.com');
    +  });
    +
    +  it('correctly ignores multiple slashes //', function () {
    +    var url = '////what-is-up.com'
    +      , parsed = parse(url);
    +
    +    assume(parsed.host).equals('');
    +    assume(parsed.hostname).equals('');
    +  });
    +
       describe('origin', function () {
         it('generates an origin property', function () {
           var url = 'http://google.com:80/pathname'
    @@ -252,6 +274,13 @@ describe('url-parse', function () {
           o = parse('wss://google.com:80/pathname');
           assume(o.origin).equals('wss://google.com:80');
         });
    +
    +    it('maintains the port number for non-default port numbers', function () {
    +      var parsed = parse('http://google.com:8080/pathname');
    +
    +      assume(parsed.host).equals('http://google.com:8080');
    +      assume(parsed.href).equals('http://google.com:8080/pathname');
    +    });
       });
     
       describe('protocol', function () {
    
209c296d3023

[major] Instruction based parsing.

https://github.com/unshiftio/url-parseArnout KazemierJan 18, 2015via ghsa
1 file changed · +58 78
  • index.js+58 78 modified
    @@ -2,12 +2,31 @@
     
     var required = require('requires-port')
       , lolcation = require('./lolcation')
    -  , qs = require('querystringify');
    +  , qs = require('querystringify')
    +  , relativere = /^\/(?!\/)/;
     
    -var keys = ',,protocol,username,password,host,hostname,port,pathname,query,hash'.split(',')
    -  , inherit = { protocol: 1, host: 1, hostname: 1 }
    -  , relativere = /^\/(?!\/)/
    -  , parts = keys.length;
    +/**
    + * These are the parse instructions for the URL parsers, it informs the parser
    + * about:
    + *
    + * 0. The char it Needs to parse, if it's a string it should be done using
    + *    indexOf, RegExp using exec and NaN means set as current value.
    + * 1. The property we should set when parsing this value.
    + * 2. Indication if it's backwards or forward parsing, when set as number it's
    + *    the value of extra chars that should be split off.
    + * 3. Inherit from location if non existing in the parser.
    + * 4. `toLowerCase` the resulting value.
    + */
    +var instructions = [
    +  ['#', 'hash'],                        // Extract from the back.
    +  ['?', 'query'],                       // Extract from the back.
    +  ['//', 'protocol', 2, 1, 1],          // Extract from the front.
    +  ['@', 'auth', 1],                     // Extract from the front.
    +  ['/', 'pathname'],                    // Extract from the back.
    +  [NaN, 'host', undefined, 1, 1],       // Set left over value.
    +  [/\:(\d+)$/, 'port'],                 // RegExp the back.
    +  [NaN, 'hostname', undefined, 1, 1]    // Set left over.
    +];
     
     /**
      * The actual URL instance. Instead of returning an object we've opted-in to
    @@ -25,73 +44,11 @@ function URL(address, location, parser) {
         return new URL(address, location, parser);
       }
     
    -  //
    -  // Story time children:
    -  //
    -  // FireFox 34 has some problems with their Regular Expression engine and
    -  // executing a RegExp can cause a `too much recursion` error. We initially fixed
    -  // this by moving the Regular Expression in the URL constructor so it's created
    -  // every single time. This fixed it for some URL's but the more complex the
    -  // URL's get the easier it is to trigger. Complexer URL like:
    -  //
    -  //   https://www.mozilla.org/en-US/firefox/34.0/whatsnew/?oldversion=33.1
    -  //
    -  // Still triggered the recursion error. After talking with Chrome and FireFox
    -  // engineers it seemed to be caused by:
    -  //
    -  //   https://code.google.com/p/v8/issues/detail?id=430
    -  //
    -  // As FireFox started using Chrome's RegExp engine. After testing various of
    -  // workarounds I finally stumbled upon this gem, use new RegExp as it sometimes
    -  // behaves different then a RegExp literal. The biggest problem with this
    -  // FireFox problem is that it's super hard to reproduce as our "normal" test
    -  // suite doesn't catch it. The only way to reproduce it was run the parser in
    -  // jsperf.com (uses the benchmark module from npm) and supply it the URL
    -  // mentioned above as URL to parse.
    -  //
    -  // Steps for compiling the new RegExp:
    -  //
    -  // 1. Take the regular RegExp as seen below.
    -  // 2. Escape the RegExp using XRegExp.escape from http://xregexp.com/tests/
    -  // 3. ??
    -  // 4. Profit.
    -  //
    -  // RegExp source: /^(?:(?:(([^:\/#\?]+:)?(?:(?:\/\/)(?:(?:(?:([^:@\/#\?]+)(?:\:([^:@\/#\?]*))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((?:\/?(?:[^\/\?#]+\/+)*)(?:[^\?#]*)))?(\?[^#]+)?)(#.*)?/
    -  //
       var relative = relativere.test(address)
    +    , parse, instruction, index, key
         , type = typeof location
         , url = this
    -    , i;
    -
    -  // backwards parsing
    -  if (~(i = address.indexOf('#'))) {
    -    url.hash = address.slice(i);
    -    address = address.slice(0, i);
    -  }
    -
    -  // backwards parsing
    -  if (~(i = address.indexOf('?'))) {
    -    url.query = address.slice(i);
    -    address = address.slice(0, i);
    -  }
    -
    -  // forward parsing
    -  if (~(i = address.indexOf('//'))) {
    -    url.protocol = address.slice(0, i);
    -    address = address.slice(i);
    -  }
    -
    -  // forward parsing
    -  if (~(i = address.indexOf('@'))) {
    -    url.auth = address.slice(0, i);
    -    address = address.slice(i + 1);
    -  }
    -
    -  // backwards parsing
    -  if (~(i = address.indexOf('/'))) {
    -    url.pathname = address.slice(i);
    -    address = address.slice(0, i);
    -  }
    +    , i = 0;
     
       //
       // The following if statements allows this module two have compatibility with
    @@ -115,17 +72,31 @@ function URL(address, location, parser) {
     
       location = lolcation(location);
     
    -  for (; i < parts; key = keys[++i]) {
    -    if (!key) continue;
    +  for (; i < instructions.length; i++) {
    +    instruction = instructions[i];
    +    parse = instruction[0];
    +    key = instruction[1];
    +
    +    if (parse !== parse) {
    +      url[key] = address;
    +    } else if ('string' === typeof parse) {
    +      if (~(index = address.indexOf(parse))) {
    +        if ('number' === typeof instruction[2]) {
    +          url[key] = address.slice(0, index);
    +          address = address.slice(index + instruction[2]);
    +        } else {
    +          url[key] = address.slice(index);
    +          address = address.slice(0, index);
    +        }
    +      }
    +    } else if (index = parse.exec(address)) {
    +      url[key] = index[1];
    +      address = address.slice(0, address.length - index[0].length);
    +    }
     
    -    url[key] = bits[i] || (key in inherit || ('port' === key && relative) ? location[key] || '' : '');
    +    url[key] = url[key] || (instruction[3] || ('port' === key && relative) ? location[key] || '' : '');
     
    -    //
    -    // The protocol, host, host name should always be lower cased even if they
    -    // are supplied in uppercase. This way, when people generate an `origin`
    -    // it be correct.
    -    //
    -    if (i === 2 || i === 5 || i === 6) url[key] = url[key].toLowerCase();
    +    if (instruction[4]) url[key] = url[key].toLowerCase();
       }
     
       //
    @@ -145,6 +116,15 @@ function URL(address, location, parser) {
         url.port = '';
       }
     
    +  //
    +  // Parse down the `auth` for the username and password.
    +  //
    +  if (url.auth) {
    +    instruction = url.auth.split(':');
    +    url.username = instruction[0];
    +    url.password = instruction[1];
    +  }
    +
       //
       // The href is just the compiled result.
       //
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.