diff --git a/.ci/Dockerfile b/.ci/Dockerfile index b2254c8fb1e05..ec7befe05f0d4 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # NOTE: This Dockerfile is ONLY used to run certain tasks in CI. It is not used to run Kibana or as a distributable. # If you're looking for the Kibana Docker image distributable, please see: src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts -ARG NODE_VERSION=12.19.1 +ARG NODE_VERSION=14.15.1 FROM node:${NODE_VERSION} AS base diff --git a/.node-version b/.node-version index e9f788b12771f..2f5ee741e0d77 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -12.19.1 +14.15.1 diff --git a/.nvmrc b/.nvmrc index e9f788b12771f..2f5ee741e0d77 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -12.19.1 +14.15.1 diff --git a/docs/api/using-api.asciidoc b/docs/api/using-api.asciidoc index c796aac3d6b27..d66718be4074a 100644 --- a/docs/api/using-api.asciidoc +++ b/docs/api/using-api.asciidoc @@ -61,7 +61,7 @@ For all APIs, you must use a request header. The {kib} APIs support the `kbn-xsr By default, you must use `kbn-xsrf` for all API calls, except in the following scenarios: * The API endpoint uses the `GET` or `HEAD` operations -* The path is whitelisted using the <> setting +* The path is allowed using the <> setting * XSRF protections are disabled using the <> setting `Content-Type: application/json`:: diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index 01ba084b9e9e7..d9a8d0558714f 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -40,7 +40,7 @@ users interacting with APM APIs must have <> setting +* The path is allowed using the <> setting * XSRF protections are disabled using the <> setting `Content-Type: application/json`:: diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index c22d4466ee09e..3786cbc7d83b6 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -575,10 +575,10 @@ all http requests to https over the port configured as <> setting requires the following format: +The <> setting requires the following format: |=== diff --git a/package.json b/package.json index d5c1f247d87d7..e50b8516d9c29 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "**/@types/hapi__boom": "^7.4.1", "**/@types/hapi__hapi": "^18.2.6", "**/@types/hapi__mimos": "4.1.0", - "**/@types/node": "12.19.4", + "**/@types/node": "14.14.7", "**/cross-fetch/node-fetch": "^2.6.1", "**/deepmerge": "^4.2.2", "**/fast-deep-equal": "^3.1.1", @@ -98,7 +98,7 @@ "**/typescript": "4.1.2" }, "engines": { - "node": "12.19.1", + "node": "14.15.1", "yarn": "^1.21.1" }, "dependencies": { @@ -109,7 +109,7 @@ "@elastic/ems-client": "7.11.0", "@elastic/eui": "30.2.0", "@elastic/filesaver": "1.1.2", - "@elastic/good": "8.1.1-kibana2", + "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/request-crypto": "1.1.4", @@ -496,7 +496,7 @@ "@types/mustache": "^0.8.31", "@types/ncp": "^2.0.1", "@types/nock": "^10.0.3", - "@types/node": "12.19.4", + "@types/node": "14.14.7", "@types/node-fetch": "^2.5.7", "@types/node-forge": "^0.9.5", "@types/nodemailer": "^6.4.0", @@ -525,7 +525,6 @@ "@types/react-resize-detector": "^4.0.1", "@types/react-router": "^5.1.7", "@types/react-router-dom": "^5.1.5", - "@types/react-sticky": "^6.0.3", "@types/react-test-renderer": "^16.9.1", "@types/react-virtualized": "^9.18.7", "@types/read-pkg": "^4.0.0", @@ -722,7 +721,7 @@ "less": "npm:@elastic/less@2.7.3-kibana", "license-checker": "^16.0.0", "listr": "^0.14.1", - "lmdb-store": "^0.8.15", + "lmdb-store": "^0.9.0", "load-grunt-config": "^3.0.1", "loader-utils": "^1.2.3", "log-symbols": "^2.2.0", @@ -782,7 +781,6 @@ "react-router-redux": "^4.0.8", "react-shortcuts": "^2.0.0", "react-sizeme": "^2.3.6", - "react-sticky": "^6.0.3", "react-syntax-highlighter": "^5.7.0", "react-test-renderer": "^16.12.0", "react-tiny-virtual-list": "^2.2.0", @@ -805,7 +803,7 @@ "sass-resources-loader": "^2.0.1", "selenium-webdriver": "^4.0.0-alpha.7", "serve-static": "1.14.1", - "shelljs": "^0.8.3", + "shelljs": "^0.8.4", "simple-git": "1.116.0", "sinon": "^7.4.2", "spawn-sync": "^1.0.15", diff --git a/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap index 4a6d86a0dfba6..5d8fb1e28beb6 100644 --- a/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap +++ b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap @@ -25,8 +25,8 @@ Object { }, "uuid": undefined, "xsrf": Object { + "allowlist": Array [], "disableProtection": false, - "whitelist": Array [], }, } `; @@ -56,8 +56,8 @@ Object { }, "uuid": undefined, "xsrf": Object { + "allowlist": Array [], "disableProtection": false, - "whitelist": Array [], }, } `; diff --git a/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts index 1c51564187442..036ff5e80b3ec 100644 --- a/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts +++ b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts @@ -96,7 +96,7 @@ describe('#get', () => { someNotSupportedValue: 'val', xsrf: { disableProtection: false, - whitelist: [], + allowlist: [], }, }, }); @@ -119,7 +119,7 @@ describe('#get', () => { someNotSupportedValue: 'val', xsrf: { disableProtection: false, - whitelist: [], + allowlist: [], }, }, }); diff --git a/packages/kbn-legacy-logging/src/setup_logging.ts b/packages/kbn-legacy-logging/src/setup_logging.ts index 3b8b4b167c63d..153e7a0f207c1 100644 --- a/packages/kbn-legacy-logging/src/setup_logging.ts +++ b/packages/kbn-legacy-logging/src/setup_logging.ts @@ -18,7 +18,7 @@ */ // @ts-expect-error missing typedef -import good from '@elastic/good'; +import { plugin as good } from '@elastic/good'; import { Server } from '@hapi/hapi'; import { LegacyLoggingConfig } from './schema'; import { getLoggingConfiguration } from './get_logging_config'; diff --git a/packages/kbn-pm/.babelrc b/packages/kbn-pm/.babelrc index 1ca768097a7ee..9ea6ecafe7287 100644 --- a/packages/kbn-pm/.babelrc +++ b/packages/kbn-pm/.babelrc @@ -9,6 +9,7 @@ ], "plugins": [ "@babel/proposal-class-properties", - "@babel/proposal-object-rest-spread" + "@babel/proposal-object-rest-spread", + "@babel/proposal-optional-chaining" ] } diff --git a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts index 35b4b85e4d22a..a1e5b2a363a9d 100644 --- a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts +++ b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts @@ -23,7 +23,7 @@ * tries to mock out simple versions of the Mocha types */ -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; export interface Suite { suites: Suite[]; diff --git a/packages/kbn-utils/src/streams/reduce_stream.test.ts b/packages/kbn-utils/src/streams/reduce_stream.test.ts index e4a7dc1cef491..7d823bb8fe113 100644 --- a/packages/kbn-utils/src/streams/reduce_stream.test.ts +++ b/packages/kbn-utils/src/streams/reduce_stream.test.ts @@ -70,7 +70,7 @@ describe('reduceStream', () => { const errorStub = jest.fn(); reduce$.on('data', dataStub); reduce$.on('error', errorStub); - const endEvent = promiseFromEvent('end', reduce$); + const closeEvent = promiseFromEvent('close', reduce$); reduce$.write(1); reduce$.write(2); @@ -79,7 +79,7 @@ describe('reduceStream', () => { reduce$.write(1000); reduce$.end(); - await endEvent; + await closeEvent; expect(reducer).toHaveBeenCalledTimes(3); expect(dataStub).toHaveBeenCalledTimes(0); expect(errorStub).toHaveBeenCalledTimes(1); diff --git a/src/cli/cluster/cluster.mock.ts b/src/cli/cluster/cluster.mock.ts index 332f8aad53ba1..85d16a79a467c 100644 --- a/src/cli/cluster/cluster.mock.ts +++ b/src/cli/cluster/cluster.mock.ts @@ -19,7 +19,7 @@ /* eslint-env jest */ // eslint-disable-next-line max-classes-per-file -import EventEmitter from 'events'; +import { EventEmitter } from 'events'; import { assign, random } from 'lodash'; import { delay } from 'bluebird'; diff --git a/src/cli/repl/__snapshots__/repl.test.js.snap b/src/cli/repl/__snapshots__/repl.test.js.snap index c7751b5797f49..804898284491d 100644 --- a/src/cli/repl/__snapshots__/repl.test.js.snap +++ b/src/cli/repl/__snapshots__/repl.test.js.snap @@ -1,17 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`repl it allows print depth to be specified 1`] = `"{ '0': { '1': { '2': [Object] } }, whoops: [Circular] }"`; +exports[`repl it allows print depth to be specified 1`] = ` +" { + '0': { '1': { '2': [Object] } }, + whoops: [Circular *1] +}" +`; exports[`repl it colorizes raw values 1`] = `"{ meaning: 42 }"`; exports[`repl it handles deep and recursive objects 1`] = ` -"{ +" { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }" `; @@ -51,13 +56,13 @@ Array [ Array [ "Promise Rejected: ", - "{ + " { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }", ], ] @@ -71,13 +76,13 @@ Array [ Array [ "Promise Resolved: ", - "{ + " { '0': { '1': { '2': { '3': { '4': { '5': [Object] } } } } }, - whoops: [Circular] + whoops: [Circular *1] }", ], ] diff --git a/src/core/public/utils/crypto/sha256.ts b/src/core/public/utils/crypto/sha256.ts index 13e0d405a706b..add93cb75b92a 100644 --- a/src/core/public/utils/crypto/sha256.ts +++ b/src/core/public/utils/crypto/sha256.ts @@ -200,7 +200,7 @@ export class Sha256 { return this; } - digest(encoding: string): string { + digest(encoding: BufferEncoding): string { // Suppose the length of the message M, in bits, is l const l = this._len * 8; diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index 7a69dc2fa726e..c645629fa5653 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -82,12 +82,13 @@ describe('core deprecations', () => { describe('xsrfDeprecation', () => { it('logs a warning if server.xsrf.whitelist is set', () => { - const { messages } = applyCoreDeprecations({ + const { migrated, messages } = applyCoreDeprecations({ server: { xsrf: { whitelist: ['/path'] } }, }); + expect(migrated.server.xsrf.allowlist).toEqual(['/path']); expect(messages).toMatchInlineSnapshot(` Array [ - "It is not recommended to disable xsrf protections for API endpoints via [server.xsrf.whitelist]. It will be removed in 8.0 release. Instead, supply the \\"kbn-xsrf\\" header.", + "\\"server.xsrf.whitelist\\" is deprecated and has been replaced by \\"server.xsrf.allowlist\\"", ] `); }); diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 6c85cfbed8e82..3dde7cfb6c1cb 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -38,16 +38,6 @@ const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { return settings; }; -const xsrfDeprecation: ConfigDeprecation = (settings, fromPath, log) => { - if ((settings.server?.xsrf?.whitelist ?? []).length > 0) { - log( - 'It is not recommended to disable xsrf protections for API endpoints via [server.xsrf.whitelist]. ' + - 'It will be removed in 8.0 release. Instead, supply the "kbn-xsrf" header.' - ); - } - return settings; -}; - const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { if (has(settings, 'server.basePath') && !has(settings, 'server.rewriteBasePath')) { log( @@ -140,10 +130,10 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unu unusedFromRoot('elasticsearch.startupTimeout'), rename('cpu.cgroup.path.override', 'ops.cGroupOverrides.cpuPath'), rename('cpuacct.cgroup.path.override', 'ops.cGroupOverrides.cpuAcctPath'), + rename('server.xsrf.whitelist', 'server.xsrf.allowlist'), configPathDeprecation, dataPathDeprecation, rewriteBasePathDeprecation, cspRulesDeprecation, mapManifestServiceUrlDeprecation, - xsrfDeprecation, ]; diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts index 523256129333f..b1c731e8ba534 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts @@ -99,7 +99,7 @@ const createStartContractMock = () => { }, xsrf: { disableProtection: false, - whitelistConfigured: false, + allowlistConfigured: false, }, }, logging: { diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index e1c78edb902a9..6686a778ee8a5 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -182,8 +182,8 @@ describe('CoreUsageDataService', () => { "truststoreConfigured": false, }, "xsrf": Object { + "allowlistConfigured": false, "disableProtection": false, - "whitelistConfigured": false, }, }, "logging": Object { diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts index f729e23cb68bc..490c411ecb852 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.ts @@ -180,7 +180,7 @@ export class CoreUsageDataService implements CoreService { expect(validated.name).toEqual('kibana-hostname'); }); -test('throws if xsrf.whitelist element does not start with a slash', () => { +test('throws if xsrf.allowlist element does not start with a slash', () => { const httpSchema = config.schema; const obj = { xsrf: { - whitelist: ['/valid-path', 'invalid-path'], + allowlist: ['/valid-path', 'invalid-path'], }, }; expect(() => httpSchema.validate(obj)).toThrowErrorMatchingInlineSnapshot( - `"[xsrf.whitelist.1]: must start with a slash"` + `"[xsrf.allowlist.1]: must start with a slash"` ); }); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 7d41b4ea9e915..be64def294625 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -82,7 +82,7 @@ export const config = { ), xsrf: schema.object({ disableProtection: schema.boolean({ defaultValue: false }), - whitelist: schema.arrayOf( + allowlist: schema.arrayOf( schema.string({ validate: match(/^\//, 'must start with a slash') }), { defaultValue: [] } ), @@ -142,7 +142,7 @@ export class HttpConfig { public ssl: SslConfig; public compression: { enabled: boolean; referrerWhitelist?: string[] }; public csp: ICspConfig; - public xsrf: { disableProtection: boolean; whitelist: string[] }; + public xsrf: { disableProtection: boolean; allowlist: string[] }; public requestId: { allowFromAnyIp: boolean; ipAllowlist: string[] }; /** diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts index a964130550bf5..7df35b04c66cf 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -36,7 +36,7 @@ const actualVersion = pkg.version; const versionHeader = 'kbn-version'; const xsrfHeader = 'kbn-xsrf'; const nameHeader = 'kbn-name'; -const whitelistedTestPath = '/xsrf/test/route/whitelisted'; +const allowlistedTestPath = '/xsrf/test/route/whitelisted'; const xsrfDisabledTestPath = '/xsrf/test/route/disabled'; const kibanaName = 'my-kibana-name'; const setupDeps = { @@ -63,7 +63,7 @@ describe('core lifecycle handlers', () => { customResponseHeaders: { 'some-header': 'some-value', }, - xsrf: { disableProtection: false, whitelist: [whitelistedTestPath] }, + xsrf: { disableProtection: false, allowlist: [allowlistedTestPath] }, requestId: { allowFromAnyIp: true, ipAllowlist: [], @@ -179,7 +179,7 @@ describe('core lifecycle handlers', () => { } ); ((router as any)[method.toLowerCase()] as RouteRegistrar)( - { path: whitelistedTestPath, validate: false }, + { path: allowlistedTestPath, validate: false }, (context, req, res) => { return res.ok({ body: 'ok' }); } @@ -235,7 +235,7 @@ describe('core lifecycle handlers', () => { }); it('accepts whitelisted requests without either an xsrf or version header', async () => { - await getSupertest(method.toLowerCase(), whitelistedTestPath).expect(200, 'ok'); + await getSupertest(method.toLowerCase(), allowlistedTestPath).expect(200, 'ok'); }); it('accepts requests on a route with disabled xsrf protection', async () => { diff --git a/src/core/server/http/lifecycle_handlers.test.ts b/src/core/server/http/lifecycle_handlers.test.ts index fdcf2a173b906..8ad823b3a6944 100644 --- a/src/core/server/http/lifecycle_handlers.test.ts +++ b/src/core/server/http/lifecycle_handlers.test.ts @@ -58,7 +58,7 @@ describe('xsrf post-auth handler', () => { describe('non destructive methods', () => { it('accepts requests without version or xsrf header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'get', headers: {} }); @@ -74,7 +74,7 @@ describe('xsrf post-auth handler', () => { describe('destructive methods', () => { it('accepts requests with xsrf header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: { 'kbn-xsrf': 'xsrf' } }); @@ -88,7 +88,7 @@ describe('xsrf post-auth handler', () => { }); it('accepts requests with version header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: { 'kbn-version': 'some-version' } }); @@ -102,7 +102,7 @@ describe('xsrf post-auth handler', () => { }); it('returns a bad request if called without xsrf or version header', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: false } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post' }); @@ -121,7 +121,7 @@ describe('xsrf post-auth handler', () => { }); it('accepts requests if protection is disabled', () => { - const config = createConfig({ xsrf: { whitelist: [], disableProtection: true } }); + const config = createConfig({ xsrf: { allowlist: [], disableProtection: true } }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: {} }); @@ -134,9 +134,9 @@ describe('xsrf post-auth handler', () => { expect(result).toEqual('next'); }); - it('accepts requests if path is whitelisted', () => { + it('accepts requests if path is allowlisted', () => { const config = createConfig({ - xsrf: { whitelist: ['/some-path'], disableProtection: false }, + xsrf: { allowlist: ['/some-path'], disableProtection: false }, }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ method: 'post', headers: {}, path: '/some-path' }); @@ -152,7 +152,7 @@ describe('xsrf post-auth handler', () => { it('accepts requests if xsrf protection on a route is disabled', () => { const config = createConfig({ - xsrf: { whitelist: [], disableProtection: false }, + xsrf: { allowlist: [], disableProtection: false }, }); const handler = createXsrfPostAuthHandler(config); const request = forgeRequest({ diff --git a/src/core/server/http/lifecycle_handlers.ts b/src/core/server/http/lifecycle_handlers.ts index 7ef7e86326039..4060284b5b56a 100644 --- a/src/core/server/http/lifecycle_handlers.ts +++ b/src/core/server/http/lifecycle_handlers.ts @@ -29,12 +29,12 @@ const XSRF_HEADER = 'kbn-xsrf'; const KIBANA_NAME_HEADER = 'kbn-name'; export const createXsrfPostAuthHandler = (config: HttpConfig): OnPostAuthHandler => { - const { whitelist, disableProtection } = config.xsrf; + const { allowlist, disableProtection } = config.xsrf; return (request, response, toolkit) => { if ( disableProtection || - whitelist.includes(request.route.path) || + allowlist.includes(request.route.path) || request.route.options.xsrfRequired === false ) { return toolkit.next(); diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index 412396644648e..cdcbe513e1224 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -43,7 +43,7 @@ configService.atPath.mockReturnValue( compression: { enabled: true }, xsrf: { disableProtection: true, - whitelist: [], + allowlist: [], }, customResponseHeaders: {}, requestId: { diff --git a/src/core/server/metrics/collectors/process.test.ts b/src/core/server/metrics/collectors/process.test.ts index a437d799371f1..0ce1b9e8e350e 100644 --- a/src/core/server/metrics/collectors/process.test.ts +++ b/src/core/server/metrics/collectors/process.test.ts @@ -62,6 +62,7 @@ describe('ProcessMetricsCollector', () => { heapTotal, heapUsed, external: 0, + arrayBuffers: 0, })); jest.spyOn(v8, 'getHeapStatistics').mockImplementation( diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 36a8d9a52fd52..59f9c4f9ff38c 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -409,7 +409,7 @@ export interface CoreConfigUsageData { }; xsrf: { disableProtection: boolean; - whitelistConfigured: boolean; + allowlistConfigured: boolean; }; requestId: { allowFromAnyIp: boolean; diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index b6eda2dbfd560..0819123138d0f 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -47,12 +47,12 @@ const packages: Package[] = [ extractMethod: 'gunzip', archives: { 'darwin-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/darwin-x64-72.gz', - sha256: '983106049bb86e21b7f823144b2b83e3f1408217401879b3cde0312c803512c9', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/darwin-x64-83.gz', + sha256: 'b45cd8296fd6eb2a091399c20111af43093ba30c99ed9e5d969278f5ff69ba8f', }, 'linux-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/linux-x64-72.gz', - sha256: '8b6692037f7b0df24dabc9c9b039038d1c3a3110f62121616b406c482169710a', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/linux-x64-83.gz', + sha256: '1bbc3f90f0ba105772b37c04e3a718f69544b4df01dda00435c2b8e50b2ad0d9', }, // ARM build is currently done manually as Github Actions used in upstream project @@ -62,16 +62,16 @@ const packages: Package[] = [ // * checkout the node-re2 project, // * install Node using the same minor used by Kibana // * npm install, which will also create a build - // * gzip -c build/Release/re2.node > linux-arm64-72.gz + // * gzip -c build/Release/re2.node > linux-arm64-83.gz // * upload to kibana-ci-proxy-cache bucket 'linux-arm64': { url: - 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.15.4/linux-arm64-72.gz', - sha256: '5942353ec9cf46a39199818d474f7af137cfbb1bc5727047fe22f31f36602a7e', + 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.15.4/linux-arm64-83.gz', + sha256: '4eb524ca9a79dea9c07342e487fbe91591166fdbc022ae987104840df948a4e9', }, 'win32-x64': { - url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/win32-x64-72.gz', - sha256: '0a6991e693577160c3e9a3f196bd2518368c52d920af331a1a183313e0175604', + url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/win32-x64-83.gz', + sha256: 'efe939d3cda1d64ee3ee3e60a20613b95166d55632e702c670763ea7e69fca06', }, }, }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts index 3fd011b0bded2..a514f9f899e55 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts @@ -65,7 +65,7 @@ export function getCoreUsageCollector( }, xsrf: { disableProtection: { type: 'boolean' }, - whitelistConfigured: { type: 'boolean' }, + allowlistConfigured: { type: 'boolean' }, }, requestId: { allowFromAnyIp: { type: 'boolean' }, diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts index 4497e509bc86b..4a3d725de7e47 100644 --- a/src/plugins/kibana_utils/public/state_management/url/format.ts +++ b/src/plugins/kibana_utils/public/state_management/url/format.ts @@ -27,6 +27,9 @@ export function replaceUrlQuery( queryReplacer: (query: ParsedQuery) => ParsedQuery ) { const url = parseUrl(rawUrl); + // @ts-expect-error `queryReplacer` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` const newQuery = queryReplacer(url.query || {}); const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), { sort: false, @@ -45,6 +48,9 @@ export function replaceUrlHashQuery( ) { const url = parseUrl(rawUrl); const hash = parseUrlHash(rawUrl); + // @ts-expect-error `queryReplacer` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` const newQuery = queryReplacer(hash?.query || {}); const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), { sort: false, diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index cb3c9470c7abd..8ec7ad00d7926 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -252,10 +252,16 @@ export function getRelativeToHistoryPath(absoluteUrl: string, history: History): return formatUrl({ pathname: stripBasename(parsedUrl.pathname ?? null), + // @ts-expect-error `urlUtils.encodeQuery` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` search: stringify(urlUtils.encodeQuery(parsedUrl.query), { sort: false, encode: false }), hash: parsedHash ? formatUrl({ pathname: parsedHash.pathname, + // @ts-expect-error `urlUtils.encodeQuery` expects key/value pairs with values of type `string | string[] | null`, + // however `@types/node` says that `url.query` has values of type `string | string[] | undefined`. + // After investigating this, it seems that no matter what the values will be of type `string | string[]` search: stringify(urlUtils.encodeQuery(parsedHash.query), { sort: false, encode: false }), }) : parsedUrl.hash, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 3d79d7c6cf0e1..e1078c60caf2e 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -1391,7 +1391,7 @@ "disableProtection": { "type": "boolean" }, - "whitelistConfigured": { + "allowlistConfigured": { "type": "boolean" } } diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx index 3bf4807877428..2806b8e989ee6 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { History, Location } from 'history'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { StickyContainer } from 'react-sticky'; import styled from 'styled-components'; import { px } from '../../../../../../style/variables'; import { Timeline } from '../../../../../shared/charts/Timeline'; @@ -128,7 +127,7 @@ export function Waterfall({ })} /> )} - +
{renderItems(waterfall.childrenByParentId)} - +
{ ], }; - const wrapper = mountWithTheme( - - - - ); + const wrapper = mountWithTheme(); expect(toJson(wrapper)).toMatchSnapshot(); }); @@ -84,12 +79,7 @@ describe('Timeline', () => { }, }; - const mountTimeline = () => - mountWithTheme( - - - - ); + const mountTimeline = () => mountWithTheme(); expect(mountTimeline).not.toThrow(); }); diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx index dcdfee22e3cfc..904917f2f9792 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx @@ -6,7 +6,6 @@ import React, { ReactNode } from 'react'; import { inRange } from 'lodash'; -import { Sticky } from 'react-sticky'; import { XAxis, XYPlot } from 'react-vis'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { useTheme } from '../../../../hooks/use_theme'; @@ -54,57 +53,51 @@ export function TimelineAxis({ const topTraceDurationFormatted = tickFormatter(topTraceDuration).formatted; return ( - - {({ style }) => { - return ( -
- - tickFormatter(time).formatted} - tickPadding={20} - style={{ - text: { fill: theme.eui.euiColorDarkShade }, - }} - /> +
+ + tickFormatter(time).formatted} + tickPadding={20} + style={{ + text: { fill: theme.eui.euiColorDarkShade }, + }} + /> - {topTraceDuration > 0 && ( - - )} + {topTraceDuration > 0 && ( + + )} - {marks.map((mark) => ( - - ))} - -
- ); - }} - + {marks.map((mark) => ( + + ))} +
+
); } diff --git a/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap index 2756de6e384bc..76e2960e78e9d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap @@ -2,18 +2,11 @@ exports[`Timeline should render with data 1`] = `
-
-
+ } +/> `; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx index fc706aee659a5..563702a143ab3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.test.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; -import { EuiLinkTo } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { IndexingStatusErrors } from './indexing_status_errors'; @@ -17,9 +17,8 @@ describe('IndexingStatusErrors', () => { it('renders', () => { const wrapper = shallow(); - expect(wrapper.find(EuiButton)).toHaveLength(1); expect(wrapper.find(EuiCallOut)).toHaveLength(1); - expect(wrapper.find(EuiLinkTo)).toHaveLength(1); - expect(wrapper.find(EuiLinkTo).prop('to')).toEqual('/path'); + expect(wrapper.find(EuiButtonTo)).toHaveLength(1); + expect(wrapper.find(EuiButtonTo).prop('to')).toEqual('/path'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx index a928400b2338c..2be27299fd77f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_errors.tsx @@ -6,9 +6,9 @@ import React from 'react'; -import { EuiButton, EuiCallOut } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; -import { EuiLinkTo } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { INDEXING_STATUS_HAS_ERRORS_TITLE, INDEXING_STATUS_HAS_ERRORS_BUTTON } from './constants'; @@ -24,8 +24,9 @@ export const IndexingStatusErrors: React.FC = ({ vie data-test-subj="IndexingStatusErrors" >

{INDEXING_STATUS_HAS_ERRORS_TITLE}

- - {INDEXING_STATUS_HAS_ERRORS_BUTTON} - + + + {INDEXING_STATUS_HAS_ERRORS_BUTTON} + ); diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index aa6160bbd8914..05060c9d863aa 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; +import mime from 'mime-types'; +import path from 'path'; import { RequestHandler, ResponseHeaders, KnownHeaders } from 'src/core/server'; import { GetInfoResponse, @@ -43,6 +45,8 @@ import { import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../errors'; import { splitPkgKey } from '../../services/epm/registry'; import { licenseService } from '../../services'; +import { getArchiveEntry } from '../../services/epm/archive/cache'; +import { bufferToStream } from '../../services/epm/streams'; export const getCategoriesHandler: RequestHandler< undefined, @@ -102,22 +106,51 @@ export const getFileHandler: RequestHandler { try { const { pkgName, pkgVersion, filePath } = request.params; - const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); - - const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; + const savedObjectsClient = context.core.savedObjects.client; + const savedObject = await getInstallationObject({ savedObjectsClient, pkgName }); + const pkgInstallSource = savedObject?.attributes.install_source; + // TODO: when package storage is available, remove installSource check and check cache and storage, remove registry call + if (pkgInstallSource === 'upload' && pkgVersion === savedObject?.attributes.version) { + const headerContentType = mime.contentType(path.extname(filePath)); + if (!headerContentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); } - return headers; - }, {} as ResponseHeaders); + const archiveFile = getArchiveEntry(`${pkgName}-${pkgVersion}/${filePath}`); + if (!archiveFile) { + return response.custom({ + body: `uploaded package file not found: ${filePath}`, + statusCode: 404, + }); + } + const headers: ResponseHeaders = { + 'cache-control': 'max-age=10, public', + 'content-type': headerContentType, + }; + return response.custom({ + body: bufferToStream(archiveFile), + statusCode: 200, + headers, + }); + } else { + const registryResponse = await getFile(`/package/${pkgName}/${pkgVersion}/${filePath}`); + const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); - return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: proxiedHeaders, - }); + return response.custom({ + body: registryResponse.body, + statusCode: registryResponse.status, + headers: proxiedHeaders, + }); + } } catch (error) { return defaultIngestErrorHandler({ error, response }); } diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index c10b26cbf0bd1..9b4b26d6fb8b3 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -98,7 +98,10 @@ export async function getPackageInfo(options: { const getPackageRes = await getPackageFromSource({ pkgName, pkgVersion, - pkgInstallSource: savedObject?.attributes.install_source, + pkgInstallSource: + savedObject?.attributes.version === pkgVersion + ? savedObject?.attributes.install_source + : 'registry', }); const paths = getPackageRes.paths; const packageInfo = getPackageRes.packageInfo; diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index a79e5353048c8..f3241b79759a1 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -147,27 +147,23 @@ export class MapsPlugin implements Plugin { return; } - let routesInitialized = false; let isEnterprisePlus = false; + let lastLicenseId: string | undefined; const emsSettings = new EMSSettings(mapsLegacyConfig, () => isEnterprisePlus); licensing.license$.subscribe((license: ILicense) => { - const basic = license.check(APP_ID, 'basic'); - const enterprise = license.check(APP_ID, 'enterprise'); isEnterprisePlus = enterprise.state === 'valid'; - - if (basic.state === 'valid' && !routesInitialized) { - routesInitialized = true; - initRoutes( - core.http.createRouter(), - license.uid, - emsSettings, - this.kibanaVersion, - this._logger - ); - } + lastLicenseId = license.uid; }); + initRoutes( + core.http.createRouter(), + () => lastLicenseId, + emsSettings, + this.kibanaVersion, + this._logger + ); + this._initHomeData(home, core.http.basePath.prepend, emsSettings); features.registerKibanaFeature({ diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index 49d646f9a4e6d..d98259540f5e4 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -52,26 +52,32 @@ const EMPTY_EMS_CLIENT = { addQueryParams() {}, }; -export function initRoutes(router, licenseUid, emsSettings, kbnVersion, logger) { +export function initRoutes(router, getLicenseId, emsSettings, kbnVersion, logger) { let emsClient; - - if (emsSettings.isIncludeElasticMapsService()) { - emsClient = new EMSClient({ - language: i18n.getLocale(), - appVersion: kbnVersion, - appName: EMS_APP_NAME, - fileApiUrl: emsSettings.getEMSFileApiUrl(), - tileApiUrl: emsSettings.getEMSTileApiUrl(), - landingPageUrl: emsSettings.getEMSLandingPageUrl(), - fetchFunction: fetch, - }); - emsClient.addQueryParams({ license: licenseUid }); - } else { - emsClient = EMPTY_EMS_CLIENT; - } + let lastLicenseId; function getEMSClient() { - return emsSettings.isEMSEnabled() ? emsClient : EMPTY_EMS_CLIENT; + const currentLicenseId = getLicenseId(); + if (emsClient && emsSettings.isEMSEnabled() && lastLicenseId === currentLicenseId) { + return emsClient; + } + + lastLicenseId = currentLicenseId; + if (emsSettings.isIncludeElasticMapsService()) { + emsClient = new EMSClient({ + language: i18n.getLocale(), + appVersion: kbnVersion, + appName: EMS_APP_NAME, + fileApiUrl: emsSettings.getEMSFileApiUrl(), + tileApiUrl: emsSettings.getEMSTileApiUrl(), + landingPageUrl: emsSettings.getEMSLandingPageUrl(), + fetchFunction: fetch, + }); + emsClient.addQueryParams({ license: currentLicenseId }); + return emsClient; + } else { + return EMPTY_EMS_CLIENT; + } } router.get( diff --git a/x-pack/plugins/security/public/authentication/login/login_page.tsx b/x-pack/plugins/security/public/authentication/login/login_page.tsx index 35703212762fd..3eff6edef33bc 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.tsx @@ -222,6 +222,7 @@ export class LoginPage extends Component { http={this.props.http} notifications={this.props.notifications} selector={selector} + // @ts-expect-error Map.get is ok with getting `undefined` infoMessage={infoMessageMap.get(query[LOGOUT_REASON_QUERY_STRING_PARAMETER]?.toString())} loginAssistanceMessage={this.props.loginAssistanceMessage} loginHelp={loginHelp} diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index c2c82639bf7d5..11caab837a766 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -118,7 +118,10 @@ const normalizeTrustedAppsPageLocation = ( * @param query * @param key */ -export const extractFirstParamValue = (query: querystring.ParsedUrlQuery, key: string): string => { +export const extractFirstParamValue = ( + query: querystring.ParsedUrlQuery, + key: string +): string | undefined => { const value = query[key]; return Array.isArray(value) ? value[value.length - 1] : value; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 1901f3589104a..05c3ac0faea69 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -186,7 +186,7 @@ export const uiQueryParams: ( typeof query[key] === 'string' ? (query[key] as string) : Array.isArray(query[key]) - ? (query[key][query[key].length - 1] as string) + ? (query[key] as string[])[(query[key] as string[]).length - 1] : undefined; if (value !== undefined) { diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts new file mode 100644 index 0000000000000..5773b88fa2bea --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { Subject } from 'rxjs'; +import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks'; +import { LicenseService } from '../../../../common/license/license'; +import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks'; +import { PolicyWatcher } from './license_watch'; +import { ILicense } from '../../../../../licensing/common/types'; +import { licenseMock } from '../../../../../licensing/common/licensing.mock'; +import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; +import { PackagePolicy } from '../../../../../fleet/common'; +import { createPackagePolicyMock } from '../../../../../fleet/common/mocks'; +import { factory } from '../../../../common/endpoint/models/policy_config'; +import { PolicyConfig } from '../../../../common/endpoint/types'; + +const MockPPWithEndpointPolicy = (cb?: (p: PolicyConfig) => PolicyConfig): PackagePolicy => { + const packagePolicy = createPackagePolicyMock(); + if (!cb) { + // eslint-disable-next-line no-param-reassign + cb = (p) => p; + } + const policyConfig = cb(factory()); + packagePolicy.inputs[0].config = { policy: { value: policyConfig } }; + return packagePolicy; +}; + +describe('Policy-Changing license watcher', () => { + const logger = loggingSystemMock.create().get('license_watch.test'); + const soStartMock = savedObjectsServiceMock.createStartContract(); + let packagePolicySvcMock: jest.Mocked; + + const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); + const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } }); + const Basic = licenseMock.createLicense({ license: { type: 'basic', mode: 'basic' } }); + + beforeEach(() => { + packagePolicySvcMock = createPackagePolicyServiceMock(); + }); + + it('is activated on license changes', () => { + // mock a license-changing service to test reactivity + const licenseEmitter: Subject = new Subject(); + const licenseService = new LicenseService(); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + + // swap out watch function, just to ensure it gets called when a license change happens + const mockWatch = jest.fn(); + pw.watch = mockWatch; + + // licenseService is watching our subject for incoming licenses + licenseService.start(licenseEmitter); + pw.start(licenseService); // and the PolicyWatcher under test, uses that to subscribe as well + + // Enqueue a license change! + licenseEmitter.next(Platinum); + + // policywatcher should have triggered + expect(mockWatch.mock.calls.length).toBe(1); + + pw.stop(); + licenseService.stop(); + licenseEmitter.complete(); + }); + + it('pages through all endpoint policies', async () => { + const TOTAL = 247; + + // set up the mocked package policy service to return and do what we want + packagePolicySvcMock.list + .mockResolvedValueOnce({ + items: Array.from({ length: 100 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 1, + perPage: 100, + }) + .mockResolvedValueOnce({ + items: Array.from({ length: 100 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 2, + perPage: 100, + }) + .mockResolvedValueOnce({ + items: Array.from({ length: TOTAL - 200 }, () => MockPPWithEndpointPolicy()), + total: TOTAL, + page: 3, + perPage: 100, + }); + + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + await pw.watch(Gold); // just manually trigger with a given license + + expect(packagePolicySvcMock.list.mock.calls.length).toBe(3); // should have asked for 3 pages of resuts + + // Assert: on the first call to packagePolicy.list, we asked for page 1 + expect(packagePolicySvcMock.list.mock.calls[0][1].page).toBe(1); + expect(packagePolicySvcMock.list.mock.calls[1][1].page).toBe(2); // second call, asked for page 2 + expect(packagePolicySvcMock.list.mock.calls[2][1].page).toBe(3); // etc + }); + + it('alters no-longer-licensed features', async () => { + const CustomMessage = 'Custom string'; + + // mock a Policy with a higher-tiered feature enabled + packagePolicySvcMock.list.mockResolvedValueOnce({ + items: [ + MockPPWithEndpointPolicy( + (pc: PolicyConfig): PolicyConfig => { + pc.windows.popup.malware.message = CustomMessage; + return pc; + } + ), + ], + total: 1, + page: 1, + perPage: 100, + }); + + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + + // emulate a license change below paid tier + await pw.watch(Basic); + + expect(packagePolicySvcMock.update).toHaveBeenCalled(); + expect( + packagePolicySvcMock.update.mock.calls[0][2].inputs[0].config!.policy.value.windows.popup + .malware.message + ).not.toEqual(CustomMessage); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts new file mode 100644 index 0000000000000..cae3b9f33850a --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts @@ -0,0 +1,116 @@ +/* + * 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 { Subscription } from 'rxjs'; + +import { + KibanaRequest, + Logger, + SavedObjectsClientContract, + SavedObjectsServiceStart, +} from 'src/core/server'; +import { PackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../fleet/common'; +import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; +import { ILicense } from '../../../../../licensing/common/types'; +import { + isEndpointPolicyValidForLicense, + unsetPolicyFeaturesAboveLicenseLevel, +} from '../../../../common/license/policy_config'; +import { isAtLeast, LicenseService } from '../../../../common/license/license'; + +export class PolicyWatcher { + private logger: Logger; + private soClient: SavedObjectsClientContract; + private policyService: PackagePolicyServiceInterface; + private subscription: Subscription | undefined; + constructor( + policyService: PackagePolicyServiceInterface, + soStart: SavedObjectsServiceStart, + logger: Logger + ) { + this.policyService = policyService; + this.soClient = this.makeInternalSOClient(soStart); + this.logger = logger; + } + + /** + * The policy watcher is not called as part of a HTTP request chain, where the + * request-scoped SOClient could be passed down. It is called via license observable + * changes. We are acting as the 'system' in response to license changes, so we are + * intentionally using the system user here. Be very aware of what you are using this + * client to do + */ + private makeInternalSOClient(soStart: SavedObjectsServiceStart): SavedObjectsClientContract { + const fakeRequest = ({ + headers: {}, + getBasePath: () => '', + path: '/', + route: { settings: {} }, + url: { href: {} }, + raw: { req: { url: '/' } }, + } as unknown) as KibanaRequest; + return soStart.getScopedClient(fakeRequest, { excludedWrappers: ['security'] }); + } + + public start(licenseService: LicenseService) { + this.subscription = licenseService.getLicenseInformation$()?.subscribe(this.watch.bind(this)); + } + + public stop() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + public async watch(license: ILicense) { + if (isAtLeast(license, 'platinum')) { + return; + } + + let page = 1; + let response: { + items: PackagePolicy[]; + total: number; + page: number; + perPage: number; + }; + do { + try { + response = await this.policyService.list(this.soClient, { + page: page++, + perPage: 100, + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`, + }); + } catch (e) { + this.logger.warn( + `Unable to verify endpoint policies in line with license change: failed to fetch package policies: ${e.message}` + ); + return; + } + response.items.forEach(async (policy) => { + const policyConfig = policy.inputs[0].config?.policy.value; + if (!isEndpointPolicyValidForLicense(policyConfig, license)) { + policy.inputs[0].config!.policy.value = unsetPolicyFeaturesAboveLicenseLevel( + policyConfig, + license + ); + try { + await this.policyService.update(this.soClient, policy.id, policy); + } catch (e) { + // try again for transient issues + try { + await this.policyService.update(this.soClient, policy.id, policy); + } catch (ee) { + this.logger.warn( + `Unable to remove platinum features from policy ${policy.id}: ${ee.message}` + ); + } + } + } + }); + } while (response.page * response.perPage < response.total); + } +} diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts index 488da5025531d..c230e36e4c896 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts @@ -39,7 +39,7 @@ export const getReadables = (dataPath: string): Promise => const readable = fs.createReadStream(dataPath, { encoding: 'utf-8' }); readable.on('data', (stream) => { - contents.push(stream); + contents.push(stream as string); }); readable.on('end', () => { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 088af40a84ae0..10e817bea0282 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -75,6 +75,7 @@ import { TelemetryPluginSetup, } from '../../../../src/plugins/telemetry/server'; import { licenseService } from './lib/license/license'; +import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; export interface SetupPlugins { alerts: AlertingSetup; @@ -127,6 +128,7 @@ export class Plugin implements IPlugin; + private policyWatcher?: PolicyWatcher; private manifestTask: ManifestTask | undefined; private exceptionsCache: LRU; @@ -370,7 +372,12 @@ export class Plugin implements IPlugin', () => { const { snapshotName } = POLICY_EDIT; // Complete step 1, change snapshot name - form.setInputValue('snapshotNameInput', `${snapshotName}-edited`); + const editedSnapshotName = `${snapshotName}-edited`; + form.setInputValue('snapshotNameInput', editedSnapshotName); actions.clickNextButton(); // Complete step 2, enable ignore unavailable indices switch @@ -143,20 +144,24 @@ describe('', () => { const latestRequest = server.requests[server.requests.length - 1]; + const { name, isManagedPolicy, schedule, repository, retention } = POLICY_EDIT; + const expected = { - ...POLICY_EDIT, - ...{ - config: { - ignoreUnavailable: true, - }, - retention: { - ...POLICY_EDIT.retention, - expireAfterValue: Number(EXPIRE_AFTER_VALUE), - expireAfterUnit: EXPIRE_AFTER_UNIT, - }, - snapshotName: `${POLICY_EDIT.snapshotName}-edited`, + name, + isManagedPolicy, + schedule, + repository, + config: { + ignoreUnavailable: true, + }, + retention: { + ...retention, + expireAfterValue: Number(EXPIRE_AFTER_VALUE), + expireAfterUnit: EXPIRE_AFTER_UNIT, }, + snapshotName: editedSnapshotName, }; + expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected); }); @@ -180,10 +185,25 @@ describe('', () => { const latestRequest = server.requests[server.requests.length - 1]; + const { + name, + isManagedPolicy, + schedule, + repository, + retention, + config, + snapshotName, + } = POLICY_EDIT; + const expected = { - ...POLICY_EDIT, + name, + isManagedPolicy, + schedule, + repository, + config, + snapshotName, retention: { - ...POLICY_EDIT.retention, + ...retention, expireAfterValue: Number(EXPIRE_AFTER_VALUE), expireAfterUnit: TIME_UNITS.DAY, // default value }, diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx index 7af663b29957d..a119c96e0a1ec 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx @@ -64,8 +64,22 @@ export const PolicyEdit: React.FunctionComponent { - if (policyData && policyData.policy) { - setPolicy(policyData.policy); + if (policyData?.policy) { + const { policy: policyToEdit } = policyData; + + // The policy response includes data not pertinent to the form + // that we need to remove, e.g., lastSuccess, lastFailure, stats + const policyFormData: SlmPolicyPayload = { + name: policyToEdit.name, + snapshotName: policyToEdit.snapshotName, + schedule: policyToEdit.schedule, + repository: policyToEdit.repository, + config: policyToEdit.config, + retention: policyToEdit.retention, + isManagedPolicy: policyToEdit.isManagedPolicy, + }; + + setPolicy(policyFormData); } }, [policyData]); diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index e5df0ec33db0b..7a13b4ac27caa 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -26,20 +26,12 @@ const snapshotRetentionSchema = schema.object({ export const policySchema = schema.object({ name: schema.string(), - version: schema.maybe(schema.number()), - modifiedDate: schema.maybe(schema.string()), - modifiedDateMillis: schema.maybe(schema.number()), snapshotName: schema.string(), schedule: schema.string(), repository: schema.string(), - nextExecution: schema.maybe(schema.string()), - nextExecutionMillis: schema.maybe(schema.number()), config: schema.maybe(snapshotConfigSchema), retention: schema.maybe(snapshotRetentionSchema), isManagedPolicy: schema.boolean(), - stats: schema.maybe(schema.object({}, { unknowns: 'allow' })), - lastFailure: schema.maybe(schema.object({}, { unknowns: 'allow' })), - lastSuccess: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); const fsRepositorySettings = schema.object({ diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index cb78e76bdd697..866dd0581b548 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -143,7 +143,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) (pluginDir) => `--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}` ), - `--server.xsrf.whitelist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, + `--server.xsrf.allowlist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl ? [ `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, diff --git a/x-pack/test/api_integration/apis/management/index.js b/x-pack/test/api_integration/apis/management/index.js index 5afb9cfba9f5f..7b6deb0c3892b 100644 --- a/x-pack/test/api_integration/apis/management/index.js +++ b/x-pack/test/api_integration/apis/management/index.js @@ -13,5 +13,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./index_management')); loadTestFile(require.resolve('./index_lifecycle_management')); loadTestFile(require.resolve('./ingest_pipelines')); + loadTestFile(require.resolve('./snapshot_restore')); }); } diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts new file mode 100644 index 0000000000000..f0eea0f960b4b --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Snapshot and Restore', () => { + loadTestFile(require.resolve('./snapshot_restore')); + }); +} diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts new file mode 100644 index 0000000000000..932df405dde12 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts @@ -0,0 +1,89 @@ +/* + * 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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +interface SlmPolicy { + name: string; + snapshotName: string; + schedule: string; + repository: string; + isManagedPolicy: boolean; + config?: { + indices?: string | string[]; + ignoreUnavailable?: boolean; + includeGlobalState?: boolean; + partial?: boolean; + metadata?: Record; + }; + retention?: { + expireAfterValue?: number | ''; + expireAfterUnit?: string; + maxCount?: number | ''; + minCount?: number | ''; + }; +} + +/** + * Helpers to create and delete SLM policies on the Elasticsearch instance + * during our tests. + * @param {ElasticsearchClient} es The Elasticsearch client instance + */ +export const registerEsHelpers = (getService: FtrProviderContext['getService']) => { + let policiesCreated: string[] = []; + + const es = getService('legacyEs'); + + const createRepository = (repoName: string) => { + return es.snapshot.createRepository({ + repository: repoName, + body: { + type: 'fs', + settings: { + location: '/tmp/', + }, + }, + verify: false, + }); + }; + + const createPolicy = (policy: SlmPolicy, cachePolicy?: boolean) => { + if (cachePolicy) { + policiesCreated.push(policy.name); + } + + return es.sr.updatePolicy({ + name: policy.name, + body: policy, + }); + }; + + const getPolicy = (policyName: string) => { + return es.sr.policy({ + name: policyName, + human: true, + }); + }; + + const deletePolicy = (policyName: string) => es.sr.deletePolicy({ name: policyName }); + + const cleanupPolicies = () => + Promise.all(policiesCreated.map(deletePolicy)) + .then(() => { + policiesCreated = []; + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); + }); + + return { + createRepository, + createPolicy, + deletePolicy, + cleanupPolicies, + getPolicy, + }; +}; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts new file mode 100644 index 0000000000000..66ea0fe40c4ce --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/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 { registerEsHelpers } from './elasticsearch'; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts new file mode 100644 index 0000000000000..575da0db2a759 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts @@ -0,0 +1,234 @@ +/* + * 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 expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { registerEsHelpers } from './lib'; + +const API_BASE_PATH = '/api/snapshot_restore'; +const REPO_NAME = 'test_repo'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const { + createRepository, + createPolicy, + deletePolicy, + cleanupPolicies, + getPolicy, + } = registerEsHelpers(getService); + + describe('Snapshot Lifecycle Management', function () { + before(async () => { + try { + await createRepository(REPO_NAME); + } catch (err) { + // eslint-disable-next-line no-console + console.log('[Setup error] Error creating repository'); + throw err; + } + }); + + after(async () => { + await cleanupPolicies(); + }); + + describe('Create', () => { + const POLICY_NAME = 'test_create_policy'; + const REQUIRED_FIELDS_POLICY_NAME = 'test_create_required_fields_policy'; + + after(async () => { + // Clean up any policies created in test cases + await Promise.all([POLICY_NAME, REQUIRED_FIELDS_POLICY_NAME].map(deletePolicy)).catch( + (err) => { + // eslint-disable-next-line no-console + console.log(`[Cleanup error] Error deleting policies: ${err.message}`); + throw err; + } + ); + }); + + it('should create a SLM policy', async () => { + const { body } = await supertest + .post(`${API_BASE_PATH}/policies`) + .set('kbn-xsrf', 'xxx') + .send({ + name: POLICY_NAME, + snapshotName: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignoreUnavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expireAfterValue: 1, + expireAfterUnit: 'd', + maxCount: 10, + minCount: 5, + }, + isManagedPolicy: false, + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignore_unavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expire_after: '1d', + max_count: 10, + min_count: 5, + }, + }); + }); + + it('should create a policy with only required fields', async () => { + const { body } = await supertest + .post(`${API_BASE_PATH}/policies`) + .set('kbn-xsrf', 'xxx') + // Exclude config and retention + .send({ + name: REQUIRED_FIELDS_POLICY_NAME, + snapshotName: 'my_snapshot', + repository: REPO_NAME, + schedule: '0 30 1 * * ?', + isManagedPolicy: false, + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(REQUIRED_FIELDS_POLICY_NAME); + expect(policyFromEs[REQUIRED_FIELDS_POLICY_NAME]).to.be.ok(); + expect(policyFromEs[REQUIRED_FIELDS_POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + repository: REPO_NAME, + schedule: '0 30 1 * * ?', + }); + }); + }); + + describe('Update', () => { + const POLICY_NAME = 'test_update_policy'; + const POLICY = { + name: POLICY_NAME, + snapshotName: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignoreUnavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expireAfterValue: 1, + expireAfterUnit: 'd', + maxCount: 10, + minCount: 5, + }, + isManagedPolicy: false, + }; + + before(async () => { + // Create SLM policy that can be used to test PUT request + try { + await createPolicy(POLICY, true); + } catch (err) { + // eslint-disable-next-line no-console + console.log('[Setup error] Error creating policy'); + throw err; + } + }); + + it('should allow an existing policy to be updated', async () => { + const uri = `${API_BASE_PATH}/policies/${POLICY_NAME}`; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send({ + ...POLICY, + schedule: '0 0 0 ? * 7', + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 0 0 ? * 7', + repository: REPO_NAME, + config: { + indices: ['my_index'], + ignore_unavailable: true, + partial: false, + metadata: { + meta: 'my_meta', + }, + }, + retention: { + expire_after: '1d', + max_count: 10, + min_count: 5, + }, + }); + }); + + it('should allow optional fields to be removed', async () => { + const uri = `${API_BASE_PATH}/policies/${POLICY_NAME}`; + const { retention, config, ...requiredFields } = POLICY; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send(requiredFields) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + + const policyFromEs = await getPolicy(POLICY_NAME); + expect(policyFromEs[POLICY_NAME]).to.be.ok(); + expect(policyFromEs[POLICY_NAME].policy).to.eql({ + name: 'my_snapshot', + schedule: '0 30 1 * * ?', + repository: REPO_NAME, + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/services/legacy_es.js b/x-pack/test/api_integration/services/legacy_es.js index 04b991151034a..c184a87365977 100644 --- a/x-pack/test/api_integration/services/legacy_es.js +++ b/x-pack/test/api_integration/services/legacy_es.js @@ -10,6 +10,7 @@ import * as legacyElasticsearch from 'elasticsearch'; import { elasticsearchClientPlugin as securityEsClientPlugin } from '../../../plugins/security/server/elasticsearch/elasticsearch_client_plugin'; import { elasticsearchJsPlugin as indexManagementEsClientPlugin } from '../../../plugins/index_management/server/client/elasticsearch'; +import { elasticsearchJsPlugin as snapshotRestoreEsClientPlugin } from '../../../plugins/snapshot_restore/server/client/elasticsearch_sr'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config'; @@ -20,6 +21,6 @@ export function LegacyEsProvider({ getService }) { apiVersion: DEFAULT_API_VERSION, host: formatUrl(config.get('servers.elasticsearch')), requestTimeout: config.get('timeouts.esRequestTimeout'), - plugins: [securityEsClientPlugin, indexManagementEsClientPlugin], + plugins: [securityEsClientPlugin, indexManagementEsClientPlugin, snapshotRestoreEsClientPlugin], }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/file.ts b/x-pack/test/fleet_api_integration/apis/epm/file.ts index ab89fceeb5b49..2823b236c0321 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/file.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/file.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import fs from 'fs'; +import path from 'path'; +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { warnAndSkipTest } from '../../helpers'; @@ -14,79 +17,175 @@ export default function ({ getService }: FtrProviderContext) { const server = dockerServers.get('registry'); describe('EPM - package file', () => { - it('fetches a .png screenshot image', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/img/screenshots/metricbeat_dashboard.png') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'image/png') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + describe('it gets files from registry', () => { + it('fetches a .png screenshot image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/img/screenshots/metricbeat_dashboard.png') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/png') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches an .svg icon image', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/img/logo.svg') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'image/svg+xml') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches an .svg icon image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/img/logo.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json kibana visualization file', async function () { - if (server.enabled) { - await supertest - .get( - '/api/fleet/epm/packages/filetest/0.1.0/kibana/visualization/sample_visualization.json' - ) - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches a .json kibana visualization file', async function () { + if (server.enabled) { + const res = await supertest + .get( + '/api/fleet/epm/packages/filetest/0.1.0/kibana/visualization/sample_visualization.json' + ) + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json kibana dashboard file', async function () { - if (server.enabled) { - await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/dashboard/sample_dashboard.json') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200); - } else { - warnAndSkipTest(this, log); - } - }); + it('fetches a .json kibana dashboard file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/dashboard/sample_dashboard.json') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); - it('fetches a .json search file', async function () { - if (server.enabled) { + it('fetches a .json search file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/search/sample_search.json') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); + }); + describe('it gets files from an uploaded package', () => { + before(async () => { + if (!server.enabled) return; + const testPkgArchiveTgz = path.join( + path.dirname(__filename), + '../fixtures/direct_upload_packages/apache_0.1.4.tar.gz' + ); + const buf = fs.readFileSync(testPkgArchiveTgz); await supertest - .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/search/sample_search.json') - .set('kbn-xsrf', 'xxx') - .expect('Content-Type', 'application/json; charset=utf-8') + .post(`/api/fleet/epm/packages`) + .set('kbn-xsrf', 'xxxx') + .type('application/gzip') + .send(buf) .expect(200); - } else { - warnAndSkipTest(this, log); - } + }); + after(async () => { + if (!server.enabled) return; + await supertest.delete(`/api/fleet/epm/packages/apache-0.1.4`).set('kbn-xsrf', 'xxxx'); + }); + it('fetches a .png screenshot image', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/kibana-apache-test.png') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/png') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); + it('fetches the logo', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/logo_apache_test.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/img/logo_apache.svg') + .set('kbn-xsrf', 'xxx') + .expect(404); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches a .json kibana dashboard file', async function () { + if (server.enabled) { + const res = await supertest + .get( + '/api/fleet/epm/packages/apache/0.1.4/kibana/dashboard/apache-Logs-Apache-Dashboard-ecs-new.json' + ) + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + expect(typeof res.body).to.equal('object'); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches a README file', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.4/docs/README.md') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'text/markdown; charset=utf-8') + .expect(200); + expect(res.text).to.equal('# Apache Uploaded Test Integration'); + } else { + warnAndSkipTest(this, log); + } + }); + + it('fetches the logo of a not uploaded (and installed) version from the registry when another version is uploaded (and installed)', async function () { + if (server.enabled) { + const res = await supertest + .get('/api/fleet/epm/packages/apache/0.1.3/img/logo_apache.svg') + .set('kbn-xsrf', 'xxx') + .expect('Content-Type', 'image/svg+xml') + .expect(200); + expect(Buffer.isBuffer(res.body)).to.equal(true); + } else { + warnAndSkipTest(this, log); + } + }); }); - }); - // Disabled for now as we don't serve prebuilt index patterns in current packages. - // it('fetches an .json index pattern file', async function () { - // if (server.enabled) { - // await supertest - // .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/index-pattern/sample-*.json') - // .set('kbn-xsrf', 'xxx') - // .expect('Content-Type', 'application/json; charset=utf-8') - // .expect(200); - // } else { - // warnAndSkipTest(this, log); - // } - // }); + // Disabled for now as we don't serve prebuilt index patterns in current packages. + // it('fetches an .json index pattern file', async function () { + // if (server.enabled) { + // await supertest + // .get('/api/fleet/epm/packages/filetest/0.1.0/kibana/index-pattern/sample-*.json') + // .set('kbn-xsrf', 'xxx') + // .expect('Content-Type', 'application/json; charset=utf-8') + // .expect(200); + // } else { + // warnAndSkipTest(this, log); + // } + // }); + }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/get.ts b/x-pack/test/fleet_api_integration/apis/epm/get.ts index 53982affa128c..a6be50804aa5e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/get.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/get.ts @@ -71,6 +71,25 @@ export default function (providerContext: FtrProviderContext) { warnAndSkipTest(this, log); } }); + it('returns correct package info from registry if a different version is installed by upload', async function () { + if (server.enabled) { + const buf = fs.readFileSync(testPkgArchiveZip); + await supertest + .post(`/api/fleet/epm/packages`) + .set('kbn-xsrf', 'xxxx') + .type('application/zip') + .send(buf) + .expect(200); + + const res = await supertest.get(`/api/fleet/epm/packages/apache-0.1.3`).expect(200); + const packageInfo = res.body.response; + expect(packageInfo.description).to.equal('Apache Integration'); + expect(packageInfo.download).to.not.equal(undefined); + await uninstallPackage(testPkgKey); + } else { + warnAndSkipTest(this, log); + } + }); it('returns a 500 for a package key without a proper name', async function () { if (server.enabled) { await supertest.get('/api/fleet/epm/packages/-0.1.0').expect(500); diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz b/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz index c5d3607e05cb8..4dbd2f223d506 100644 Binary files a/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz and b/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz differ diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.zip b/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.zip index 6a8a12b2f2d49..a1c396370a937 100644 Binary files a/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.zip and b/x-pack/test/fleet_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.zip differ diff --git a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts index b2eb645c8372c..b4cd9c361778f 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts @@ -4,13 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { promisify } from 'bluebird'; -import fs from 'fs'; +import { promises as fs } from 'fs'; import path from 'path'; import { comparePngs } from '../../../../../../../test/functional/services/lib/compare_pngs'; -const mkdirAsync = promisify(fs.mkdir); - export async function checkIfPngsMatch( actualpngPath: string, baselinepngPath: string, @@ -23,8 +20,8 @@ export async function checkIfPngsMatch( const sessionDirectoryPath = path.resolve(screenshotsDirectory, 'session'); const failureDirectoryPath = path.resolve(screenshotsDirectory, 'failure'); - await mkdirAsync(sessionDirectoryPath, { recursive: true }); - await mkdirAsync(failureDirectoryPath, { recursive: true }); + await fs.mkdir(sessionDirectoryPath, { recursive: true }); + await fs.mkdir(failureDirectoryPath, { recursive: true }); const actualpngFileName = path.basename(actualpngPath, '.png'); const baselinepngFileName = path.basename(baselinepngPath, '.png'); @@ -39,14 +36,14 @@ export async function checkIfPngsMatch( // don't want to start causing failures for other devs working on OS's which are lacking snapshots. We have // mac and linux covered which is better than nothing for now. try { - log.debug(`writeFileSync: ${baselineCopyPath}`); - fs.writeFileSync(baselineCopyPath, fs.readFileSync(baselinepngPath)); + log.debug(`writeFile: ${baselineCopyPath}`); + await fs.writeFile(baselineCopyPath, await fs.readFile(baselinepngPath)); } catch (error) { log.error(`No baseline png found at ${baselinepngPath}`); return 0; } - log.debug(`writeFileSync: ${actualCopyPath}`); - fs.writeFileSync(actualCopyPath, fs.readFileSync(actualpngPath)); + log.debug(`writeFile: ${actualCopyPath}`); + await fs.writeFile(actualCopyPath, await fs.readFile(actualpngPath)); let diffTotal = 0; diff --git a/yarn.lock b/yarn.lock index 28f032ef7122f..73741371d10c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1489,15 +1489,14 @@ async-retry "^1.2.3" strip-ansi "^5.2.0" -"@elastic/good@8.1.1-kibana2": - version "8.1.1-kibana2" - resolved "https://registry.yarnpkg.com/@elastic/good/-/good-8.1.1-kibana2.tgz#3ba7413da9fae4c67f128f3e9b1dc28f24523c7a" - integrity sha512-2AYmQMBjmh2896FePnnGr9nwoqRxZ6bTjregDRI0CB9r4sIpIzG6J7oMa0GztdDMlrk5CslX1g9SN5EihddPlw== +"@elastic/good@^9.0.1-kibana3": + version "9.0.1-kibana3" + resolved "https://registry.yarnpkg.com/@elastic/good/-/good-9.0.1-kibana3.tgz#a70c2b30cbb4f44d1cf4a464562e0680322eac9b" + integrity sha512-UtPKr0TmlkL1abJfO7eEVUTqXWzLKjMkz+65FvxU/Ub9kMAr4No8wHLRfDHFzBkWoDIbDWygwld011WzUnea1Q== dependencies: - hoek "5.x.x" - joi "13.x.x" - oppsy "2.x.x" - pumpify "1.3.x" + "@hapi/hoek" "9.x.x" + "@hapi/oppsy" "3.x.x" + "@hapi/validate" "1.x.x" "@elastic/makelogs@^6.0.0": version "6.0.0" @@ -1847,7 +1846,7 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== -"@hapi/hoek@^9.0.0": +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0": version "9.1.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== @@ -1912,6 +1911,13 @@ "@hapi/hoek" "8.x.x" "@hapi/vise" "3.x.x" +"@hapi/oppsy@3.x.x": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@hapi/oppsy/-/oppsy-3.0.0.tgz#1ae397e200e86d0aa41055f103238ed8652947ca" + integrity sha512-0kfUEAqIi21GzFVK2snMO07znMEBiXb+/pOx1dmgOO9TuvFstcfmHU5i56aDfiFP2DM5WzQCU2UWc2gK1lMDhQ== + dependencies: + "@hapi/hoek" "9.x.x" + "@hapi/pez@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@hapi/pez/-/pez-4.1.2.tgz#14984d0c31fed348f10c962968a21d9761f55503" @@ -2002,6 +2008,14 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@hapi/validate@1.x.x": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@hapi/validate/-/validate-1.1.3.tgz#f750a07283929e09b51aa16be34affb44e1931ad" + integrity sha512-/XMR0N0wjw0Twzq2pQOzPBZlDzkekGcoCtzO314BpIEsbXdYGthQUbxgkGDf4nhk1+IPDAsXqWjMohRQYO06UA== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@hapi/vise@3.x.x": version "3.1.1" resolved "https://registry.yarnpkg.com/@hapi/vise/-/vise-3.1.1.tgz#dfc88f2ac90682f48bdc1b3f9b8f1eab4eabe0c8" @@ -5262,10 +5276,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@12.19.4", "@types/node@8.10.54", "@types/node@>= 8", "@types/node@>=8.9.0", "@types/node@^12.0.2": - version "12.19.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" - integrity sha512-o3oj1bETk8kBwzz1WlO6JWL/AfAA3Vm6J1B3C9CsdxHYp7XgPiH7OEXPUbZTndHlRaIElrANkQfe6ZmfJb3H2w== +"@types/node@*", "@types/node@14.14.7", "@types/node@8.10.54", "@types/node@>= 8", "@types/node@>=8.9.0", "@types/node@^12.0.2": + version "14.14.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" + integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== "@types/nodemailer@^6.4.0": version "6.4.0" @@ -5523,13 +5537,6 @@ "@types/history" "*" "@types/react" "*" -"@types/react-sticky@^6.0.3": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/react-sticky/-/react-sticky-6.0.3.tgz#94d16a951467b29ad44c224081d9503e7e590434" - integrity sha512-tW0Y1hTr2Tao4yX58iKl0i7BaqrdObGXAzsyzd8VGVrWVEgbQuV6P6QKVd/kFC7FroXyelftiVNJ09pnfkcjww== - dependencies: - "@types/react" "*" - "@types/react-syntax-highlighter@11.0.4": version "11.0.4" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" @@ -18818,14 +18825,14 @@ lmdb-store-0.9@0.7.3: node-gyp-build "^4.2.3" weak-lru-cache "^0.3.9" -lmdb-store@^0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.8.15.tgz#4efb0341c2df505dd6f3a7f26f834f0a142a80a2" - integrity sha512-4Q0WZh2FmcJC6esZRUWMfkCmNiz0WU9cOgrxt97ZMTnVfHyOdZhtrt0oOF5EQPfetxxJf/BorKY28aX92R6G6g== +lmdb-store@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.9.0.tgz#9a07735baaabcb8a46ee08c58ce1d578b69bdc12" + integrity sha512-5yxZ/s2J4w5mq3II5w2i4EiAAT+RvGZ3dtiWPYQDV/F8BpwqZOi7QmHdwawf15stvXv9P92Rm7t2WPbjOV9Xkg== dependencies: fs-extra "^9.0.1" lmdb-store-0.9 "0.7.3" - msgpackr "^0.5.4" + msgpackr "^0.6.0" nan "^2.14.1" node-gyp-build "^4.2.3" weak-lru-cache "^0.3.9" @@ -20344,21 +20351,28 @@ ms@2.1.1, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -msgpackr-extract@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.5.tgz#0f206da058bd3dad0f8605d324de001a8f4de967" - integrity sha512-zHhstybu+m/j3H6CVBMcILVIzATK6dWRGtlePJjsnSAj8kLT5joMa9i0v21Uc80BPNDcwFsnG/dz2318tfI81w== +msgpackr-extract@^0.3.5, msgpackr-extract@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.6.tgz#f20c0a278e44377471b1fa2a3a75a32c87693755" + integrity sha512-ASUrKn0MEFp2onn+xUBQhCNR6+RzzQAcs6p0RqKQ9sfqOZjzQ21a+ASyzgh+QAJrKcWBiZLN6L4+iXKPJV6pXg== dependencies: nan "^2.14.1" node-gyp-build "^4.2.3" -msgpackr@^0.5.3, msgpackr@^0.5.4: +msgpackr@^0.5.3: version "0.5.4" resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.5.4.tgz#c21c03d5e132d2e54d0b9ced02a75b1f48413380" integrity sha512-ILEWtIWwd5ESWHKoVjJ4GP7JWkpuAUJ20qi2j2qEC6twecBmK4E6YG3QW847OpmvdAhMJGq2LoDJRn/kNERTeQ== optionalDependencies: msgpackr-extract "^0.3.5" +msgpackr@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.6.0.tgz#57f75f80247ed3bcb937b7b5b0c7ef48123bee80" + integrity sha512-GF+hXvh1mn9f43ndEigmyTwomeJ/5OQWaxJTMeFrXouGTCYvzEtnF7Bd1DTCxOHXO85BeWFgUVA7Ev61R2KkVw== + optionalDependencies: + msgpackr-extract "^0.3.6" + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -21311,7 +21325,7 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" -oppsy@2.x.x, oppsy@^2.0.0: +oppsy@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/oppsy/-/oppsy-2.0.0.tgz#3a194517adc24c3c61cdc56f35f4537e93a35e34" integrity sha1-OhlFF63CTDxhzcVvNfRTfpOjXjQ= @@ -22732,7 +22746,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@1.3.x, pumpify@^1.3.3, pumpify@^1.3.5: +pumpify@^1.3.3, pumpify@^1.3.5: version "1.3.6" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.6.tgz#00d40e5ded0a3bf1e0788b1c0cf426a42882ab64" integrity sha512-BurGAcvezsINL5US9T9wGHHcLNrG6MCp//ECtxron3vcR+Rfx5Anqq7HbZXNJvFQli8FGVsWCAvywEJFV5Hx/Q== @@ -22871,7 +22885,7 @@ raf-schd@^4.0.0, raf-schd@^4.0.2: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== -raf@^3.1.0, raf@^3.3.0, raf@^3.4.1: +raf@^3.1.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -23549,14 +23563,6 @@ react-sizeme@^2.6.7: shallowequal "^1.1.0" throttle-debounce "^2.1.0" -react-sticky@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/react-sticky/-/react-sticky-6.0.3.tgz#7a18b643e1863da113d7f7036118d2a75d9ecde4" - integrity sha512-LNH4UJlRatOqo29/VHxDZOf6fwbgfgcHO4mkEFvrie5FuaZCSTGtug5R8NGqJ0kSnX8gHw8qZN37FcvnFBJpTQ== - dependencies: - prop-types "^15.5.8" - raf "^3.3.0" - react-style-singleton@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.0.tgz#7396885332e9729957f9df51f08cadbfc164e1c4" @@ -25422,10 +25428,10 @@ shelljs@^0.6.0: resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg= -shelljs@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" - integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== +shelljs@^0.8.3, shelljs@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" interpret "^1.0.0"