diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 6cd848e9634315..8b50fc38167d39 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -453,11 +453,11 @@ deprecation warning at startup. This setting cannot end in a slash (`/`). | `server.cors.enabled:` | experimental[] Set to `true` to allow cross-origin API calls. *Default:* `false` -| `server.cors.credentials:` +| `server.cors.allowCredentials:` | experimental[] Set to `true` to allow browser code to access response body whenever request performed with user credentials. *Default:* `false` -| `server.cors.origin:` - | experimental[] List of origins permitted to access resources. You must specify explicit hostnames and not use `*` for `server.cors.origin` when `server.cors.credentials: true`. *Default:* "*" +| `server.cors.allowOrigin:` + | experimental[] List of origins permitted to access resources. You must specify explicit hostnames and not use `server.cors.allowOrigin: ["*"]` when `server.cors.allowCredentials: true`. *Default:* ["*"] | `server.compression.referrerWhitelist:` | Specifies an array of trusted hostnames, such as the {kib} host, or a reverse diff --git a/src/core/server/http/__snapshots__/http_config.test.ts.snap b/src/core/server/http/__snapshots__/http_config.test.ts.snap index a440c67944fabe..9b667f888771e1 100644 --- a/src/core/server/http/__snapshots__/http_config.test.ts.snap +++ b/src/core/server/http/__snapshots__/http_config.test.ts.snap @@ -39,9 +39,11 @@ Object { "enabled": true, }, "cors": Object { - "credentials": false, + "allowCredentials": false, + "allowOrigin": Array [ + "*", + ], "enabled": false, - "origin": "*", }, "customResponseHeaders": Object {}, "host": "localhost", diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index f893e7783ac8fb..b71763e8a2e14a 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -331,51 +331,67 @@ describe('with compression', () => { }); describe('cors', () => { - describe('origin', () => { + describe('allowOrigin', () => { it('list cannot be empty', () => { expect(() => config.schema.validate({ cors: { - origin: [], + allowOrigin: [], }, }) ).toThrowErrorMatchingInlineSnapshot(` - "[cors.origin]: types that failed validation: - - [cors.origin.0]: expected value to equal [*] - - [cors.origin.1]: array size is [0], but cannot be smaller than [1]" - `); + "[cors.allowOrigin]: types that failed validation: + - [cors.allowOrigin.0]: array size is [0], but cannot be smaller than [1] + - [cors.allowOrigin.1]: array size is [0], but cannot be smaller than [1]" + `); }); it('list of valid URLs', () => { - const origin = ['http://127.0.0.1:3000', 'https://elastic.co']; + const allowOrigin = ['http://127.0.0.1:3000', 'https://elastic.co']; expect( config.schema.validate({ - cors: { origin }, - }).cors.origin - ).toStrictEqual(origin); + cors: { allowOrigin }, + }).cors.allowOrigin + ).toStrictEqual(allowOrigin); expect(() => config.schema.validate({ cors: { - origin: ['*://elastic.co/*'], + allowOrigin: ['*://elastic.co/*'], }, }) ).toThrow(); }); it('can be configured as "*" wildcard', () => { - expect(config.schema.validate({ cors: { origin: '*' } }).cors.origin).toBe('*'); + expect(config.schema.validate({ cors: { allowOrigin: ['*'] } }).cors.allowOrigin).toEqual([ + '*', + ]); + }); + + it('cannot mix wildcard "*" with valid URLs', () => { + expect( + () => + config.schema.validate({ cors: { allowOrigin: ['*', 'https://elastic.co'] } }).cors + .allowOrigin + ).toThrowErrorMatchingInlineSnapshot(` + "[cors.allowOrigin]: types that failed validation: + - [cors.allowOrigin.0.0]: expected URI with scheme [http|https]. + - [cors.allowOrigin.1.1]: expected value to equal [*]" + `); }); }); describe('credentials', () => { - it('cannot use wildcard origin if "credentials: true"', () => { + it('cannot use wildcard allowOrigin if "credentials: true"', () => { expect( - () => config.schema.validate({ cors: { credentials: true, origin: '*' } }).cors.origin + () => + config.schema.validate({ cors: { allowCredentials: true, allowOrigin: ['*'] } }).cors + .allowOrigin ).toThrowErrorMatchingInlineSnapshot( `"[cors]: Cannot specify wildcard origin \\"*\\" with \\"credentials: true\\". Please provide a list of allowed origins."` ); expect( - () => config.schema.validate({ cors: { credentials: true } }).cors.origin + () => config.schema.validate({ cors: { allowCredentials: true } }).cors.allowOrigin ).toThrowErrorMatchingInlineSnapshot( `"[cors]: Cannot specify wildcard origin \\"*\\" with \\"credentials: true\\". Please provide a list of allowed origins."` ); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 74cdbfbedeea9d..2bd296fe338aba 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -48,17 +48,20 @@ export const config = { cors: schema.object( { enabled: schema.boolean({ defaultValue: false }), - credentials: schema.boolean({ defaultValue: false }), - origin: schema.oneOf( - [schema.literal('*'), schema.arrayOf(hostURISchema, { minSize: 1 })], + allowCredentials: schema.boolean({ defaultValue: false }), + allowOrigin: schema.oneOf( + [ + schema.arrayOf(hostURISchema, { minSize: 1 }), + schema.arrayOf(schema.literal('*'), { minSize: 1, maxSize: 1 }), + ], { - defaultValue: '*', + defaultValue: ['*'], } ), }, { validate(value) { - if (value.credentials === true && value.origin === '*') { + if (value.allowCredentials === true && value.allowOrigin.includes('*')) { return 'Cannot specify wildcard origin "*" with "credentials: true". Please provide a list of allowed origins.'; } }, @@ -168,8 +171,8 @@ export class HttpConfig { public port: number; public cors: { enabled: boolean; - credentials: boolean; - origin: '*' | string[]; + allowCredentials: boolean; + allowOrigin: string[]; }; public customResponseHeaders: Record; public maxPayload: ByteSizeValue; diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts index 4098b631b19d82..962c2107513b58 100644 --- a/src/core/server/http/http_tools.test.ts +++ b/src/core/server/http/http_tools.test.ts @@ -196,8 +196,8 @@ describe('getServerOptions', () => { config.schema.validate({ cors: { enabled: true, - credentials: false, - origin: '*', + allowCredentials: false, + allowOrigin: ['*'], }, }), {} as any, @@ -206,7 +206,7 @@ describe('getServerOptions', () => { expect(getServerOptions(httpConfig).routes?.cors).toEqual({ credentials: false, - origin: '*', + origin: ['*'], headers: ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf'], }); }); diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts index 61688a51345b5c..8bec26f31fa26b 100644 --- a/src/core/server/http/http_tools.ts +++ b/src/core/server/http/http_tools.ts @@ -39,8 +39,8 @@ const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None- export function getServerOptions(config: HttpConfig, { configureTLS = true } = {}) { const cors: RouteOptionsCors | false = config.cors.enabled ? { - credentials: config.cors.credentials, - origin: config.cors.origin, + credentials: config.cors.allowCredentials, + origin: config.cors.allowOrigin, headers: corsAllowedHeaders, } : false; diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index da03fee476f134..b792aa2d183b65 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -55,8 +55,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--plugin-path=${corsTestPlugin}`, `--test.cors.port=${pluginPort}`, '--server.cors.enabled=true', - '--server.cors.credentials=true', - `--server.cors.origin=["${originUrl}"]`, + '--server.cors.allowCredentials=true', + `--server.cors.allowOrigin=["${originUrl}"]`, ], }, }; diff --git a/x-pack/test/functional_cors/tests/cors.ts b/x-pack/test/functional_cors/tests/cors.ts index ff5da26b4e2754..774ffe1719f07f 100644 --- a/x-pack/test/functional_cors/tests/cors.ts +++ b/x-pack/test/functional_cors/tests/cors.ts @@ -15,9 +15,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('CORS', () => { it('Communicates to Kibana with configured CORS', async () => { const args: string[] = config.get('kbnTestServer.serverArgs'); - const originSetting = args.find((str) => str.includes('server.cors.origin')); + const originSetting = args.find((str) => str.includes('server.cors.allowOrigin')); if (!originSetting) { - throw new Error('Cannot find "server.cors.origin" argument'); + throw new Error('Cannot find "server.cors.allowOrigin" argument'); } const [, value] = originSetting.split('='); const url = JSON.parse(value);