[Pkg-javascript-commits] [node-proxy-addr] 01/02: Imported Upstream version 1.0.1

Leo Iannacone l3on-guest at moszumanska.debian.org
Sat Jul 5 10:40:54 UTC 2014


This is an automated email from the git hooks/post-receive script.

l3on-guest pushed a commit to branch master
in repository node-proxy-addr.

commit d2fcf4a43dab1052be27646d2382388521bb95f9
Author: Leo Iannacone <l3on at ubuntu.com>
Date:   Sat Jul 5 12:20:24 2014 +0200

    Imported Upstream version 1.0.1
---
 .npmignore             |   4 +
 .travis.yml            |  11 ++
 History.md             |  27 +++
 LICENSE                |  22 +++
 README.md              | 124 +++++++++++++
 benchmark/compiling.js |  49 ++++++
 benchmark/index.js     |  28 +++
 benchmark/kind.js      |  56 ++++++
 benchmark/matching.js  |  77 ++++++++
 index.js               | 353 +++++++++++++++++++++++++++++++++++++
 package.json           |  38 ++++
 test/test.js           | 465 +++++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 1254 insertions(+)

diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..85c82a5
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+benchmark/
+coverage/
+test/
+.travis.yml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1ff243c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+node_js:
+  - "0.8"
+  - "0.10"
+  - "0.11"
+matrix:
+  allow_failures:
+    - node_js: "0.11"
+  fast_finish: true
+script: "npm run-script test-travis"
+after_script: "npm install coveralls at 2.10.0 && cat ./coverage/lcov.info | coveralls"
diff --git a/History.md b/History.md
new file mode 100644
index 0000000..61a0d50
--- /dev/null
+++ b/History.md
@@ -0,0 +1,27 @@
+1.0.1 / 2014-06-03
+==================
+
+  * Fix links in npm package
+
+1.0.0 / 2014-05-08
+==================
+
+  * Add `trust` argument to determine proxy trust on
+    * Accepts custom function
+    * Accepts IPv4/IPv6 address(es)
+    * Accepts subnets
+    * Accepts pre-defined names
+  * Add optional `trust` argument to `proxyaddr.all` to
+    stop at first untrusted
+  * Add `proxyaddr.compile` to pre-compile `trust` function
+    to make subsequent calls faster
+
+0.0.1 / 2014-05-04
+==================
+
+  * Fix bad npm publish
+
+0.0.0 / 2014-05-04
+==================
+
+  * Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b7dce6c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2014 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4fe57d3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,124 @@
+# proxy-addr
+
+[![NPM version](https://badge.fury.io/js/proxy-addr.svg)](http://badge.fury.io/js/proxy-addr)
+[![Build Status](https://travis-ci.org/expressjs/proxy-addr.svg?branch=master)](https://travis-ci.org/expressjs/proxy-addr)
+[![Coverage Status](https://img.shields.io/coveralls/expressjs/proxy-addr.svg?branch=master)](https://coveralls.io/r/expressjs/proxy-addr)
+
+Determine address of proxied request
+
+## Install
+
+```sh
+$ npm install proxy-addr
+```
+
+## API
+
+```js
+var proxyaddr = require('proxy-addr')
+```
+
+### proxyaddr(req, trust)
+
+Return the address of the request, using the given `trust` parameter.
+
+The `trust` argument is a function that returns `true` if you trust
+the address, `false` if you don't. The closest untrusted address is
+returned.
+
+```js
+proxyaddr(req, function(addr){ return addr === '127.0.0.1' })
+proxyaddr(req, function(addr, i){ return i < 1 })
+```
+
+The `trust` arugment may also be a single IP address string or an
+array of trusted addresses, as plain IP addresses, CIDR-formatted
+strings, or IP/netmask strings.
+
+```js
+proxyaddr(req, '127.0.0.1')
+proxyaddr(req, ['127.0.0.0/8', '10.0.0.0/8'])
+proxyaddr(req, ['127.0.0.0/255.0.0.0', '192.168.0.0/255.255.0.0'])
+```
+
+This module also supports IPv6. Your IPv6 addresses will be normalized
+automatically (i.e. `fe80::00ed:1` equals `fe80:0:0:0:0:0:ed:1`).
+
+```js
+proxyaddr(req, '::1')
+proxyaddr(req, ['::1/128', 'fe80::/10'])
+proxyaddr(req, ['fe80::/ffc0::'])
+```
+
+This module will automatically work with IPv4-mapped IPv6 addresses
+as well to support node.js in IPv6-only mode. This means that you do
+not have to specify both `::ffff:a00:1` and `10.0.0.1`.
+
+As a convenience, this module also takes certain pre-defined names
+in addition to IP addresses, which expand into IP addresses:
+
+```js
+proxyaddr(req, 'loopback')
+proxyaddr(req, ['loopback', 'fc00:ac:1ab5:fff::1/64'])
+```
+
+  * `loopback`: IPv4 and IPv6 loopback addresses (like `::1` and
+    `127.0.0.1`).
+  * `linklocal`: IPv4 and IPv6 link-local addresses (like
+    `fe80::1:1:1:1` and `169.254.0.1`).
+  * `uniquelocal`: IPv4 private addresses and IPv6 unique-local
+    addresses (like `fc00:ac:1ab5:fff::1` and `192.168.0.1`).
+
+When `trust` is specified as a function, it will be called for each
+address to determine if it is a trusted address. The function is
+given two arguments: `addr` and `i`, where `addr` is a string of
+the address to check and `i` is a number that represents the distance
+from the socket address.
+
+### proxyaddr.all(req, [trust])
+
+Return all the addresses of the request, optionally stopping at the
+first untrusted. This array is ordered from closest to furthest
+(i.e. `arr[0] === req.connection.remoteAddress`).
+
+```js
+proxyaddr.all(req)
+```
+
+The optional `trust` argument takes the same arguments as `trust`
+does in `proxyaddr(req, trust)`.
+
+```js
+proxyaddr.all(req, 'loopback')
+```
+
+### proxyaddr.compile(val)
+
+Compiles argument `val` into a `trust` function. This function takes
+the same arguments as `trust` does in `proxyaddr(req, trust)` and
+returns a function suitable for `proxyaddr(req, trust)`.
+
+```js
+var trust = proxyaddr.compile('localhost')
+var addr  = proxyaddr(req, trust)
+```
+
+This function is meant to be optimized for use against every request.
+It is recommend to compile a trust function up-front for the trusted
+configuration and pass that to `proxyaddr(req, trust)` for each request.
+
+## Testing
+
+```sh
+$ npm test
+```
+
+## Benchmarks
+
+```sh
+$ npm run-script bench
+```
+
+## License
+
+[MIT](LICENSE)
diff --git a/benchmark/compiling.js b/benchmark/compiling.js
new file mode 100644
index 0000000..11d78d7
--- /dev/null
+++ b/benchmark/compiling.js
@@ -0,0 +1,49 @@
+
+/**
+ * Globals for benchmark.js
+ */
+global.proxyaddr = require('..');
+global.createReq = createReq;
+
+/**
+ * Module dependencies.
+ */
+var benchmark = require('benchmark');
+var benchmarks = require('beautify-benchmark');
+
+var suite = new benchmark.Suite;
+
+suite.add({
+  'name': 're-compiling',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, "loopback")',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1")'
+});
+
+suite.add({
+  'name': 'pre-compiling',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("loopback")'
+});
+
+suite.on('cycle', function onCycle(event) {
+  benchmarks.add(event.target);
+});
+
+suite.on('complete', function onComplete() {
+  benchmarks.log();
+});
+
+suite.run({'async': false});
+
+function createReq(socketAddr, forwardedFor) {
+  return {
+    connection: {
+      remoteAddress: socketAddr
+    },
+    headers: {
+      'x-forwarded-for': (forwardedFor || '')
+    }
+  };
+}
diff --git a/benchmark/index.js b/benchmark/index.js
new file mode 100644
index 0000000..2fbd813
--- /dev/null
+++ b/benchmark/index.js
@@ -0,0 +1,28 @@
+var fs = require('fs');
+var path = require('path');
+var spawn = require('child_process').spawn;
+
+var exe = process.argv[0];
+var cwd = process.cwd();
+
+runScripts(fs.readdirSync(__dirname));
+
+function runScripts(fileNames) {
+  var fileName = fileNames.shift();
+
+  if (!fileName) return;
+  if (!/\.js$/i.test(fileName)) return runScripts(fileNames);
+  if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames);
+
+  var fullPath = path.join(__dirname, fileName);
+
+  console.log('> %s %s', exe, path.relative(cwd, fullPath));
+
+  var proc = spawn(exe, [fullPath], {
+    'stdio': 'inherit'
+  });
+
+  proc.on('exit', function () {
+    runScripts(fileNames);
+  });
+}
diff --git a/benchmark/kind.js b/benchmark/kind.js
new file mode 100644
index 0000000..d249878
--- /dev/null
+++ b/benchmark/kind.js
@@ -0,0 +1,56 @@
+
+/**
+ * Globals for benchmark.js
+ */
+global.proxyaddr = require('..');
+global.createReq = createReq;
+
+/**
+ * Module dependencies.
+ */
+var benchmark = require('benchmark');
+var benchmarks = require('beautify-benchmark');
+
+var suite = new benchmark.Suite;
+
+suite.add({
+  'name': 'ipv4',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")'
+});
+
+suite.add({
+  'name': 'ipv4-mapped',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("::ffff:7f00:1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")'
+});
+
+suite.add({
+  'name': 'ipv6',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("::1", "10.0.0.1"); trust = proxyaddr.compile("::1")'
+});
+
+suite.on('cycle', function onCycle(event) {
+  benchmarks.add(event.target);
+});
+
+suite.on('complete', function onComplete() {
+  benchmarks.log();
+});
+
+suite.run({'async': false});
+
+function createReq(socketAddr, forwardedFor) {
+  return {
+    connection: {
+      remoteAddress: socketAddr
+    },
+    headers: {
+      'x-forwarded-for': (forwardedFor || '')
+    }
+  };
+}
diff --git a/benchmark/matching.js b/benchmark/matching.js
new file mode 100644
index 0000000..abcda04
--- /dev/null
+++ b/benchmark/matching.js
@@ -0,0 +1,77 @@
+
+/**
+ * Globals for benchmark.js
+ */
+global.proxyaddr = require('..');
+global.createReq = createReq;
+
+/**
+ * Module dependencies.
+ */
+var benchmark = require('benchmark');
+var benchmarks = require('beautify-benchmark');
+
+var suite = new benchmark.Suite;
+
+suite.add({
+  'name': 'trust none',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile([])'
+});
+
+suite.add({
+  'name': 'trust all',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = function() {return true}'
+});
+
+suite.add({
+  'name': 'trust single',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1")'
+});
+
+suite.add({
+  'name': 'trust first',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = function(a, i) {return i<1}'
+});
+
+suite.add({
+  'name': 'trust subnet',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile("127.0.0.1/8")'
+});
+
+suite.add({
+  'name': 'trust multiple',
+  'minSamples': 100,
+  'fn': 'proxyaddr(req, trust)',
+  'setup': 'req = createReq("127.0.0.1", "10.0.0.1"); trust = proxyaddr.compile(["127.0.0.1", "10.0.0.1"])'
+});
+
+suite.on('cycle', function onCycle(event) {
+  benchmarks.add(event.target);
+});
+
+suite.on('complete', function onComplete() {
+  benchmarks.log();
+});
+
+suite.run({'async': false});
+
+function createReq(socketAddr, forwardedFor) {
+  return {
+    connection: {
+      remoteAddress: socketAddr
+    },
+    headers: {
+      'x-forwarded-for': (forwardedFor || '')
+    }
+  };
+}
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..75ee432
--- /dev/null
+++ b/index.js
@@ -0,0 +1,353 @@
+/*!
+ * proxy-addr
+ * Copyright(c) 2014 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+/**
+ * Module exports.
+ */
+
+module.exports = proxyaddr;
+module.exports.all = alladdrs;
+module.exports.compile = compile;
+
+/**
+ * Module dependencies.
+ */
+
+var ipaddr = require('ipaddr.js');
+
+/**
+ * Variables.
+ */
+
+var digitre = /^[0-9]+$/;
+var isip = ipaddr.isValid;
+var parseip = ipaddr.parse;
+
+/**
+ * Pre-defined IP ranges.
+ */
+
+var ipranges = {
+  linklocal: ['169.254.0.0/16', 'fe80::/10'],
+  loopback: ['127.0.0.1/8', '::1/128'],
+  uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7']
+};
+
+/**
+ * Get all addresses in the request, optionally stopping
+ * at the first untrusted.
+ *
+ * @param {Object} request
+ * @param {Function|Array|String} [trust]
+ * @api public
+ */
+
+function alladdrs(req, trust) {
+  if (!req) {
+    throw new TypeError('req argument is required');
+  }
+
+  var proxyAddrs = (req.headers['x-forwarded-for'] || '')
+    .split(/ *, */)
+    .filter(Boolean)
+    .reverse();
+  var socketAddr = req.connection.remoteAddress;
+  var addrs = [socketAddr].concat(proxyAddrs);
+
+  if (!trust) {
+    // Return all addresses
+    return addrs;
+  }
+
+  if (typeof trust !== 'function') {
+    trust = compile(trust);
+  }
+
+  for (var i = 0; i < addrs.length - 1; i++) {
+    if (trust(addrs[i], i)) continue;
+
+    addrs.length = i + 1;
+  }
+
+  return addrs;
+}
+
+/**
+ * Compile argument into trust function.
+ *
+ * @param {Array|String} val
+ * @api private
+ */
+
+function compile(val) {
+  if (!val) {
+    throw new TypeError('argument is required');
+  }
+
+  var trust = typeof val === 'string'
+    ? [val]
+    : val;
+
+  if (!Array.isArray(trust)) {
+    throw new TypeError('unsupported trust argument');
+  }
+
+  for (var i = 0; i < trust.length; i++) {
+    val = trust[i];
+
+    if (!ipranges.hasOwnProperty(val)) {
+      continue;
+    }
+
+    // Splice in pre-defined range
+    val = ipranges[val];
+    trust.splice.apply(trust, [i, 1].concat(val));
+    i += val.length - 1;
+  }
+
+  return compileTrust(compileRangeSubnets(trust));
+}
+
+/**
+ * Compile `arr` elements into range subnets.
+ *
+ * @param {Array} arr
+ * @api private
+ */
+
+function compileRangeSubnets(arr) {
+  var rangeSubnets = new Array(arr.length);
+
+  for (var i = 0; i < arr.length; i++) {
+    rangeSubnets[i] = parseipNotation(arr[i]);
+  }
+
+  return rangeSubnets;
+}
+
+/**
+ * Compile range subnet array into trust function.
+ *
+ * @param {Array} rangeSubnets
+ * @api private
+ */
+
+function compileTrust(rangeSubnets) {
+  // Return optimized function based on length
+  var len = rangeSubnets.length;
+  return len === 0
+    ? trustNone
+    : len === 1
+    ? trustSingle(rangeSubnets[0])
+    : trustMulti(rangeSubnets);
+}
+
+/**
+ * Parse IP notation string into range subnet.
+ *
+ * @param {String} note
+ * @api private
+ */
+
+function parseipNotation(note) {
+  var ip;
+  var kind;
+  var max;
+  var pos = note.lastIndexOf('/');
+  var range;
+
+  ip = pos !== -1
+    ? note.substring(0, pos)
+    : note;
+
+  if (!isip(ip)) {
+    throw new TypeError('invalid IP address: ' + ip);
+  }
+
+  ip = parseip(ip);
+
+  kind = ip.kind();
+  max = kind === 'ipv4' ? 32
+    : kind === 'ipv6' ? 128
+    : 0;
+
+  range = pos !== -1
+    ? note.substring(pos + 1, note.length)
+    : max;
+
+  if (typeof range !== 'number') {
+    range = digitre.test(range)
+      ? parseInt(range, 10)
+      : isip(range)
+      ? parseNetmask(range)
+      : 0;
+  }
+
+  if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
+    // Store as IPv4
+    ip = ip.toIPv4Address();
+    range = range <= max
+      ? range - 96
+      : range;
+  }
+
+  if (range <= 0 || range > max) {
+    throw new TypeError('invalid range on address: ' + note);
+  }
+
+  return [ip, range];
+}
+
+/**
+ * Parse netmask string into CIDR range.
+ *
+ * @param {String} note
+ * @api private
+ */
+
+function parseNetmask(netmask) {
+  var ip = parseip(netmask);
+  var parts;
+  var size;
+
+  switch (ip.kind()) {
+    case 'ipv4':
+      parts = ip.octets;
+      size = 8;
+      break;
+    case 'ipv6':
+      parts = ip.parts;
+      size = 16;
+      break;
+    default:
+      throw new TypeError('unknown netmask');
+  }
+
+  var max = Math.pow(2, size) - 1;
+  var part;
+  var range = 0;
+
+  for (var i = 0; i < parts.length; i++) {
+    part = parts[i] & max;
+
+    if (part === max) {
+      range += size;
+      continue;
+    }
+
+    while (part) {
+      part = (part << 1) & max;
+      range += 1;
+    }
+
+    break;
+  }
+
+  return range;
+}
+
+/**
+ * Determine address of proxied request.
+ *
+ * @param {Object} request
+ * @param {Function|Array|String} trust
+ * @api public
+ */
+
+function proxyaddr(req, trust) {
+  if (!req) {
+    throw new TypeError('req argument is required');
+  }
+
+  if (!trust) {
+    throw new TypeError('trust argument is required');
+  }
+
+  var addrs = alladdrs(req, trust);
+  var addr = addrs[addrs.length - 1];
+
+  return addr;
+}
+
+/**
+ * Static trust function to trust nothing.
+ *
+ * @api private
+ */
+
+function trustNone() {
+  return false;
+}
+
+/**
+ * Compile trust function for multiple subnets.
+ *
+ * @param {Array} subnets
+ * @api private
+ */
+
+function trustMulti(subnets) {
+  return function trust(addr) {
+    if (!isip(addr)) return false;
+
+    var ip = parseip(addr);
+    var ipv4;
+    var kind = ip.kind();
+    var subnet;
+    var subnetip;
+    var subnetkind;
+    var trusted;
+
+    for (var i = 0; i < subnets.length; i++) {
+      subnet = subnets[i];
+      subnetip = subnet[0];
+      subnetkind = subnetip.kind();
+      subnetrange = subnet[1];
+      trusted = ip;
+
+      if (kind !== subnetkind) {
+        if (kind !== 'ipv6' || subnetkind !== 'ipv4' || !ip.isIPv4MappedAddress()) {
+          continue;
+        }
+
+        // Store addr as IPv4
+        ipv4 = ipv4 || ip.toIPv4Address();
+        trusted = ipv4;
+      }
+
+      if (trusted.match(subnetip, subnetrange)) return true;
+    }
+
+    return false;
+  };
+}
+
+/**
+ * Compile trust function for single subnet.
+ *
+ * @param {Object} subnet
+ * @api private
+ */
+
+function trustSingle(subnet) {
+  var subnetip = subnet[0];
+  var subnetkind = subnetip.kind();
+  var subnetisipv4 = subnetkind === 'ipv4';
+  var subnetrange = subnet[1];
+
+  return function trust(addr) {
+    if (!isip(addr)) return false;
+
+    var ip = parseip(addr);
+    var kind = ip.kind();
+
+    return kind === subnetkind
+      ? ip.match(subnetip, subnetrange)
+      : subnetisipv4 && kind === 'ipv6' && ip.isIPv4MappedAddress()
+      ? ip.toIPv4Address().match(subnetip, subnetrange)
+      : false;
+  };
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2f6bcaa
--- /dev/null
+++ b/package.json
@@ -0,0 +1,38 @@
+{
+  "name": "proxy-addr",
+  "description": "Determine address of proxied request",
+  "version": "1.0.1",
+  "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+  "license": "MIT",
+  "keywords": [
+    "ip",
+    "proxy",
+    "x-forwarded-for"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/expressjs/proxy-addr.git"
+  },
+  "bugs": {
+    "url": "https://github.com/expressjs/proxy-addr/issues"
+  },
+  "dependencies": {
+    "ipaddr.js": "0.1.2"
+  },
+  "devDependencies": {
+    "benchmark": "1.0.0",
+    "beautify-benchmark": "0.2.4",
+    "istanbul": "0.2.10",
+    "mocha": "~1.20.0",
+    "should": "~4.0.0"
+  },
+  "engines": {
+    "node": ">= 0.8.0"
+  },
+  "scripts": {
+    "bench": "node benchmark/index.js",
+    "test": "mocha --reporter dot test/",
+    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/",
+    "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec test/"
+  }
+}
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..1b05b90
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,465 @@
+
+var proxyaddr = require('..');
+var should = require('should');
+
+describe('proxyaddr(req, trust)', function () {
+  describe('arguments', function () {
+    describe('req', function () {
+      it('should be required', function () {
+        proxyaddr.bind().should.throw(/req.*required/);
+      });
+    });
+
+    describe('trust', function () {
+      it('should be required', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req).should.throw(/trust.*required/);
+      });
+
+      it('should accept a function', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, all).should.not.throw();
+      });
+
+      it('should accept an array', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, []).should.not.throw();
+      });
+
+      it('should accept a string', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, '127.0.0.1').should.not.throw();
+      });
+
+      it('should reject a number', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, 42).should.throw(/unsupported trust argument/);
+      });
+
+      it('should accept IPv4', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, '127.0.0.1').should.not.throw();
+      });
+
+      it('should accept IPv6', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, '::1').should.not.throw();
+      });
+
+      it('should accept IPv4-style IPv6', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, '::ffff:127.0.0.1').should.not.throw();
+      });
+
+      it('should accept pre-defined names', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, 'loopback').should.not.throw();
+      });
+
+      it('should accept pre-defined names in array', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, ['loopback', '10.0.0.1']).should.not.throw();
+      });
+
+      it('should reject non-IP', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, 'blargh').should.throw(/invalid IP address/);
+        proxyaddr.bind(null, req, '10.0.300.1/16').should.throw(/invalid IP address/);
+      });
+
+      it('should reject bad CIDR', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.bind(null, req, '10.0.0.1/6000').should.throw(/invalid range on address/);
+        proxyaddr.bind(null, req, '::1/6000').should.throw(/invalid range on address/);
+        proxyaddr.bind(null, req, '::ffff:a00:2/136').should.throw(/invalid range on address/);
+        proxyaddr.bind(null, req, '::ffff:a00:2/46').should.throw(/invalid range on address/);
+      });
+
+      it('should be invoked as trust(addr, i)', function () {
+        var log = [];
+        var req = createReq('127.0.0.1', {
+          'x-forwarded-for': '192.168.0.1, 10.0.0.1'
+        });
+
+        proxyaddr(req, function (addr, i) {
+          return log.push(Array.prototype.slice.call(arguments));
+        });
+
+        log.should.eql([
+          ['127.0.0.1', 0],
+          ['10.0.0.1', 1]
+        ]);
+      });
+    });
+  });
+
+  describe('with all trusted', function () {
+    it('should return socket address with no headers', function () {
+      var req = createReq('127.0.0.1');
+      proxyaddr(req, all).should.equal('127.0.0.1');
+    });
+
+    it('should return header value', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1'
+      });
+      proxyaddr(req, all).should.equal('10.0.0.1');
+    });
+
+    it('should return furthest header value', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, all).should.equal('10.0.0.1');
+    });
+  });
+
+  describe('with none trusted', function () {
+    it('should return socket address with no headers', function () {
+      var req = createReq('127.0.0.1');
+      proxyaddr(req, none).should.equal('127.0.0.1');
+    });
+
+    it('should return socket address with headers', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, none).should.equal('127.0.0.1');
+    });
+  });
+
+  describe('with some trusted', function () {
+    it('should return socket address with no headers', function () {
+      var req = createReq('127.0.0.1');
+      proxyaddr(req, trust10x).should.equal('127.0.0.1');
+    });
+
+    it('should return socket address when not trusted', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, trust10x).should.equal('127.0.0.1');
+    });
+
+    it('should return header when socket trusted', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1'
+      });
+      proxyaddr(req, trust10x).should.equal('192.168.0.1');
+    });
+
+    it('should return first untrusted after trusted', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, trust10x).should.equal('192.168.0.1');
+    });
+
+    it('should not skip untrusted', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '10.0.0.3, 192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, trust10x).should.equal('192.168.0.1');
+    });
+  });
+
+  describe('when given array', function () {
+    it('should accept literal IP addresses', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['10.0.0.1', '10.0.0.2']).should.equal('192.168.0.1');
+    });
+
+    it('should not trust non-IP addresses', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2, localhost'
+      });
+      proxyaddr(req, ['10.0.0.1', '10.0.0.2']).should.equal('localhost');
+    });
+
+    it('should return socket address if none match', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['127.0.0.1', '192.168.0.100']).should.equal('10.0.0.1');
+    });
+
+    describe('when array empty', function () {
+      it('should return socket address ', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr(req, []).should.equal('127.0.0.1');
+      });
+
+      it('should return socket address with headers', function () {
+        var req = createReq('127.0.0.1', {
+          'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+        });
+        proxyaddr(req, []).should.equal('127.0.0.1');
+      });
+    });
+  });
+
+  describe('when given IPv4 addresses', function () {
+    it('should accept literal IP addresses', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['10.0.0.1', '10.0.0.2']).should.equal('192.168.0.1');
+    });
+
+    it('should accept CIDR notation', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.200'
+      });
+      proxyaddr(req, '10.0.0.2/26').should.equal('10.0.0.200');
+    });
+
+    it('should accept netmask notation', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.200'
+      });
+      proxyaddr(req, '10.0.0.2/255.255.255.192').should.equal('10.0.0.200');
+    });
+  });
+
+  describe('when given IPv6 addresses', function () {
+    it('should accept literal IP addresses', function () {
+      var req = createReq('fe80::1', {
+        'x-forwarded-for': '2002:c000:203::1, fe80::2'
+      });
+      proxyaddr(req, ['fe80::1', 'fe80::2']).should.equal('2002:c000:203::1');
+    });
+
+    it('should accept CIDR notation', function () {
+      var req = createReq('fe80::1', {
+        'x-forwarded-for': '2002:c000:203::1, fe80::ff00'
+      });
+      proxyaddr(req, 'fe80::/125').should.equal('fe80::ff00');
+    });
+
+    it('should accept netmask notation', function () {
+      var req = createReq('fe80::1', {
+        'x-forwarded-for': '2002:c000:203::1, fe80::ff00'
+      });
+      proxyaddr(req, 'fe80::/ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8').should.equal('fe80::ff00');
+    });
+  });
+
+  describe('when IP versions mixed', function () {
+    it('should match respective versions', function () {
+      var req = createReq('::1', {
+        'x-forwarded-for': '2002:c000:203::1'
+      });
+      proxyaddr(req, ['127.0.0.1', '::1']).should.equal('2002:c000:203::1');
+    });
+
+    it('should not match IPv4 to IPv6', function () {
+      var req = createReq('::1', {
+        'x-forwarded-for': '2002:c000:203::1'
+      });
+      proxyaddr(req, '127.0.0.1').should.equal('::1');
+    });
+  });
+
+  describe('when IPv4-mapped IPv6 addresses', function () {
+    it('should match IPv4 trust to IPv6 request', function () {
+      var req = createReq('::ffff:a00:1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['10.0.0.1', '10.0.0.2']).should.equal('192.168.0.1');
+    });
+
+    it('should match IPv4 netmask trust to IPv6 request', function () {
+      var req = createReq('::ffff:a00:1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['10.0.0.1/16']).should.equal('192.168.0.1');
+    });
+
+    it('should match IPv6 trust to IPv4 request', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.2'
+      });
+      proxyaddr(req, ['::ffff:a00:1', '::ffff:a00:2']).should.equal('192.168.0.1');
+    });
+
+    it('should match CIDR notation for IPv4-mapped address', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.200'
+      });
+      proxyaddr(req, '::ffff:a00:2/122').should.equal('10.0.0.200');
+    });
+
+    it('should match subnet notation for IPv4-mapped address', function () {
+      var req = createReq('10.0.0.1', {
+        'x-forwarded-for': '192.168.0.1, 10.0.0.200'
+      });
+      proxyaddr(req, '::ffff:a00:2/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0').should.equal('10.0.0.200');
+    });
+  });
+
+  describe('when given pre-defined names', function () {
+    it('should accept single pre-defined name', function () {
+      var req = createReq('fe80::1', {
+        'x-forwarded-for': '2002:c000:203::1, fe80::2'
+      });
+      proxyaddr(req, 'linklocal').should.equal('2002:c000:203::1');
+    });
+
+    it('should accept multiple pre-defined names', function () {
+      var req = createReq('::1', {
+        'x-forwarded-for': '2002:c000:203::1, fe80::2'
+      });
+      proxyaddr(req, ['loopback', 'linklocal']).should.equal('2002:c000:203::1');
+    });
+  });
+
+  describe('when header contains non-ip addresses', function () {
+    it('should stop at first non-ip after trusted', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': 'myrouter, 127.0.0.1, proxy'
+      });
+      proxyaddr(req, '127.0.0.1').should.equal('proxy');
+    });
+
+    it('should provide all values to function', function () {
+      var log = [];
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': 'myrouter, 127.0.0.1, proxy'
+      });
+
+      proxyaddr(req, function (addr, i) {
+        return log.push(Array.prototype.slice.call(arguments));
+      });
+
+      log.should.eql([
+        ['127.0.0.1', 0],
+        ['proxy', 1],
+        ['127.0.0.1', 2]
+      ]);
+    });
+  });
+});
+
+describe('proxyaddr.all(req, [trust])', function () {
+  describe('arguments', function () {
+    describe('req', function () {
+      it('should be required', function () {
+        proxyaddr.all.bind().should.throw(/req.*required/);
+      });
+    });
+
+    describe('trust', function () {
+      it('should be optional', function () {
+        var req = createReq('127.0.0.1');
+        proxyaddr.all.bind(null, req).should.not.throw();
+      });
+    });
+  });
+
+  describe('with no headers', function () {
+    it('should return socket address', function () {
+      var req = createReq('127.0.0.1');
+      proxyaddr.all(req).should.eql(['127.0.0.1']);
+    });
+  });
+
+  describe('with x-forwarded-for header', function () {
+    it('should include x-forwarded-for', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1'
+      });
+      proxyaddr.all(req).should.eql(['127.0.0.1', '10.0.0.1']);
+    });
+
+    it('should include x-forwarded-for in correct order', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr.all(req).should.eql(['127.0.0.1', '10.0.0.2', '10.0.0.1']);
+    });
+  });
+
+  describe('with trust argument', function () {
+    it('should stop at first untrusted', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr.all(req, '127.0.0.1').should.eql(['127.0.0.1', '10.0.0.2']);
+    });
+
+    it('should be only socket address for no trust', function () {
+      var req = createReq('127.0.0.1', {
+        'x-forwarded-for': '10.0.0.1, 10.0.0.2'
+      });
+      proxyaddr.all(req, []).should.eql(['127.0.0.1']);
+    });
+  });
+});
+
+describe('proxyaddr.compile(trust)', function () {
+  describe('arguments', function () {
+    describe('trust', function () {
+      it('should be required', function () {
+        proxyaddr.compile.bind(null).should.throw(/argument.*required/);
+      });
+
+      it('should accept an array', function () {
+        proxyaddr.compile([]).should.be.function;
+      });
+
+      it('should accept a string', function () {
+        proxyaddr.compile('127.0.0.1').should.be.function;
+      });
+
+      it('should reject a number', function () {
+        proxyaddr.compile.bind(null, 42).should.throw(/unsupported trust argument/);
+      });
+
+      it('should accept IPv4', function () {
+        proxyaddr.compile('127.0.0.1').should.be.function;
+      });
+
+      it('should accept IPv6', function () {
+        proxyaddr.compile('::1').should.be.function;
+      });
+
+      it('should accept IPv4-style IPv6', function () {
+        proxyaddr.compile('::ffff:127.0.0.1').should.be.function;
+      });
+
+      it('should accept pre-defined names', function () {
+        proxyaddr.compile('loopback').should.be.function;
+      });
+
+      it('should accept pre-defined names in array', function () {
+        proxyaddr.compile(['loopback', '10.0.0.1']).should.be.function;
+      });
+
+      it('should reject non-IP', function () {
+        proxyaddr.compile.bind(null, 'blargh').should.throw(/invalid IP address/);
+      });
+
+      it('should reject bad CIDR', function () {
+        proxyaddr.compile.bind(null, '10.0.0.1/6000').should.throw(/invalid range on address/);
+        proxyaddr.compile.bind(null, '::1/6000').should.throw(/invalid range on address/);
+        proxyaddr.compile.bind(null, '::ffff:a00:2/136').should.throw(/invalid range on address/);
+        proxyaddr.compile.bind(null, '::ffff:a00:2/46').should.throw(/invalid range on address/);
+      });
+    });
+  });
+});
+
+function createReq(socketAddr, headers) {
+  return {
+    connection: {
+      remoteAddress: socketAddr
+    },
+    headers: headers || {}
+  };
+}
+
+function all() { return true; }
+function none() { return false; }
+function trust10x(addr) { return /^10\./.test(addr); }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-proxy-addr.git



More information about the Pkg-javascript-commits mailing list