Commit 52cd1806 authored by Xavier Guimard's avatar Xavier Guimard

New upstream version 1.4.7

parent 40ce5e37
......@@ -3,12 +3,12 @@ language: node_js
matrix:
fast_finish: true
include:
- node_js: "10"
env: SCRIPT=test
- node_js: "8"
env: SCRIPT=test
- node_js: "6"
env: SCRIPT=test
- node_js: "4"
env: SCRIPT=test
- node_js: "8"
env:
- secure: IF01oyIKSs0C5dARdYRTilKnU1TG4zenjjEPClkQxAWIpUOxl9xcNJWDVEOPxJ/4pVt+pozyT80Rp7efh6ZiREJIQI1tUboBKSqZzSbnD5uViQNSbQ90PaDP0FIUc0IQ5o07W36rijBB0DTmtU1VofzN9PKkJO7XiSSXevI8RcM=
......
......@@ -38,14 +38,14 @@ All examples assume that this library is bootstrapped using:
```js
'use strict';
var URL = require('url-parse');
var Url = require('url-parse');
```
To parse an URL simply call the `URL` method with the URL that needs to be
transformed into an object.
```js
var url = new URL('https://github.com/foo/bar');
var url = new Url('https://github.com/foo/bar');
```
The `new` keyword is optional but it will save you an extra function invocation.
......@@ -100,7 +100,7 @@ var parse = require('url-parse');
parse('hostname', {});
```
### URL.set(key, value)
### Url.set(key, value)
A simple helper function to change parts of the URL and propagating it through
all properties. When you set a new `host` you want the same value to be applied
......@@ -117,7 +117,7 @@ console.log(parsed.href); // http://yahoo.com/parse-things
It's aware of default ports so you cannot set a port 80 on an URL which has
`http` as protocol.
### URL.toString()
### Url.toString()
The returned `url` object comes with a custom `toString` method which will
generate a full URL again when called. The method accepts an extra function
......
# 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 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
> The `extractProtocol` method does not return the correct protocol when
> provided with unsanitized content which could lead to false positives.
- **Reporter credits**
- Reported through our security email & Twitter interaction.
- Twitter: [@ronperris](https://twitter.com/ronperris)
- Fixed in: 1.4.5
---
> url-parse returns wrong hostname which leads to multiple vulnerabilities such
> as SSRF, Open Redirect, Bypass Authentication Protocol.
- **Reporter credits**
- Hacker1: [lolwaleet](https://hackerone.com/lolwalee)
- Twitter: [@ahm3dsec](https://twitter.com/ahm3dsec)
- Blog: [0xahmed.ninja](https://0xahmed.ninja)
- Hacker1 report: https://hackerone.com/reports/384029
- Triaged by [Liran Tal](https://hackerone.com/lirantal)
- Fixed in: 1.4.3
---
[twitter]: https://twitter.com/3rdEden
[hacker1]: https://hackerone.com/3rdeden
......@@ -2,8 +2,20 @@
var required = require('requires-port')
, qs = require('querystringify')
, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//
, protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i
, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//;
, whitespace = '[\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\xA0\\u1680\\u180E\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200A\\u202F\\u205F\\u3000\\u2028\\u2029\\uFEFF]'
, left = new RegExp('^'+ whitespace +'+');
/**
* Trim a given string.
*
* @param {String} str String to trim.
* @public
*/
function trimLeft(str) {
return (str ? str : '').toString().replace(left, '');
}
/**
* These are the parse rules for the URL parser, it informs the parser
......@@ -20,6 +32,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,19 +62,27 @@ var ignore = { hash: 1, query: 1 };
*
* @param {Object|String} loc Optional default location object.
* @returns {Object} lolcation object.
* @api public
* @public
*/
function lolcation(loc) {
loc = loc || global.location || {};
var globalVar;
if (typeof window !== 'undefined') globalVar = window;
else if (typeof global !== 'undefined') globalVar = global;
else if (typeof self !== 'undefined') globalVar = self;
else globalVar = {};
var location = globalVar.location || {};
loc = loc || location;
var finaldestination = {}
, type = typeof loc
, key;
if ('blob:' === loc.protocol) {
finaldestination = new URL(unescape(loc.pathname), {});
finaldestination = new Url(unescape(loc.pathname), {});
} else if ('string' === type) {
finaldestination = new URL(loc, {});
finaldestination = new Url(loc, {});
for (key in ignore) delete finaldestination[key];
} else if ('object' === type) {
for (key in loc) {
......@@ -88,9 +111,10 @@ function lolcation(loc) {
*
* @param {String} address URL we want to extract from.
* @return {ProtocolExtract} Extracted information.
* @api private
* @private
*/
function extractProtocol(address) {
address = trimLeft(address);
var match = protocolre.exec(address);
return {
......@@ -106,9 +130,11 @@ 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) {
if (relative === '') return base;
var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/'))
, i = path.length
, last = path[i - 1]
......@@ -139,15 +165,20 @@ 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
* @param {Object|String} [location] Location defaults for relative paths.
* @param {Boolean|Function} [parser] Parser for the query string.
* @private
*/
function URL(address, location, parser) {
if (!(this instanceof URL)) {
return new URL(address, location, parser);
function Url(address, location, parser) {
address = trimLeft(address);
if (!(this instanceof Url)) {
return new Url(address, location, parser);
}
var relative, extracted, parse, instruction, index, key
......@@ -189,10 +220,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];
......@@ -283,8 +320,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;
......@@ -369,8 +406,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;
......@@ -399,14 +436,15 @@ function toString(stringify) {
return result;
}
URL.prototype = { set: set, toString: toString };
Url.prototype = { set: set, toString: toString };
//
// Expose the URL parser and some additional properties that might be useful for
// others or testing.
//
URL.extractProtocol = extractProtocol;
URL.location = lolcation;
URL.qs = qs;
Url.extractProtocol = extractProtocol;
Url.location = lolcation;
Url.trimLeft = trimLeft;
Url.qs = qs;
module.exports = URL;
module.exports = Url;
{
"name": "url-parse",
"version": "1.2.0",
"version": "1.4.7",
"description": "Small footprint URL parser that works seamlessly across Node.js and browser environments",
"main": "index.js",
"scripts": {
"browserify": "mkdir -p dist && browserify index.js -s URLParse | uglifyjs -cm -o dist/url-parse.min.js",
"browserify": "rm -rf dist && mkdir -p dist && browserify index.js -s URLParse -o dist/url-parse.js",
"minify": "uglifyjs dist/url-parse.js --source-map -cm -o dist/url-parse.min.js",
"test": "nyc --reporter=html --reporter=text mocha test/test.js",
"test-browser": "node test/browser.js",
"prepublishOnly": "npm run browserify",
"prepublishOnly": "npm run browserify && npm run minify",
"watch": "mocha --watch test/test.js"
},
"files": [
......@@ -32,17 +33,17 @@
"author": "Arnout Kazemier",
"license": "MIT",
"dependencies": {
"querystringify": "~1.0.0",
"requires-port": "~1.0.0"
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
},
"devDependencies": {
"assume": "~1.5.0",
"browserify": "~14.5.0",
"mocha": "~4.0.0",
"nyc": "~11.3.0",
"pre-commit": "~1.2.0",
"sauce-browsers": "~1.0.0",
"sauce-test": "~1.3.3",
"uglify-js": "~3.1.0"
"assume": "^2.2.0",
"browserify": "^16.2.3",
"mocha": "^6.1.4",
"nyc": "^14.0.0",
"pre-commit": "^1.2.2",
"sauce-browsers": "^2.0.0",
"sauce-test": "^1.3.3",
"uglify-js": "^3.5.7"
}
}
......@@ -12,16 +12,19 @@ const platforms = sauceBrowsers([
{ name: 'firefox', version: ['oldest', 'latest'] },
{ name: 'internet explorer', version: 'oldest..latest' },
{ name: 'iphone', version: ['oldest', 'latest'] },
{ name: 'opera', version: 'oldest..latest' },
{ name: 'safari', version: 'oldest..latest' },
{ name: 'microsoftedge', version: 'oldest..latest' }
]).then((platforms) => {
return platforms.map((platform) => {
return {
const ret = {
browserName: platform.api_name,
version: platform.short_version,
platform: platform.os
};
if (ret.browserName === 'android') ret.deviceName = platform.long_name;
return ret;
});
});
......
......@@ -29,8 +29,53 @@ describe('url-parse', function () {
assume(url.hostname).to.be.a('string');
});
it('works when the global variable is not defined', function () {
var globalVar = global;
global = undefined;
var url = parse('http://google.com/?foo=bar', true);
assume(url).to.be.an('object');
assume(url.pathname).to.be.a('string');
assume(url.host).to.be.a('string');
assume(url.hostname).to.be.a('string');
global = globalVar;
});
describe('trimLeft', function () {
it('is a function', function () {
assume(parse.trimLeft).is.a('function');
});
it('removes whitespace on the left', function () {
assume(parse.trimLeft(' lol')).equals('lol');
});
it('calls toString on a given value', function () {
//
// When users pass in `window.location` it's not an actual string
// so you can't replace on it. So it needs to be cast to a string.
//
var fake = {
toString: function () {
return 'wat';
}
};
assume(parse.trimLeft(fake)).equals('wat');
});
});
describe('extractProtocol', function () {
it('extracts the protocol data', function () {
assume(parse.extractProtocol('http://example.com')).eql({
slashes: true,
protocol: 'http:',
rest: 'example.com'
});
});
it('extracts the protocol data for nothing', function () {
assume(parse.extractProtocol('')).eql({
slashes: false,
protocol: '',
......@@ -47,6 +92,14 @@ describe('url-parse', function () {
rest: input
});
});
it('trimsLeft', function () {
assume(parse.extractProtocol(' javascript://foo')).eql({
slashes: true,
protocol: 'javascript:',
rest: 'foo'
});
});
});
it('parses the query string into an object', function () {
......@@ -179,6 +232,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'
......@@ -239,6 +314,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('google.com:8080');
assume(parsed.href).equals('http://google.com:8080/pathname');
});
});
describe('protocol', function () {
......@@ -455,6 +537,7 @@ describe('url-parse', function () {
var tests = [
['', 'http://foo.com', ''],
['', 'http://foo.com/', '/'],
['', 'http://foo.com/a', '/a'],
['a', 'http://foo.com', '/a'],
['a/', 'http://foo.com', '/a/'],
['b/c', 'http://foo.com/a', '/b/c'],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment