mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* [csp] allow blob styles when running from source * update kbn/pm dist * add kibanaServer service to saml_api_integration suite * use common naming * attempt to use env.packageInfo instead of IS_KIBANA_DISTRIBUTABLE const * remove mock, clone rules before modifying * pass env where necessary * update core api docs * make env optional in HttpConfig * add tests for CspConfig changes, base header on this.rules * fix test snapshot * make env optional in HttpConfig too * remove CspConfig.DEFAULT and make env a required constructor arg * update csp_usage_collector tests * update core api docs * fix test name * rename headerChangedFromDefault back to rulesChangedFromDefault Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
3dc41fbc80
commit
d198bb521b
25 changed files with 212 additions and 97 deletions
|
@ -1,17 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
`Promise<[CoreStart, TPluginsStart]>`
|
||||
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
`Promise<[CoreStart, TPluginsStart]>`
|
||||
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface CoreSetup<TPluginsStart extends object = object>
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
|
||||
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
|
||||
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
|
||||
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
|
||||
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
|
||||
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
|
||||
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [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 <code>start</code>. This should only be used inside handlers registered during <code>setup</code> that will only be executed after <code>start</code> lifecycle. |
|
||||
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface CoreSetup<TPluginsStart extends object = object>
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
|
||||
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
|
||||
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
|
||||
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
|
||||
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
|
||||
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
|
||||
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [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 <code>start</code>. This should only be used inside handlers registered during <code>setup</code> that will only be executed after <code>start</code> lifecycle. |
|
||||
|
||||
|
|
|
@ -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) | <code>static</code> | <code>CspConfig</code> | |
|
||||
| [header](./kibana-plugin-server.cspconfig.header.md) | | <code>string</code> | |
|
||||
| [rules](./kibana-plugin-server.cspconfig.rules.md) | | <code>string[]</code> | |
|
||||
| [rulesChangedFromDefault](./kibana-plugin-server.cspconfig.ruleschangedfromdefault.md) | | <code>boolean</code> | |
|
||||
| [strict](./kibana-plugin-server.cspconfig.strict.md) | | <code>boolean</code> | |
|
||||
| [warnLegacyBrowsers](./kibana-plugin-server.cspconfig.warnlegacybrowsers.md) | | <code>boolean</code> | |
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
static readonly DEFAULT: CspConfig;
|
||||
readonly rulesChangedFromDefault: boolean;
|
||||
```
|
|
@ -18,6 +18,7 @@ export interface ICspConfig
|
|||
| --- | --- | --- |
|
||||
| [header](./kibana-plugin-server.icspconfig.header.md) | <code>string</code> | The CSP rules in a formatted directives string for use in a <code>Content-Security-Policy</code> header. |
|
||||
| [rules](./kibana-plugin-server.icspconfig.rules.md) | <code>string[]</code> | The CSP rules used for Kibana. |
|
||||
| [rulesChangedFromDefault](./kibana-plugin-server.icspconfig.ruleschangedfromdefault.md) | <code>boolean</code> | Flag indicating that the configuraion changes the csp rules from the defaults |
|
||||
| [strict](./kibana-plugin-server.icspconfig.strict.md) | <code>boolean</code> | Specify whether browsers that do not support CSP should be able to use Kibana. Use <code>true</code> to block and <code>false</code> to allow. |
|
||||
| [warnLegacyBrowsers](./kibana-plugin-server.icspconfig.warnlegacybrowsers.md) | <code>boolean</code> | Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance. |
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly rulesChangedFromDefault: boolean;
|
||||
```
|
|
@ -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
|
||||
*/
|
||||
|
|
4
packages/kbn-pm/dist/index.js
vendored
4
packages/kbn-pm/dist/index.js
vendored
|
@ -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
|
||||
*/
|
||||
|
|
29
src/core/server/config/env.mock.ts
Normal file
29
src/core/server/config/env.mock.ts
Normal file
|
@ -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;
|
||||
}
|
|
@ -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": false,
|
||||
"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": false,
|
||||
"warnLegacyBrowsers": true,
|
||||
}
|
||||
|
@ -64,7 +53,9 @@ describe('CspConfig', () => {
|
|||
});
|
||||
|
||||
test('creates from partial config', () => {
|
||||
expect(new CspConfig({ strict: true, 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,14 +63,15 @@ describe('CspConfig', () => {
|
|||
"worker-src blob: 'self'",
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
],
|
||||
"strict": true,
|
||||
"rulesChangedFromDefault": false,
|
||||
"strict": false,
|
||||
"warnLegacyBrowsers": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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": false,
|
||||
"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": false,
|
||||
"warnLegacyBrowsers": true,
|
||||
}
|
||||
|
|
|
@ -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<Omit<ICspConfig, 'header'>> = {}) {
|
||||
constructor(env: Env, rawCspConfig?: Partial<Omit<ICspConfig, 'header'>>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -70,7 +70,7 @@ export class HttpService implements CoreService<InternalHttpServiceSetup, HttpSe
|
|||
this.config$ = combineLatest([
|
||||
configService.atPath<HttpConfigType>(httpConfig.path),
|
||||
configService.atPath<CspConfigType>(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'));
|
||||
}
|
||||
|
|
|
@ -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(`
|
||||
|
|
|
@ -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<HttpConfigType>(httpConfig.path),
|
||||
configService.atPath<CspConfigType>(cspConfig.path)
|
||||
).pipe(map(([http, csp]) => new HttpConfig(http, csp)));
|
||||
).pipe(map(([http, csp]) => new HttpConfig(http, csp, env)));
|
||||
}
|
||||
|
||||
public async discoverPlugins(): Promise<LegacyServiceDiscoverPlugins> {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -583,14 +583,14 @@ export interface CoreStart {
|
|||
// @public
|
||||
export class CspConfig implements ICspConfig {
|
||||
// @internal
|
||||
constructor(rawCspConfig?: Partial<Omit<ICspConfig, 'header'>>);
|
||||
// (undocumented)
|
||||
static readonly DEFAULT: CspConfig;
|
||||
constructor(env: Env, rawCspConfig?: Partial<Omit<ICspConfig, 'header'>>);
|
||||
// (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<THandler extends HandlerFunction<any>, TContextName
|
|||
export interface ICspConfig {
|
||||
readonly header: string;
|
||||
readonly rules: string[];
|
||||
readonly rulesChangedFromDefault: boolean;
|
||||
readonly strict: boolean;
|
||||
readonly warnLegacyBrowsers: boolean;
|
||||
}
|
||||
|
|
|
@ -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<typeof createMockKbnServer>;
|
||||
|
||||
function updateCsp(config: Partial<ICspConfig>) {
|
||||
kbnServer.newPlatform.setup.core.http.csp = new CspConfig(config);
|
||||
kbnServer.newPlatform.setup.core.http.csp = new CspConfig(createMockEnv(), config);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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'"]],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<string, any>).__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.
|
||||
|
|
|
@ -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<string, any>).__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.
|
||||
|
|
|
@ -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'),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue