Skip to content

Commit

Permalink
Move CSP options to new platform
Browse files Browse the repository at this point in the history
  • Loading branch information
eliperelman committed Dec 10, 2019
1 parent 48d897e commit 02d335a
Show file tree
Hide file tree
Showing 20 changed files with 230 additions and 60 deletions.
13 changes: 13 additions & 0 deletions docs/development/core/server/kibana-plugin-server.csp_options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CSP\_OPTIONS](./kibana-plugin-server.csp_options.md)

## CSP\_OPTIONS variable

The default set of CSP options used for Kibana.

<b>Signature:</b>

```typescript
CSP_OPTIONS: Readonly<CspOptions>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CspOptions](./kibana-plugin-server.cspoptions.md) &gt; [directives](./kibana-plugin-server.cspoptions.directives.md)

## CspOptions.directives property

The CSP rules in a formatted directives string for use in a Content-Security-Policy header

<b>Signature:</b>

```typescript
directives: string;
```
23 changes: 23 additions & 0 deletions docs/development/core/server/kibana-plugin-server.cspoptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CspOptions](./kibana-plugin-server.cspoptions.md)

## CspOptions interface

The CSP options used for Kibana.

<b>Signature:</b>

```typescript
export interface CspOptions
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [directives](./kibana-plugin-server.cspoptions.directives.md) | <code>string</code> | The CSP rules in a formatted directives string for use in a Content-Security-Policy header |
| [rules](./kibana-plugin-server.cspoptions.rules.md) | <code>string[]</code> | The CSP rules used for Kibana. |
| [strict](./kibana-plugin-server.cspoptions.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.cspoptions.warnlegacybrowsers.md) | <code>boolean</code> | Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance. |

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CspOptions](./kibana-plugin-server.cspoptions.md) &gt; [rules](./kibana-plugin-server.cspoptions.rules.md)

## CspOptions.rules property

The CSP rules used for Kibana.

<b>Signature:</b>

```typescript
rules: string[];
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CspOptions](./kibana-plugin-server.cspoptions.md) &gt; [strict](./kibana-plugin-server.cspoptions.strict.md)

## CspOptions.strict property

Specify whether browsers that do not support CSP should be able to use Kibana. Use `true` to block and `false` to allow.

<b>Signature:</b>

```typescript
strict: boolean;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CspOptions](./kibana-plugin-server.cspoptions.md) &gt; [warnLegacyBrowsers](./kibana-plugin-server.cspoptions.warnlegacybrowsers.md)

## CspOptions.warnLegacyBrowsers property

Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance.

<b>Signature:</b>

