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
21 changes: 15 additions & 6 deletions src/define_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { ServerConfig } from './types/server.ts'

type UserDefinedServerConfig = DeepPartial<
Omit<ServerConfig, 'trustProxy'> & {
trustProxy: ((address: string, distance: number) => boolean) | boolean | string
trustProxy: ((address: string, distance: number) => boolean) | boolean | string | string[]
}
>

Expand All @@ -28,6 +28,9 @@ type UserDefinedServerConfig = DeepPartial<
* normalizes certain properties (like cookie maxAge and trustProxy settings),
* and returns a complete ServerConfig object.
*
* The `trustProxy` option accepts a range string, a comma-separated list of
* ranges, or an array of ranges. It also accepts a boolean or a custom function.
*
* @param config - User-defined server configuration options
*
* @example
Expand Down Expand Up @@ -103,15 +106,21 @@ export function defineConfig(config: UserDefinedServerConfig): ServerConfig {
}

/**
* Normalizing trust proxy setting to allow boolean and
* string values
* Normalizing trust proxy setting to allow boolean, string,
* and array values
*/
if (typeof trustProxy === 'boolean') {
const tpValue = trustProxy
normalizedConfig.trustProxy = (_, __) => tpValue
} else if (typeof trustProxy === 'string') {
const tpValue = trustProxy
normalizedConfig.trustProxy = proxyAddr.compile(tpValue)
} else if (typeof trustProxy === 'string' || Array.isArray(trustProxy)) {
normalizedConfig.trustProxy = proxyAddr.compile(
typeof trustProxy === 'string'
? trustProxy
.split(',')
.map((value) => value.trim())
.filter(Boolean)
: trustProxy
)
} else if (trustProxy) {
normalizedConfig.trustProxy = trustProxy
}
Expand Down
34 changes: 34 additions & 0 deletions tests/define_config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,40 @@ test.group('Define config', () => {
const config = defineConfig({ trustProxy: 'loopback' })

assert.typeOf(config.trustProxy, 'function')
assert.isTrue(config.trustProxy('127.0.0.1', 0))
assert.isFalse(config.trustProxy('10.20.30.40', 0))
})

test('compile comma-separated trustProxy config', ({ assert }) => {
const config = defineConfig({
trustProxy: 'loopback, 10.20.0.0/16, 2001:db8::/32',
})

assert.isTrue(config.trustProxy('127.0.0.1', 0))
assert.isTrue(config.trustProxy('10.20.30.40', 0))
assert.isTrue(config.trustProxy('2001:db8::1', 0))
assert.isFalse(config.trustProxy('192.168.1.1', 0))
})

test('compile trustProxy config when an array', ({ assert }) => {
const config = defineConfig({ trustProxy: ['loopback', '2001:db8::/32'] })

assert.isTrue(config.trustProxy('127.0.0.1', 0))
assert.isTrue(config.trustProxy('2001:db8::1', 0))
assert.isFalse(config.trustProxy('10.20.30.40', 0))
})

test('trust no proxies when a comma-separated string has no ranges', ({ assert }) => {
const config = defineConfig({ trustProxy: ' , ' })

assert.isFalse(config.trustProxy('127.0.0.1', 0))
})

test('throw when a trustProxy range is invalid', ({ assert }) => {
assert.throws(
() => defineConfig({ trustProxy: 'loopback, invalid' }),
'invalid IP address: invalid'
)
})

test('compile trustProxy config when a function', ({ assert }) => {
Expand Down
14 changes: 14 additions & 0 deletions tests/request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { EncryptionFactory } from '@boringnode/encryption/factories'
import { RouterFactory } from '../factories/router.ts'
import { HttpRequestFactory } from '../factories/request.ts'
import { httpServer } from '../factories/http_server.ts'
import { defineConfig } from '../index.ts'
import { CookieSerializer } from '../src/cookies/serializer.ts'
import { HttpContextFactory } from '../factories/http_context.ts'

Expand Down Expand Up @@ -612,6 +613,19 @@ test.group('Request', () => {
assert.equal(body.ip, '10.10.10.10')
})

test('trust proxy from an array in the defined config', async ({ assert }) => {
const config = defineConfig({ trustProxy: ['loopback'] })
const { url } = await httpServer.create((req, res) => {
req.headers['x-forwarded-for'] = '10.10.10.10'
const request = new HttpRequestFactory().merge({ req, res, encryption, config }).create()
res.writeHead(200, { 'content-type': 'application/json' })
res.end(JSON.stringify({ ip: request.ip() }))
})

const { body } = await supertest(url).get('/')
assert.equal(body.ip, '10.10.10.10')
})

test('use the first forwarded host from a trusted proxy', async ({ assert }) => {
const { url } = await httpServer.create((req, res) => {
const request = new HttpRequestFactory()
Expand Down
Loading