pax_global_header00006660000000000000000000000064124076245430014521gustar00rootroot0000000000000052 comment=0a9006a64de938d879f1e4b38b275c1c46524dfd proxy-addr-1.0.3/000077500000000000000000000000001240762454300136135ustar00rootroot00000000000000proxy-addr-1.0.3/.gitignore000066400000000000000000000000301240762454300155740ustar00rootroot00000000000000coverage/ node_modules/ proxy-addr-1.0.3/.travis.yml000066400000000000000000000006071240762454300157270ustar00rootroot00000000000000language: node_js node_js: - "0.6" - "0.8" - "0.10" - "0.11" matrix: allow_failures: - node_js: "0.11" fast_finish: true script: - "test $TRAVIS_NODE_VERSION != '0.6' || npm test" - "test $TRAVIS_NODE_VERSION = '0.6' || npm run-script test-travis" after_script: - "test $TRAVIS_NODE_VERSION = '0.10' && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" proxy-addr-1.0.3/HISTORY.md000066400000000000000000000014421240762454300152770ustar00rootroot000000000000001.0.3 / 2014-09-21 ================== * Use `forwarded` npm module 1.0.2 / 2014-09-18 ================== * Fix a global leak when multiple subnets are trusted * Support Node.js 0.6 * deps: ipaddr.js@0.1.3 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 proxy-addr-1.0.3/LICENSE000066400000000000000000000021011240762454300146120ustar00rootroot00000000000000(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. proxy-addr-1.0.3/README.md000066400000000000000000000076771240762454300151130ustar00rootroot00000000000000# proxy-addr [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Node.js Version][node-version-image]][node-version-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] 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) [npm-image]: https://img.shields.io/npm/v/proxy-addr.svg?style=flat [npm-url]: https://npmjs.org/package/proxy-addr [node-version-image]: https://img.shields.io/node/v/proxy-addr.svg?style=flat [node-version-url]: http://nodejs.org/download/ [travis-image]: https://img.shields.io/travis/jshttp/proxy-addr.svg?style=flat [travis-url]: https://travis-ci.org/jshttp/proxy-addr [coveralls-image]: https://img.shields.io/coveralls/jshttp/proxy-addr.svg?style=flat [coveralls-url]: https://coveralls.io/r/jshttp/proxy-addr?branch=master [downloads-image]: https://img.shields.io/npm/dm/proxy-addr.svg?style=flat [downloads-url]: https://npmjs.org/package/proxy-addr proxy-addr-1.0.3/benchmark/000077500000000000000000000000001240762454300155455ustar00rootroot00000000000000proxy-addr-1.0.3/benchmark/compiling.js000066400000000000000000000017141240762454300200670ustar00rootroot00000000000000 /** * 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 || '') } }; } proxy-addr-1.0.3/benchmark/index.js000066400000000000000000000012361240762454300172140ustar00rootroot00000000000000var 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); }); } proxy-addr-1.0.3/benchmark/kind.js000066400000000000000000000022251240762454300170310ustar00rootroot00000000000000 /** * 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 || '') } }; } proxy-addr-1.0.3/benchmark/matching.js000066400000000000000000000033451240762454300177020ustar00rootroot00000000000000 /** * 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 || '') } }; } proxy-addr-1.0.3/index.js000066400000000000000000000140151240762454300152610ustar00rootroot00000000000000/*! * 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 forwarded = require('forwarded'); 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) { // get addresses var addrs = forwarded(req); 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 === 'ipv6' ? 128 : 32; 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; } 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 subnetrange; 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; }; } proxy-addr-1.0.3/package.json000066400000000000000000000017571240762454300161130ustar00rootroot00000000000000{ "name": "proxy-addr", "description": "Determine address of proxied request", "version": "1.0.3", "author": "Douglas Christopher Wilson ", "license": "MIT", "keywords": [ "ip", "proxy", "x-forwarded-for" ], "repository": "jshttp/proxy-addr", "dependencies": { "forwarded": "~0.1.0", "ipaddr.js": "0.1.3" }, "devDependencies": { "benchmark": "1.0.0", "beautify-benchmark": "0.2.4", "istanbul": "0.3.2", "mocha": "~1.21.4", "should": "~4.0.0" }, "files": [ "LICENSE", "HISTORY.md", "README.md", "index.js" ], "engines": { "node": ">= 0.6" }, "scripts": { "bench": "node benchmark/index.js", "test": "mocha --reporter spec --bail --check-leaks test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" } } proxy-addr-1.0.3/test/000077500000000000000000000000001240762454300145725ustar00rootroot00000000000000proxy-addr-1.0.3/test/test.js000066400000000000000000000361611240762454300161160ustar00rootroot00000000000000 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/internet').should.throw(/invalid range on address/); 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); }