```typescript
warnLegacyBrowsers: boolean;
```
2 changes: 2 additions & 0 deletions docs/development/core/server/kibana-plugin-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ContextSetup](./kibana-plugin-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. |
| [CoreSetup](./kibana-plugin-server.coresetup.md) | Context passed to the plugins <code>setup</code> method. |
| [CoreStart](./kibana-plugin-server.corestart.md) | Context passed to the plugins <code>start</code> method. |
| [CspOptions](./kibana-plugin-server.cspoptions.md) | The CSP options used for Kibana. |
| [CustomHttpResponseOptions](./kibana-plugin-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. |
| [DeprecationAPIClientParams](./kibana-plugin-server.deprecationapiclientparams.md) | |
| [DeprecationAPIResponse](./kibana-plugin-server.deprecationapiresponse.md) | |
Expand Down Expand Up @@ -140,6 +141,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->

| Variable | Description |
| --- | --- |
| [CSP\_OPTIONS](./kibana-plugin-server.csp_options.md) | The default set of CSP options used for Kibana. |
| [kibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) | Set of helpers used to create <code>KibanaResponse</code> to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-server.requesthandler.md) execution. |
| [validBodyOutput](./kibana-plugin-server.validbodyoutput.md) | The set of valid body.output |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@
* under the License.
*/

import {
createCSPRuleString,
DEFAULT_CSP_RULES,
DEFAULT_CSP_STRICT,
DEFAULT_CSP_WARN_LEGACY_BROWSERS,
} from './';
import { createCspDirectives, CSP_OPTIONS } from './csp';

// 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,
// we test the default rules exactly so any change to those rules gets flagged
// for manual review. In otherwords, this test is intentionally fragile to draw
// for manual review. In other words, this test is intentionally fragile to draw
// extra attention if defaults are modified in any way.
//
// A test failure here does not necessarily mean this change cannot be made,
Expand All @@ -37,25 +32,28 @@ import {
// The tests use inline snapshots to make it as easy as possible to identify
// the nature of a change in defaults during a PR review.
test('default CSP rules', () => {
expect(DEFAULT_CSP_RULES).toMatchInlineSnapshot(`
Array [
"script-src 'unsafe-eval' 'self'",
"worker-src blob: 'self'",
"style-src 'unsafe-inline' 'self'",
]
expect(CSP_OPTIONS).toMatchInlineSnapshot(`
Object {
"directives": "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('CSP strict mode defaults to disabled', () => {
expect(DEFAULT_CSP_STRICT).toBe(true);
});

test('CSP legacy browser warning defaults to enabled', () => {
expect(DEFAULT_CSP_WARN_LEGACY_BROWSERS).toBe(true);
});

test('createCSPRuleString() converts an array of rules into a CSP header string', () => {
const csp = createCSPRuleString([`string-src 'self'`, 'worker-src blob:', 'img-src data: blob:']);
test('createCspDirectives() converts an array of rules into a CSP header string', () => {
const directives = createCspDirectives([
`string-src 'self'`,
'worker-src blob:',
'img-src data: blob:',
]);

expect(csp).toMatchInlineSnapshot(`"string-src 'self'; worker-src blob:; img-src data: blob:"`);
expect(directives).toMatchInlineSnapshot(
`"string-src 'self'; worker-src blob:; img-src data: blob:"`
);
});
87 changes: 87 additions & 0 deletions src/core/server/csp/csp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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 { TypeOf, schema } from '@kbn/config-schema';

/**
* The CSP options used for Kibana.
* @public
*/
export interface CspOptions {
/**
* The CSP rules in a formatted directives string for use
* in a `Content-Security-Policy` header.
*/
directives: string;

/**
* The CSP rules used for Kibana.
*/
rules: string[];

/**
* Specify whether browsers that do not support CSP should be
* able to use Kibana. Use `true` to block and `false` to allow.
*/
strict: boolean;

/**
* Specify whether users with legacy browsers should be warned
* about their lack of Kibana security compliance.
*/
warnLegacyBrowsers: boolean;
}

export type CspConfigType = TypeOf<typeof config.schema>;

const DEFAULT_RULES = [
`script-src 'unsafe-eval' 'self'`,
`worker-src blob: 'self'`,
`style-src 'unsafe-inline' 'self'`,
];
const defaults = {
directives: createCspDirectives(DEFAULT_RULES),
rules: DEFAULT_RULES,
strict: true,
warnLegacyBrowsers: true,
};
export const config = {
path: 'csp',
schema: schema.object({
directives: schema.string({ defaultValue: defaults.directives }),
rules: schema.arrayOf(schema.string(), { defaultValue: defaults.rules }),
strict: schema.boolean({ defaultValue: defaults.strict }),
warnLegacyBrowsers: schema.boolean({ defaultValue: defaults.warnLegacyBrowsers }),
}),
};

/**
* The default set of CSP options used for Kibana.
* @public
*/
export const CSP_OPTIONS: Readonly<CspOptions> = Object.freeze(defaults);

/**
* Converts an array of rules into a formatted directives string for use
* in a `Content-Security-Policy` header.
* @internal
*/
export function createCspDirectives(rules: string[]) {
return rules.join('; ');
}
14 changes: 1 addition & 13 deletions src/legacy/server/csp/index.ts → src/core/server/csp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,4 @@
* under the License.
*/

export const DEFAULT_CSP_RULES = Object.freeze([
`script-src 'unsafe-eval' 'self'`,
`worker-src blob: 'self'`,
`style-src 'unsafe-inline' 'self'`,
]);

export const DEFAULT_CSP_STRICT = true;

export const DEFAULT_CSP_WARN_LEGACY_BROWSERS = true;

export function createCSPRuleString(rules: string[]) {
return rules.join('; ');
}
export * from './csp';
1 change: 1 addition & 0 deletions src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export {
HandlerParameters,
} from './context';
export { CoreId } from './core_context';
export { CspOptions, CSP_OPTIONS, createCspDirectives } from './csp';
export {
ClusterClient,
IClusterClient,
Expand Down
14 changes: 14 additions & 0 deletions src/core/server/server.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,20 @@ export interface CoreStart {
savedObjects: SavedObjectsServiceStart;
}

// @internal
export function createCspDirectives(rules: string[]): string;

// @public
export const CSP_OPTIONS: Readonly<CspOptions>;

// @public
export interface CspOptions {
directives: string;
rules: string[];
strict: boolean;
warnLegacyBrowsers: boolean;
}

// @public
export interface CustomHttpResponseOptions<T extends HttpResponsePayload | ResponseError> {
body?: T;
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { UiSettingsService } from './ui_settings';
import { PluginsService, config as pluginsConfig } from './plugins';
import { SavedObjectsService } from '../server/saved_objects';

import { config as cspConfig } from './csp';
import { config as elasticsearchConfig } from './elasticsearch';
import { config as httpConfig } from './http';
import { config as loggingConfig } from './logging';
Expand Down Expand Up @@ -210,6 +211,7 @@ export class Server {
public async setupConfigSchemas() {
const schemas: Array<[ConfigPath, Type<unknown>]> = [
[pathConfig.path, pathConfig.schema],
[cspConfig.path, cspConfig.schema],
[elasticsearchConfig.path, elasticsearchConfig.schema],
[loggingConfig.path, loggingConfig.schema],
[httpConfig.path, httpConfig.schema],
Expand Down
1 change: 1 addition & 0 deletions src/core/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export { PluginOpaqueId } from './plugins/types';
export * from './saved_objects/types';
export * from './ui_settings/types';
export { EnvironmentMode, PackageInfo } from './config/types';
export { CspOptions } from './csp';
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import sinon from 'sinon';
import { Server } from 'hapi';
import { DEFAULT_CSP_RULES } from '../../../../../server/csp';
import { CSP_OPTIONS } from '../../../../../../core/server';
import { createCspCollector } from './csp_collector';

interface MockConfig {
Expand Down Expand Up @@ -85,7 +85,7 @@ test('does not arbitrarily fetch other csp configurations (e.g. whitelist only)'

function setupCollector() {
const mockConfig = { get: sinon.stub() };
mockConfig.get.withArgs('csp.rules').returns(DEFAULT_CSP_RULES);
mockConfig.get.withArgs('csp.rules').returns(CSP_OPTIONS.rules);
mockConfig.get.withArgs('csp.strict').returns(true);
mockConfig.get.withArgs('csp.warnLegacyBrowsers').returns(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import { Server } from 'hapi';
import { createCSPRuleString, DEFAULT_CSP_RULES } from '../../../../../server/csp';
import { createCspDirectives, CSP_OPTIONS } from '../../../../../../core/server';
import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';

export function createCspCollector(server: Server) {
Expand All @@ -31,8 +31,8 @@ export function createCspCollector(server: Server) {
// It's important that we do not send the value of csp.rules here as it
// can be customized with values that can be identifiable to given
// installs, such as URLs
const defaultRulesString = createCSPRuleString([...DEFAULT_CSP_RULES]);
const actualRulesString = createCSPRuleString(config.get('csp.rules'));
const defaultRulesString = createCspDirectives([...CSP_OPTIONS.rules]);
const actualRulesString = createCspDirectives(config.get('csp.rules'));

return {
strict: config.get('csp.strict'),
Expand Down
11 changes: 1 addition & 10 deletions src/legacy/server/config/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ import os from 'os';
import { join } from 'path';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getDataPath } from '../../../core/server/path'; // Still used by optimize config schema
import {
DEFAULT_CSP_RULES,
DEFAULT_CSP_STRICT,
DEFAULT_CSP_WARN_LEGACY_BROWSERS,
} from '../csp';

const HANDLED_IN_NEW_PLATFORM = Joi.any().description('This key is handled in the new platform ONLY');
export default () => Joi.object({
Expand All @@ -52,11 +47,7 @@ export default () => Joi.object({
exclusive: Joi.boolean().default(false)
}).default(),

csp: Joi.object({
rules: Joi.array().items(Joi.string()).default(DEFAULT_CSP_RULES),
strict: Joi.boolean().default(DEFAULT_CSP_STRICT),
warnLegacyBrowsers: Joi.boolean().default(DEFAULT_CSP_WARN_LEGACY_BROWSERS),
}).default(),
csp: HANDLED_IN_NEW_PLATFORM,

cpu: Joi.object({
cgroup: Joi.object({
Expand Down
Loading

0 comments on commit 02d335a

Please sign in to comment.