diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index 43e30606a7238..e1289d9f6babe 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -281,6 +281,34 @@ test('accepts any type of objects for custom headers', () => { expect(() => httpSchema.validate(obj)).not.toThrow(); }); +test('forbids the "location" custom response header', () => { + const httpSchema = config.schema; + const obj = { + customResponseHeaders: { + location: 'string', + Location: 'string', + lOcAtIoN: 'string', + }, + }; + expect(() => httpSchema.validate(obj)).toThrowErrorMatchingInlineSnapshot( + `"[customResponseHeaders]: The following custom response headers are not allowed to be set: location, Location, lOcAtIoN"` + ); +}); + +test('forbids the "refresh" custom response header', () => { + const httpSchema = config.schema; + const obj = { + customResponseHeaders: { + refresh: 'string', + Refresh: 'string', + rEfReSh: 'string', + }, + }; + expect(() => httpSchema.validate(obj)).toThrowErrorMatchingInlineSnapshot( + `"[customResponseHeaders]: The following custom response headers are not allowed to be set: refresh, Refresh, rEfReSh"` + ); +}); + describe('with TLS', () => { test('throws if TLS is enabled but `redirectHttpFromPort` is equal to `port`', () => { const httpSchema = config.schema; diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 901dd8bc1a1ef..6e78e4ec38551 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -26,6 +26,9 @@ const hostURISchema = schema.uri({ scheme: ['http', 'https'] }); const match = (regex: RegExp, errorMsg: string) => (str: string) => regex.test(str) ? undefined : errorMsg; +// The lower-case set of response headers which are forbidden within `customResponseHeaders`. +const RESPONSE_HEADER_DENY_LIST = ['location', 'refresh']; + const configSchema = schema.object( { name: schema.string({ defaultValue: () => hostname() }), @@ -70,6 +73,16 @@ const configSchema = schema.object( securityResponseHeaders: securityResponseHeadersSchema, customResponseHeaders: schema.recordOf(schema.string(), schema.any(), { defaultValue: {}, + validate(value) { + const forbiddenKeys = Object.keys(value).filter((headerName) => + RESPONSE_HEADER_DENY_LIST.includes(headerName.toLowerCase()) + ); + if (forbiddenKeys.length > 0) { + return `The following custom response headers are not allowed to be set: ${forbiddenKeys.join( + ', ' + )}`; + } + }, }), host: schema.string({ defaultValue: 'localhost',