From 936133ad42e0730ac278ad383e9a0b238b74419a Mon Sep 17 00:00:00 2001 From: Alec Fenichel Date: Sat, 10 Oct 2026 11:39:48 -0400 Subject: [PATCH 1/4] Add optional support for unix domain sockets Signed-off-by: Alec Fenichel --- README.md | 7 ++++-- index.js | 55 ++++++++++++++++++++++++++++++++++++++---- package.json | 1 - test/base.test.js | 7 ++++++ test/forwarded.test.js | 48 ++++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 test/forwarded.test.js 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..d211e64 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,62 @@ 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() + + 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 +263,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 +276,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..578676b --- /dev/null +++ b/test/forwarded.test.js @@ -0,0 +1,48 @@ +'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 valid 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 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']) +}) From c08a52f33d1487a4991e87e2f73f78c5152cfa50 Mon Sep 17 00:00:00 2001 From: Alec Fenichel Date: Sat, 10 Oct 2026 11:44:49 -0400 Subject: [PATCH 2/4] Fix typo Signed-off-by: Alec Fenichel --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index d211e64..3c30589 100644 --- a/index.js +++ b/index.js @@ -89,7 +89,7 @@ function forwarded (header) { * * @param {Object} request * @param {Function|Array|String} [trust] - * @param {boolean} [uds] + * @param {Boolean} [uds] * @public */ @@ -263,7 +263,7 @@ function parseNetmask (netmask) { * * @param {Object} request * @param {Function|Array|String} trust - * @param {boolean} [uds] + * @param {Boolean} [uds] * @public */ From bd6a8549a78607fe8e9f75241da1916c3daaae10 Mon Sep 17 00:00:00 2001 From: Alec Fenichel Date: Sat, 10 Oct 2026 21:07:16 -0400 Subject: [PATCH 3/4] Clean up test Signed-off-by: Alec Fenichel --- test/forwarded.test.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/forwarded.test.js b/test/forwarded.test.js index 578676b..a460b63 100644 --- a/test/forwarded.test.js +++ b/test/forwarded.test.js @@ -15,7 +15,7 @@ test('with empty x-forwarded-for header should return empty array', function (t) t.assert.deepStrictEqual(proxyaddr.forwarded(' '), []) }) -test('with single valid x-forwarded-for header should return array with that address', function (t) { +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']) }) @@ -23,10 +23,6 @@ test('with multiple x-forwarded-for header should return array with those addres 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 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']) }) From 6e6dd767d3a85f83a96455fc8a19f2d52e7e35b3 Mon Sep 17 00:00:00 2001 From: Alec Fenichel Date: Sat, 10 Oct 2026 21:16:28 -0400 Subject: [PATCH 4/4] Add forwarded function algorithem attribution Signed-off-by: Alec Fenichel --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 3c30589..b5951f6 100644 --- a/index.js +++ b/index.js @@ -60,6 +60,8 @@ function forwarded (header) { 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