diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md b/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md
index b05d28899f9d2..589529cf2a7f7 100644
--- a/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md
+++ b/docs/development/core/server/kibana-plugin-server.coresetup.getstartservices.md
@@ -1,17 +1,17 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) > [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md)
-
-## CoreSetup.getStartServices() method
-
-Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle.
-
-Signature:
-
-```typescript
-getStartServices(): Promise<[CoreStart, TPluginsStart]>;
-```
-Returns:
-
-`Promise<[CoreStart, TPluginsStart]>`
-
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) > [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md)
+
+## CoreSetup.getStartServices() method
+
+Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle.
+
+Signature:
+
+```typescript
+getStartServices(): Promise<[CoreStart, TPluginsStart]>;
+```
+Returns:
+
+`Promise<[CoreStart, TPluginsStart]>`
+
diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md
index c36d649837e8a..325f7216122b5 100644
--- a/docs/development/core/server/kibana-plugin-server.coresetup.md
+++ b/docs/development/core/server/kibana-plugin-server.coresetup.md
@@ -1,32 +1,32 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md)
-
-## CoreSetup interface
-
-Context passed to the plugins `setup` method.
-
-Signature:
-
-```typescript
-export interface CoreSetup
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | CapabilitiesSetup
| [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
-| [context](./kibana-plugin-server.coresetup.context.md) | ContextSetup
| [ContextSetup](./kibana-plugin-server.contextsetup.md) |
-| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup
| [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
-| [http](./kibana-plugin-server.coresetup.http.md) | HttpServiceSetup
| [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
-| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup
| [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
-| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | UiSettingsServiceSetup
| [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
-| [uuid](./kibana-plugin-server.coresetup.uuid.md) | UuidServiceSetup
| [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
-
-## Methods
-
-| Method | Description |
-| --- | --- |
-| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start
. This should only be used inside handlers registered during setup
that will only be executed after start
lifecycle. |
-
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md)
+
+## CoreSetup interface
+
+Context passed to the plugins `setup` method.
+
+Signature:
+
+```typescript
+export interface CoreSetup
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | CapabilitiesSetup
| [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
+| [context](./kibana-plugin-server.coresetup.context.md) | ContextSetup
| [ContextSetup](./kibana-plugin-server.contextsetup.md) |
+| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup
| [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
+| [http](./kibana-plugin-server.coresetup.http.md) | HttpServiceSetup
| [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
+| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup
| [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
+| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | UiSettingsServiceSetup
| [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
+| [uuid](./kibana-plugin-server.coresetup.uuid.md) | UuidServiceSetup
| [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
+
+## Methods
+
+| Method | Description |
+| --- | --- |
+| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start
. This should only be used inside handlers registered during setup
that will only be executed after start
lifecycle. |
+
diff --git a/docs/development/core/server/kibana-plugin-server.cspconfig.md b/docs/development/core/server/kibana-plugin-server.cspconfig.md
index 7e491cb0df912..6f12d64676ce9 100644
--- a/docs/development/core/server/kibana-plugin-server.cspconfig.md
+++ b/docs/development/core/server/kibana-plugin-server.cspconfig.md
@@ -20,9 +20,9 @@ The constructor for this class is marked as internal. Third-party code should no
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
-| [DEFAULT](./kibana-plugin-server.cspconfig.default.md) | static
| CspConfig
| |
| [header](./kibana-plugin-server.cspconfig.header.md) | | string
| |
| [rules](./kibana-plugin-server.cspconfig.rules.md) | | string[]
| |
+| [rulesChangedFromDefault](./kibana-plugin-server.cspconfig.ruleschangedfromdefault.md) | | boolean
| |
| [strict](./kibana-plugin-server.cspconfig.strict.md) | | boolean
| |
| [warnLegacyBrowsers](./kibana-plugin-server.cspconfig.warnlegacybrowsers.md) | | boolean
| |
diff --git a/docs/development/core/server/kibana-plugin-server.cspconfig.default.md b/docs/development/core/server/kibana-plugin-server.cspconfig.ruleschangedfromdefault.md
similarity index 56%
rename from docs/development/core/server/kibana-plugin-server.cspconfig.default.md
rename to docs/development/core/server/kibana-plugin-server.cspconfig.ruleschangedfromdefault.md
index 56e6cf35cdd13..3320785935b21 100644
--- a/docs/development/core/server/kibana-plugin-server.cspconfig.default.md
+++ b/docs/development/core/server/kibana-plugin-server.cspconfig.ruleschangedfromdefault.md
@@ -1,11 +1,11 @@
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CspConfig](./kibana-plugin-server.cspconfig.md) > [DEFAULT](./kibana-plugin-server.cspconfig.default.md)
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CspConfig](./kibana-plugin-server.cspconfig.md) > [rulesChangedFromDefault](./kibana-plugin-server.cspconfig.ruleschangedfromdefault.md)
-## CspConfig.DEFAULT property
+## CspConfig.rulesChangedFromDefault property
Signature:
```typescript
-static readonly DEFAULT: CspConfig;
+readonly rulesChangedFromDefault: boolean;
```
diff --git a/docs/development/core/server/kibana-plugin-server.icspconfig.md b/docs/development/core/server/kibana-plugin-server.icspconfig.md
index fb8188386a376..00107d561873f 100644
--- a/docs/development/core/server/kibana-plugin-server.icspconfig.md
+++ b/docs/development/core/server/kibana-plugin-server.icspconfig.md
@@ -18,6 +18,7 @@ export interface ICspConfig
| --- | --- | --- |
| [header](./kibana-plugin-server.icspconfig.header.md) | string
| The CSP rules in a formatted directives string for use in a Content-Security-Policy
header. |
| [rules](./kibana-plugin-server.icspconfig.rules.md) | string[]
| The CSP rules used for Kibana. |
+| [rulesChangedFromDefault](./kibana-plugin-server.icspconfig.ruleschangedfromdefault.md) | boolean
| Flag indicating that the configuraion changes the csp rules from the defaults |
| [strict](./kibana-plugin-server.icspconfig.strict.md) | boolean
| Specify whether browsers that do not support CSP should be able to use Kibana. Use true
to block and false
to allow. |
| [warnLegacyBrowsers](./kibana-plugin-server.icspconfig.warnlegacybrowsers.md) | boolean
| Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance. |
diff --git a/docs/development/core/server/kibana-plugin-server.icspconfig.ruleschangedfromdefault.md b/docs/development/core/server/kibana-plugin-server.icspconfig.ruleschangedfromdefault.md
new file mode 100644
index 0000000000000..427c07d2d1e4d
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.icspconfig.ruleschangedfromdefault.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ICspConfig](./kibana-plugin-server.icspconfig.md) > [rulesChangedFromDefault](./kibana-plugin-server.icspconfig.ruleschangedfromdefault.md)
+
+## ICspConfig.rulesChangedFromDefault property
+
+Flag indicating that the configuraion changes the csp rules from the defaults
+
+Signature:
+
+```typescript
+readonly rulesChangedFromDefault: boolean;
+```
diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client_status.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client_status.ts
index 22baf4a330416..3775aecb2db09 100644
--- a/packages/kbn-dev-utils/src/kbn_client/kbn_client_status.ts
+++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client_status.ts
@@ -32,6 +32,7 @@ interface Status {
interface ApiResponseStatus {
name: string;
uuid: string;
+ running_from_source?: true;
version: {
number: string;
build_hash: string;
@@ -58,6 +59,11 @@ export class KbnClientStatus {
});
}
+ public async isDistributable() {
+ const status = await this.get();
+ return !status.running_from_source;
+ }
+
/**
* Get the overall/merged state
*/
diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js
index 8bded9d403c21..364b91a30841c 100644
--- a/packages/kbn-pm/dist/index.js
+++ b/packages/kbn-pm/dist/index.js
@@ -43639,6 +43639,10 @@ class KbnClientStatus {
path: 'api/status',
});
}
+ async isDistributable() {
+ const status = await this.get();
+ return !status.running_from_source;
+ }
/**
* Get the overall/merged state
*/
diff --git a/src/core/server/config/env.mock.ts b/src/core/server/config/env.mock.ts
new file mode 100644
index 0000000000000..f9b4e0732c1cb
--- /dev/null
+++ b/src/core/server/config/env.mock.ts
@@ -0,0 +1,29 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Env } from './env';
+
+export function createMockEnv(options: { dist?: boolean } = {}): Env {
+ return {
+ // required by CspConfig
+ packageInfo: {
+ dist: options.dist ?? true,
+ },
+ } as any;
+}
diff --git a/src/core/server/csp/csp_config.test.ts b/src/core/server/csp/csp_config.test.ts
index 45fa8445791b0..15cddc6fccd34 100644
--- a/src/core/server/csp/csp_config.test.ts
+++ b/src/core/server/csp/csp_config.test.ts
@@ -18,6 +18,7 @@
*/
import { CspConfig } from '.';
+import { createMockEnv } from '../config/env.mock';
// CSP rules aren't strictly additive, so any change can potentially expand or
// restrict the policy in a way we consider a breaking change. For that reason,
@@ -33,23 +34,10 @@ import { CspConfig } from '.';
// the nature of a change in defaults during a PR review.
describe('CspConfig', () => {
- test('DEFAULT', () => {
- expect(CspConfig.DEFAULT).toMatchInlineSnapshot(`
- CspConfig {
- "header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
- "rules": Array [
- "script-src 'unsafe-eval' 'self'",
- "worker-src blob: 'self'",
- "style-src 'unsafe-inline' 'self'",
- ],
- "strict": true,
- "warnLegacyBrowsers": true,
- }
- `);
- });
-
test('defaults from config', () => {
- expect(new CspConfig()).toMatchInlineSnapshot(`
+ const cspConfig = new CspConfig(createMockEnv());
+
+ expect(cspConfig).toMatchInlineSnapshot(`
CspConfig {
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
"rules": Array [
@@ -57,6 +45,7 @@ describe('CspConfig', () => {
"worker-src blob: 'self'",
"style-src 'unsafe-inline' 'self'",
],
+ "rulesChangedFromDefault": false,
"strict": true,
"warnLegacyBrowsers": true,
}
@@ -64,7 +53,9 @@ describe('CspConfig', () => {
});
test('creates from partial config', () => {
- expect(new CspConfig({ strict: false, warnLegacyBrowsers: false })).toMatchInlineSnapshot(`
+ const cspConfig = new CspConfig(createMockEnv(), { strict: false, warnLegacyBrowsers: false });
+
+ expect(cspConfig).toMatchInlineSnapshot(`
CspConfig {
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
"rules": Array [
@@ -72,6 +63,7 @@ describe('CspConfig', () => {
"worker-src blob: 'self'",
"style-src 'unsafe-inline' 'self'",
],
+ "rulesChangedFromDefault": false,
"strict": false,
"warnLegacyBrowsers": false,
}
@@ -79,7 +71,7 @@ describe('CspConfig', () => {
});
test('computes header from rules', () => {
- const cspConfig = new CspConfig({ rules: ['alpha', 'beta', 'gamma'] });
+ const cspConfig = new CspConfig(createMockEnv(), { rules: ['alpha', 'beta', 'gamma'] });
expect(cspConfig).toMatchInlineSnapshot(`
CspConfig {
@@ -89,6 +81,25 @@ describe('CspConfig', () => {
"beta",
"gamma",
],
+ "rulesChangedFromDefault": true,
+ "strict": true,
+ "warnLegacyBrowsers": true,
+ }
+ `);
+ });
+
+ test(`includes blob: style-src if env indicates we're running from source`, () => {
+ const cspConfig = new CspConfig(createMockEnv({ dist: false }));
+
+ expect(cspConfig).toMatchInlineSnapshot(`
+ CspConfig {
+ "header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src blob: 'unsafe-inline' 'self'",
+ "rules": Array [
+ "script-src 'unsafe-eval' 'self'",
+ "worker-src blob: 'self'",
+ "style-src blob: 'unsafe-inline' 'self'",
+ ],
+ "rulesChangedFromDefault": false,
"strict": true,
"warnLegacyBrowsers": true,
}
diff --git a/src/core/server/csp/csp_config.ts b/src/core/server/csp/csp_config.ts
index bb57702a4a241..592a7209f4364 100644
--- a/src/core/server/csp/csp_config.ts
+++ b/src/core/server/csp/csp_config.ts
@@ -18,6 +18,7 @@
*/
import { config } from './config';
+import { Env } from '../config';
const DEFAULT_CONFIG = Object.freeze(config.schema.validate({}));
@@ -48,6 +49,12 @@ export interface ICspConfig {
* in a `Content-Security-Policy` header.
*/
readonly header: string;
+
+ /**
+ * Flag indicating that the configuraion changes the csp
+ * rules from the defaults
+ */
+ readonly rulesChangedFromDefault: boolean;
}
/**
@@ -55,23 +62,37 @@ export interface ICspConfig {
* @public
*/
export class CspConfig implements ICspConfig {
- static readonly DEFAULT = new CspConfig();
-
public readonly rules: string[];
public readonly strict: boolean;
public readonly warnLegacyBrowsers: boolean;
public readonly header: string;
+ public readonly rulesChangedFromDefault: boolean;
/**
* Returns the default CSP configuration when passed with no config
* @internal
*/
- constructor(rawCspConfig: Partial> = {}) {
+ constructor(env: Env, rawCspConfig?: Partial>) {
const source = { ...DEFAULT_CONFIG, ...rawCspConfig };
- this.rules = source.rules;
+ this.rules = source.rules.map(rule => {
+ // if we receive an env, and it indicates that this isn't a distributable, add `blob:` to the style csp rules
+ if (env && !env.packageInfo.dist && rule.startsWith('style-src ')) {
+ return rule.replace(/^style-src /, 'style-src blob: ');
+ }
+
+ return rule;
+ });
this.strict = source.strict;
this.warnLegacyBrowsers = source.warnLegacyBrowsers;
- this.header = source.rules.join('; ');
+ this.header = this.rules.join('; ');
+
+ // only check to see if the csp values are customized when `rawCspConfig` was received.
+ if (!rawCspConfig) {
+ this.rulesChangedFromDefault = false;
+ } else {
+ const defaultCsp = new CspConfig(env);
+ this.rulesChangedFromDefault = defaultCsp.header !== this.header;
+ }
}
}
diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts
index 73f44f3c5ab5c..b249dbb5b6a04 100644
--- a/src/core/server/http/http_config.ts
+++ b/src/core/server/http/http_config.ts
@@ -22,6 +22,7 @@ import { hostname } from 'os';
import { CspConfigType, CspConfig, ICspConfig } from '../csp';
import { SslConfig, sslSchema } from './ssl_config';
+import { Env } from '../config';
const validBasePathRegex = /(^$|^\/.*[^\/]$)/;
const uuidRegexp = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -148,7 +149,7 @@ export class HttpConfig {
/**
* @internal
*/
- constructor(rawHttpConfig: HttpConfigType, rawCspConfig: CspConfigType) {
+ constructor(rawHttpConfig: HttpConfigType, rawCspConfig: CspConfigType, env: Env) {
this.autoListen = rawHttpConfig.autoListen;
this.host = rawHttpConfig.host;
this.port = rawHttpConfig.port;
@@ -162,7 +163,7 @@ export class HttpConfig {
this.rewriteBasePath = rawHttpConfig.rewriteBasePath;
this.ssl = new SslConfig(rawHttpConfig.ssl || {});
this.compression = rawHttpConfig.compression;
- this.csp = new CspConfig(rawCspConfig);
+ this.csp = new CspConfig(env, rawCspConfig);
this.xsrf = rawHttpConfig.xsrf;
}
}
diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts
index 6db1ca80ab437..7fc2f5d990824 100644
--- a/src/core/server/http/http_service.mock.ts
+++ b/src/core/server/http/http_service.mock.ts
@@ -21,6 +21,7 @@ import { Server } from 'hapi';
import { CspConfig } from '../csp';
import { mockRouter } from './router/router.mock';
import { configMock } from '../config/config.mock';
+import { createMockEnv } from '../config/env.mock';
import { InternalHttpServiceSetup } from './types';
import { HttpService } from './http_service';
import { OnPreAuthToolkit } from './lifecycle/on_pre_auth';
@@ -61,7 +62,7 @@ const createSetupContractMock = () => {
registerOnPreResponse: jest.fn(),
createRouter: jest.fn().mockImplementation(() => mockRouter.create({})),
basePath: createBasePathMock(),
- csp: CspConfig.DEFAULT,
+ csp: new CspConfig(createMockEnv()),
auth: {
get: jest.fn(),
isAuthenticated: jest.fn(),
diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts
index ae9d53f9fd3db..8a86667a1a6bc 100644
--- a/src/core/server/http/http_service.ts
+++ b/src/core/server/http/http_service.ts
@@ -70,7 +70,7 @@ export class HttpService implements CoreService(httpConfig.path),
configService.atPath(cspConfig.path),
- ]).pipe(map(([http, csp]) => new HttpConfig(http, csp)));
+ ]).pipe(map(([http, csp]) => new HttpConfig(http, csp, env)));
this.httpServer = new HttpServer(logger, 'Kibana');
this.httpsRedirectServer = new HttpsRedirectServer(logger.get('http', 'redirect', 'server'));
}
diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts
index c1322a5aa94db..d552339f1ea1c 100644
--- a/src/core/server/http/http_tools.test.ts
+++ b/src/core/server/http/http_tools.test.ts
@@ -29,6 +29,7 @@ import { defaultValidationErrorHandler, HapiValidationError, getServerOptions }
import { HttpServer } from './http_server';
import { HttpConfig, config } from './http_config';
import { Router } from './router';
+import { createMockEnv } from '../config/env.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { ByteSizeValue } from '@kbn/config-schema';
@@ -120,7 +121,8 @@ describe('getServerOptions', () => {
certificate: 'some-certificate-path',
},
}),
- {} as any
+ {} as any,
+ createMockEnv()
);
expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(`
@@ -149,7 +151,8 @@ describe('getServerOptions', () => {
clientAuthentication: 'required',
},
}),
- {} as any
+ {} as any,
+ createMockEnv()
);
expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(`
diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts
index 07cc933033054..7862412fbad06 100644
--- a/src/core/server/legacy/legacy_service.ts
+++ b/src/core/server/legacy/legacy_service.ts
@@ -86,7 +86,7 @@ export class LegacyService implements CoreService {
public legacyInternals?: ILegacyInternals;
constructor(private readonly coreContext: CoreContext) {
- const { logger, configService } = coreContext;
+ const { logger, configService, env } = coreContext;
this.log = logger.get('legacy-service');
this.devConfig$ = configService
@@ -95,7 +95,7 @@ export class LegacyService implements CoreService {
this.httpConfig$ = combineLatest(
configService.atPath(httpConfig.path),
configService.atPath(cspConfig.path)
- ).pipe(map(([http, csp]) => new HttpConfig(http, csp)));
+ ).pipe(map(([http, csp]) => new HttpConfig(http, csp, env)));
}
public async discoverPlugins(): Promise {
diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts
index c0a8973d98a54..846c2e4d8c507 100644
--- a/src/core/server/mocks.ts
+++ b/src/core/server/mocks.ts
@@ -33,6 +33,7 @@ import { capabilitiesServiceMock } from './capabilities/capabilities_service.moc
export { httpServerMock } from './http/http_server.mocks';
export { sessionStorageMock } from './http/cookie_session_storage.mocks';
export { configServiceMock } from './config/config_service.mock';
+import { createMockEnv } from './config/env.mock';
export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
export { httpServiceMock } from './http/http_service.mock';
export { loggingServiceMock } from './logging/logging_service.mock';
@@ -97,7 +98,7 @@ function createCoreSetupMock() {
registerOnPostAuth: httpService.registerOnPostAuth,
registerOnPreResponse: httpService.registerOnPreResponse,
basePath: httpService.basePath,
- csp: CspConfig.DEFAULT,
+ csp: new CspConfig(createMockEnv()),
isTlsEnabled: httpService.isTlsEnabled,
createRouter: jest.fn(),
registerRouteHandlerContext: jest.fn(),
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index dce5ec64bfa66..aa9ed8ecdbd40 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -583,14 +583,14 @@ export interface CoreStart {
// @public
export class CspConfig implements ICspConfig {
// @internal
- constructor(rawCspConfig?: Partial>);
- // (undocumented)
- static readonly DEFAULT: CspConfig;
+ constructor(env: Env, rawCspConfig?: Partial>);
// (undocumented)
readonly header: string;
// (undocumented)
readonly rules: string[];
// (undocumented)
+ readonly rulesChangedFromDefault: boolean;
+ // (undocumented)
readonly strict: boolean;
// (undocumented)
readonly warnLegacyBrowsers: boolean;
@@ -774,6 +774,7 @@ export type IContextProvider, TContextName
export interface ICspConfig {
readonly header: string;
readonly rules: string[];
+ readonly rulesChangedFromDefault: boolean;
readonly strict: boolean;
readonly warnLegacyBrowsers: boolean;
}
diff --git a/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.test.ts b/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.test.ts
index 395cb60587832..584621a1ce13f 100644
--- a/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.test.ts
+++ b/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.test.ts
@@ -18,6 +18,8 @@
*/
import { CspConfig, ICspConfig } from '../../../../../../core/server';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { createMockEnv } from '../../../../../../core/server/config/env.mock';
import { createCspCollector } from './csp_collector';
const createMockKbnServer = () => ({
@@ -25,7 +27,7 @@ const createMockKbnServer = () => ({
setup: {
core: {
http: {
- csp: new CspConfig(),
+ csp: new CspConfig(createMockEnv()),
},
},
},
@@ -36,7 +38,7 @@ describe('csp collector', () => {
let kbnServer: ReturnType;
function updateCsp(config: Partial) {
- kbnServer.newPlatform.setup.core.http.csp = new CspConfig(config);
+ kbnServer.newPlatform.setup.core.http.csp = new CspConfig(createMockEnv(), config);
}
beforeEach(() => {
diff --git a/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.ts b/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.ts
index 6622ed4bef478..dce4c7fe6fefc 100644
--- a/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.ts
+++ b/src/legacy/core_plugins/kibana/server/lib/csp_usage_collector/csp_collector.ts
@@ -18,7 +18,6 @@
*/
import { Server } from 'hapi';
-import { CspConfig } from '../../../../../../core/server';
import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
export function createCspCollector(server: Server) {
@@ -26,7 +25,11 @@ export function createCspCollector(server: Server) {
type: 'csp',
isReady: () => true,
async fetch() {
- const { strict, warnLegacyBrowsers, header } = server.newPlatform.setup.core.http.csp;
+ const {
+ strict,
+ warnLegacyBrowsers,
+ rulesChangedFromDefault,
+ } = server.newPlatform.setup.core.http.csp;
return {
strict,
@@ -34,7 +37,7 @@ export function createCspCollector(server: Server) {
// It's important that we do not send the value of csp.header here as it
// can be customized with values that can be identifiable to given
// installs, such as URLs
- rulesChangedFromDefault: header !== CspConfig.DEFAULT.header,
+ rulesChangedFromDefault,
};
},
};
diff --git a/src/legacy/server/status/routes/api/register_status.js b/src/legacy/server/status/routes/api/register_status.js
index 259a00667810f..55bf81f2c5f9a 100644
--- a/src/legacy/server/status/routes/api/register_status.js
+++ b/src/legacy/server/status/routes/api/register_status.js
@@ -18,6 +18,7 @@
*/
import { wrapAuthConfig } from '../../wrap_auth_config';
+import { IS_KIBANA_DISTRIBUTABLE } from '../../../../utils/artifact_type';
const matchSnapshot = /-SNAPSHOT$/;
@@ -35,6 +36,8 @@ export function registerStatusApi(kbnServer, server, config) {
return {
name: config.get('server.name'),
uuid: config.get('server.uuid'),
+ // flag to help tests know that kibana is running from source, not included in distributable response
+ ...(IS_KIBANA_DISTRIBUTABLE ? {} : { running_from_source: true }),
version: {
number: config.get('pkg.version').replace(matchSnapshot, ''),
build_hash: config.get('pkg.buildSha'),
diff --git a/test/api_integration/apis/general/csp.js b/test/api_integration/apis/general/csp.js
index 8c191703070d8..38893c371fb52 100644
--- a/test/api_integration/apis/general/csp.js
+++ b/test/api_integration/apis/general/csp.js
@@ -21,6 +21,7 @@ import expect from '@kbn/expect';
export default function({ getService }) {
const supertest = getService('supertest');
+ const kibanaServer = getService('kibanaServer');
describe('csp smoke test', () => {
it('app response sends content security policy headers', async () => {
@@ -36,11 +37,12 @@ export default function({ getService }) {
})
);
+ const isDist = await kibanaServer.status.isDistributable();
const entries = Array.from(parsed.entries());
expect(entries).to.eql([
['script-src', ["'unsafe-eval'", "'self'"]],
['worker-src', ['blob:', "'self'"]],
- ['style-src', ["'unsafe-inline'", "'self'"]],
+ ['style-src', [...(isDist ? [] : ['blob:']), "'unsafe-inline'", "'self'"]],
]);
});
});
diff --git a/x-pack/test/oidc_api_integration/apis/implicit_flow/oidc_auth.ts b/x-pack/test/oidc_api_integration/apis/implicit_flow/oidc_auth.ts
index 1f5a64835416a..87979ca643a19 100644
--- a/x-pack/test/oidc_api_integration/apis/implicit_flow/oidc_auth.ts
+++ b/x-pack/test/oidc_api_integration/apis/implicit_flow/oidc_auth.ts
@@ -15,6 +15,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function({ getService }: FtrProviderContext) {
const supertest = getService('supertestWithoutAuth');
const config = getService('config');
+ const kibanaServer = getService('kibanaServer');
describe('OpenID Connect Implicit Flow authentication', () => {
describe('finishing handshake', () => {
@@ -56,12 +57,17 @@ export default function({ getService }: FtrProviderContext) {
});
await (dom.window as Record).__isScriptExecuted__;
+ const isDist = await kibanaServer.status.isDistributable();
// Check that proxy page is returned with proper headers.
expect(response.headers['content-type']).to.be('text/html; charset=utf-8');
expect(response.headers['cache-control']).to.be('private, no-cache, no-store');
expect(response.headers['content-security-policy']).to.be(
- `script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'`
+ [
+ `script-src 'unsafe-eval' 'self';`,
+ `worker-src blob: 'self';`,
+ `style-src ${isDist ? '' : 'blob: '}'unsafe-inline' 'self'`,
+ ].join(' ')
);
// Check that script that forwards URL fragment worked correctly.
diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts
index 0436d59906ea8..d4de4d601a36c 100644
--- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts
+++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts
@@ -17,6 +17,7 @@ export default function({ getService }: FtrProviderContext) {
const randomness = getService('randomness');
const supertest = getService('supertestWithoutAuth');
const config = getService('config');
+ const kibanaServer = getService('kibanaServer');
const kibanaServerConfig = config.get('servers.kibana');
@@ -137,12 +138,17 @@ export default function({ getService }: FtrProviderContext) {
});
await (dom.window as Record).__isScriptExecuted__;
+ const isDist = await kibanaServer.status.isDistributable();
// Check that proxy page is returned with proper headers.
expect(response.headers['content-type']).to.be('text/html; charset=utf-8');
expect(response.headers['cache-control']).to.be('private, no-cache, no-store');
expect(response.headers['content-security-policy']).to.be(
- `script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'`
+ [
+ `script-src 'unsafe-eval' 'self';`,
+ `worker-src blob: 'self';`,
+ `style-src ${isDist ? '' : 'blob: '}'unsafe-inline' 'self'`,
+ ].join(' ')
);
// Check that script that forwards URL fragment worked correctly.
diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts
index 6ea29b0d9e56e..1d83f374e966c 100644
--- a/x-pack/test/saml_api_integration/config.ts
+++ b/x-pack/test/saml_api_integration/config.ts
@@ -20,6 +20,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [require.resolve('./apis')],
servers: xPackAPITestsConfig.get('servers'),
services: {
+ kibanaServer: kibanaAPITestsConfig.get('services.kibanaServer'),
randomness: kibanaAPITestsConfig.get('services.randomness'),
legacyEs: kibanaAPITestsConfig.get('services.legacyEs'),
supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'),