Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ $ npm i @fastify/proxy-addr
const proxyaddr = require('@fastify/proxy-addr')
```

### proxyaddr(req, trust)
### proxyaddr(req, trust, uds)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% convinced that a new parameter should be added; why not included within the trust one?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking this would help clarify the distinction between this and regular trust. When an IP address is trusted that trust applies in the socket and in the X-Forwarded-For header. When UDS is set, it signals to the function that the socket address is expected to be undefined and that the first IP address in the X-Forwarded-For header is the starting point.

That being said, happy to change it to use the trust parameter if that is preferable. Any suggestions on what to put in the trust parameter to indicate unix domain sockets are being used?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See your point; agree that it will be better to keep it separate. Maybe we can create a new opts object that has this property. In that way is a bit more explicit than a positional argument, and if more flags start to being added, we can also add it to that baggage without requiring to adjust positional args.


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.

The optional `uds` (Unix domain socket) argument indicates whether
an undefined socket remote address should be treated as trusted.

<!-- eslint-disable no-undef -->

```js
Expand Down Expand Up @@ -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
Expand Down
57 changes: 52 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')

/**
Expand All @@ -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
Expand Down Expand Up @@ -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')
}
Expand All @@ -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]
}
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
}
],
"dependencies": {
"@fastify/forwarded": "^3.0.0",
"ipaddr.js": "^2.1.0"
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions test/base.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
44 changes: 44 additions & 0 deletions test/forwarded.test.js
Original file line number Diff line number Diff line change
@@ -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'])
})
Loading