diff --git a/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.test.ts b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.test.ts new file mode 100644 index 0000000000000..caea9fd49fed6 --- /dev/null +++ b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import sinon from 'sinon'; +import { DEFAULT_CSP_RULES } from '../../../../../../../../src/legacy/server/csp'; +import { + getMockCallWithInternal, + getMockKbnServer, + getMockTaskFetch, +} from '../../../../test_utils'; +import { createCspCollector } from './csp_collector'; + +test('fetches whether strict mode is enabled', async () => { + const { collector, mockConfig } = setupCollector(); + + expect((await collector.fetch()).strict).toEqual(true); + + mockConfig.get.withArgs('csp.strict').returns(false); + expect((await collector.fetch()).strict).toEqual(false); +}); + +test('fetches whether the legacy browser warning is enabled', async () => { + const { collector, mockConfig } = setupCollector(); + + expect((await collector.fetch()).warnLegacyBrowsers).toEqual(true); + + mockConfig.get.withArgs('csp.warnLegacyBrowsers').returns(false); + expect((await collector.fetch()).warnLegacyBrowsers).toEqual(false); +}); + +test('fetches whether the csp rules have been changed or not', async () => { + const { collector, mockConfig } = setupCollector(); + + expect((await collector.fetch()).rulesChangedFromDefault).toEqual(false); + + mockConfig.get.withArgs('csp.rules').returns(['not', 'default']); + expect((await collector.fetch()).rulesChangedFromDefault).toEqual(true); +}); + +test('does not include raw csp.rules under any property names', async () => { + const { collector } = setupCollector(); + + // 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 + // + // We use a snapshot here to ensure csp.rules isn't finding its way into the + // payload under some new and unexpected variable name (e.g. cspRules). + expect(await collector.fetch()).toMatchInlineSnapshot(` + Object { + "rulesChangedFromDefault": false, + "strict": true, + "warnLegacyBrowsers": true, + } + `); +}); + +test('does not arbitrarily fetch other csp configurations (e.g. whitelist only)', async () => { + const { collector, mockConfig } = setupCollector(); + + mockConfig.get.withArgs('csp.foo').returns('bar'); + + expect(await collector.fetch()).not.toHaveProperty('foo'); +}); + +function setupCollector() { + const mockConfig = { get: sinon.stub() }; + mockConfig.get.withArgs('csp.rules').returns(DEFAULT_CSP_RULES); + mockConfig.get.withArgs('csp.strict').returns(true); + mockConfig.get.withArgs('csp.warnLegacyBrowsers').returns(true); + + const mockKbnServer = getMockKbnServer(getMockCallWithInternal(), getMockTaskFetch(), mockConfig); + + return { mockConfig, collector: createCspCollector(mockKbnServer) }; +} diff --git a/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.ts b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.ts new file mode 100644 index 0000000000000..3bacde22bdbc4 --- /dev/null +++ b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/csp_collector.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + createCSPRuleString, + DEFAULT_CSP_RULES, +} from '../../../../../../../../src/legacy/server/csp'; +import { HapiServer } from '../../../../'; + +export function createCspCollector(server: HapiServer) { + return { + type: 'csp', + isReady: () => true, + async fetch() { + const config = server.config(); + + // 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')); + + return { + strict: config.get('csp.strict'), + warnLegacyBrowsers: config.get('csp.warnLegacyBrowsers'), + rulesChangedFromDefault: defaultRulesString !== actualRulesString, + }; + }, + }; +} + +export function registerCspCollector(server: HapiServer): void { + const { usage } = server; + const collector = usage.collectorSet.makeUsageCollector(createCspCollector(server)); + usage.collectorSet.register(collector); +} diff --git a/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/index.ts b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/index.ts new file mode 100644 index 0000000000000..a7c1088f9961b --- /dev/null +++ b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/csp/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerCspCollector } from './csp_collector'; diff --git a/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/index.ts b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/index.ts index 8b825b13178f2..abe96fe061faa 100644 --- a/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/index.ts +++ b/x-pack/legacy/plugins/oss_telemetry/server/lib/collectors/index.ts @@ -5,8 +5,10 @@ */ import { HapiServer } from '../../../'; +import { registerCspCollector } from './csp'; import { registerVisualizationsCollector } from './visualizations/register_usage_collector'; export function registerCollectors(server: HapiServer) { registerVisualizationsCollector(server); + registerCspCollector(server); } diff --git a/x-pack/legacy/plugins/oss_telemetry/test_utils/index.ts b/x-pack/legacy/plugins/oss_telemetry/test_utils/index.ts index 7659f0d3516f9..7168f598dca23 100644 --- a/x-pack/legacy/plugins/oss_telemetry/test_utils/index.ts +++ b/x-pack/legacy/plugins/oss_telemetry/test_utils/index.ts @@ -30,9 +30,16 @@ export const getMockTaskFetch = (docs: TaskInstance[] = defaultMockTaskDocs) => return () => Promise.resolve({ docs }); }; +export const getMockConfig = () => { + return { + get: () => '', + }; +}; + export const getMockKbnServer = ( mockCallWithInternal = getMockCallWithInternal(), - mockTaskFetch = getMockTaskFetch() + mockTaskFetch = getMockTaskFetch(), + mockConfig = getMockConfig() ): HapiServer => ({ plugins: { elasticsearch: { @@ -53,6 +60,6 @@ export const getMockKbnServer = ( register: () => undefined, }, }, - config: () => ({ get: () => '' }), + config: () => mockConfig, log: () => undefined, });