diff --git a/README.md b/README.md index b5dcd60..64136ff 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ $ npm i @fastify/proxy-addr const proxyaddr = require('@fastify/proxy-addr') ``` -### proxyaddr(req, trust) +### proxyaddr(req, trust, uds) Return the address of the request, using the given `trust` parameter. @@ -30,6 +30,9 @@ 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. +The optional `uds` (Unix domain socket) argument indicates whether +an undefined socket remote address should be treated as trusted. + ```js @@ -86,7 +89,7 @@ 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]) +### proxyaddr.all(req, [trust], [uds]) Return all the request addresses, optionally stopping at the first untrusted. This array is ordered from closest to furthest diff --git a/index.js b/index.js index b6ae18f..b5951f6 100644 --- a/index.js +++ b/index.js @@ -17,13 +17,13 @@ module.exports.default = proxyaddr module.exports.proxyaddr = proxyaddr module.exports.all = alladdrs module.exports.compile = compile +module.exports.forwarded = forwarded /** * Module dependencies. * @private */ -const forwarded = require('@fastify/forwarded') const ipaddr = require('ipaddr.js') /** @@ -46,18 +46,64 @@ const IP_RANGES = { uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'] } +/** + * Get all addresses in the `X-Forwarded-For` header. + * + * @param {string} header + * @public + */ + +function forwarded (header) { + if (!header || typeof header !== 'string') { + return [] + } + + header = header.trim() + + // Based on https://github.com/fastify/forwarded + // a fork of https://github.com/jshttp/forwarded + const result = [] + let end = header.length + let start = end + let char + let i + + for (i = end - 1; i >= 0; --i) { + char = header[i] + if (char === ' ' || char === '\t') { + (start === end) && (start = end = i) + } else if (char === ',') { + (start !== end) && result.push(header.slice(start, end)) + start = end = i + } else { + start = i + } + } + + (start !== end) && result.push(header.substring(start, end)) + + return result +} + /** * Get all addresses in the request, optionally stopping * at the first untrusted. * * @param {Object} request * @param {Function|Array|String} [trust] + * @param {Boolean} [uds] * @public */ -function alladdrs (req, trust) { +function alladdrs (req, trust, uds) { + if (!req) { + throw new TypeError('argument req is required') + } + // get addresses - const addrs = forwarded(req) + const socketAddr = req.socket.remoteAddress + const header = forwarded(req.headers['x-forwarded-for']) + const addrs = uds && !socketAddr ? header : [socketAddr, ...header] if (!trust) { // Return all addresses @@ -219,10 +265,11 @@ function parseNetmask (netmask) { * * @param {Object} request * @param {Function|Array|String} trust + * @param {Boolean} [uds] * @public */ -function proxyaddr (req, trust) { +function proxyaddr (req, trust, uds) { if (!req) { throw new TypeError('req argument is required') } @@ -231,7 +278,7 @@ function proxyaddr (req, trust) { throw new TypeError('trust argument is required') } - const addrs = alladdrs(req, trust) + const addrs = alladdrs(req, trust, uds) return addrs[addrs.length - 1] } diff --git a/package.json b/package.json index 13bd704..46d0634 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ } ], "dependencies": { - "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" }, "devDependencies": { diff --git a/test/base.test.js b/test/base.test.js index f0c7d6c..a9d9cbb 100644 --- a/test/base.test.js +++ b/test/base.test.js @@ -386,6 +386,13 @@ test('when socket address undefined should return undefined even wtesth trusted t.assert.strictEqual(proxyaddr(req, '127.0.0.1'), undefined) }) +test('when socket address undefined and uds set should skip socket', function (t) { + const req = createReq(undefined, { + 'x-forwarded-for': '127.0.0.1, 10.0.0.1' + }) + t.assert.strictEqual(proxyaddr(req, '127.0.0.1', true), '10.0.0.1') +}) + function createReq (socketAddr, headers) { return { socket: { diff --git a/test/forwarded.test.js b/test/forwarded.test.js new file mode 100644 index 0000000..a460b63 --- /dev/null +++ b/test/forwarded.test.js @@ -0,0 +1,44 @@ +'use strict' + +const { test } = require('node:test') +const proxyaddr = require('..') + +test('with no header should return empty array', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded(), []) +}) + +test('with no x-forwarded-for header should return empty array', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded(), []) +}) + +test('with empty x-forwarded-for header should return empty array', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded(' '), []) +}) + +test('with single x-forwarded-for header should return array with that address', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded('10.0.0.1'), ['10.0.0.1']) +}) + +test('with multiple x-forwarded-for header should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded('10.0.0.2, 10.0.0.1'), ['10.0.0.1', '10.0.0.2']) +}) + +test('with multiple including empty first x-forwarded-for header should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded(', 10.0.0.2, 10.0.0.1'), ['10.0.0.1', '10.0.0.2']) +}) + +test('with multiple including empty middle x-forwarded-for header should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded('10.0.0.2, , 10.0.0.1'), ['10.0.0.1', '10.0.0.2']) +}) + +test('with multiple including empty last x-forwarded-for header should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded('10.0.0.2, 10.0.0.1,'), ['10.0.0.1', '10.0.0.2']) +}) + +test('with multiple x-forwarded-for header with spaces should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded(' 10.0.0.2 , 10.0.0.1 '), ['10.0.0.1', '10.0.0.2']) +}) + +test('with multiple x-forwarded-for header with tabs should return array with those addresses in order', function (t) { + t.assert.deepStrictEqual(proxyaddr.forwarded('\t10.0.0.2\t,\t10.0.0.1\t'), ['10.0.0.1', '10.0.0.2']) +})