(req: Request, routeSchemas?: RouteValidator | RouteValidatorFullConfig
, withoutSecretHeaders?: boolean): KibanaRequest
;
readonly headers: Headers;
+ readonly isSystemRequest: boolean;
// (undocumented)
readonly params: Params;
// (undocumented)
diff --git a/src/legacy/core_plugins/data/public/search/fetch/types.ts b/src/legacy/core_plugins/data/public/search/fetch/types.ts
index fba14119d83c3..c7f32cf08aa94 100644
--- a/src/legacy/core_plugins/data/public/search/fetch/types.ts
+++ b/src/legacy/core_plugins/data/public/search/fetch/types.ts
@@ -19,16 +19,6 @@
import { ISearchStart } from 'src/plugins/data/public';
import { IUiSettingsClient } from '../../../../../../core/public';
-import { SearchRequest, SearchResponse } from '../types';
-
-export interface ApiCaller {
- search: (searchRequest: SearchRequest) => ApiCallerResponse;
- msearch: (searchRequest: SearchRequest) => ApiCallerResponse;
-}
-
-export interface ApiCallerResponse extends Promise {
- abort: () => void;
-}
export interface FetchOptions {
abortSignal?: AbortSignal;
diff --git a/src/legacy/core_plugins/data/public/search/search_service.ts b/src/legacy/core_plugins/data/public/search/search_service.ts
index 85701187fb31d..77203da9ce585 100644
--- a/src/legacy/core_plugins/data/public/search/search_service.ts
+++ b/src/legacy/core_plugins/data/public/search/search_service.ts
@@ -19,7 +19,7 @@
import { Plugin, CoreSetup, CoreStart } from '../../../../../core/public';
import { SearchSource } from './search_source';
-import { defaultSearchStrategy } from './search_strategy';
+import { defaultSearchStrategy, addSearchStrategy } from './search_strategy';
import { SearchStrategyProvider } from './search_strategy/types';
export interface SearchSetup {} // eslint-disable-line @typescript-eslint/no-empty-interface
@@ -41,6 +41,8 @@ export class SearchService implements Plugin {
}
public start(core: CoreStart): SearchStart {
+ addSearchStrategy(defaultSearchStrategy);
+
return {
defaultSearchStrategy,
SearchSource,
diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
index 1cefabe08c2d5..fea834686eb4f 100644
--- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts
@@ -31,8 +31,6 @@ import { EventsProvider } from 'ui/events';
import { PersistedState } from 'ui/persisted_state';
// @ts-ignore
import { PromiseServiceCreator } from 'ui/promises/promises';
-// @ts-ignore
-import { createEsService } from 'ui/es';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
// @ts-ignore
import { PrivateProvider } from 'ui/private/private';
@@ -66,7 +64,7 @@ import { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
// @ts-ignore
import { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav';
import { configureAppAngularModule } from 'ui/legacy_compat';
-import { IndexPatterns } from '../../../../../plugins/data/public';
+import { IndexPatterns, DataPublicPluginStart } from '../../../../../plugins/data/public';
import { Storage } from '../../../../../plugins/kibana_utils/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
import { createDocTableDirective } from './np_ready/angular/doc_table/doc_table';
@@ -94,7 +92,7 @@ import { DiscoverStartPlugins } from './plugin';
* needs to render, so in the end the current 'kibana' angular module is no longer necessary
*/
export function getInnerAngularModule(name: string, core: CoreStart, deps: DiscoverStartPlugins) {
- const module = initializeInnerAngularModule(name, core, deps.navigation);
+ const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data);
configureAppAngularModule(module, core as LegacyCoreStart, true);
return module;
}
@@ -107,7 +105,7 @@ export function getInnerAngularModuleEmbeddable(
core: CoreStart,
deps: DiscoverStartPlugins
) {
- const module = initializeInnerAngularModule(name, core, deps.navigation, true);
+ const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data, true);
configureAppAngularModule(module, core as LegacyCoreStart, true);
return module;
}
@@ -118,6 +116,7 @@ export function initializeInnerAngularModule(
name = 'app/discover',
core: CoreStart,
navigation: NavigationStart,
+ data: DataPublicPluginStart,
embeddable = false
) {
if (!initialized) {
@@ -131,7 +130,7 @@ export function initializeInnerAngularModule(
createLocalGlobalStateModule();
createLocalAppStateModule();
createLocalStorageModule();
- createElasticSearchModule();
+ createElasticSearchModule(data);
createIndexPatternsModule();
createPagerFactoryModule();
createDocTableModule();
@@ -163,7 +162,6 @@ export function initializeInnerAngularModule(
'ngRoute',
'react',
'ui.bootstrap',
- 'elasticsearch',
'discoverConfig',
'discoverI18n',
'discoverPrivate',
@@ -298,11 +296,13 @@ const createLocalStorageService = function(type: string) {
};
};
-function createElasticSearchModule() {
+function createElasticSearchModule(data: DataPublicPluginStart) {
angular
- .module('discoverEs', ['elasticsearch', 'discoverConfig'])
+ .module('discoverEs', ['discoverConfig'])
// Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy
- .service('es', createEsService);
+ .service('es', () => {
+ return data.search.__LEGACY.esClient;
+ });
}
function createIndexPatternsModule() {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
index 754fda2a6fe5a..d1087b4575e82 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
@@ -17,7 +17,6 @@
* under the License.
*/
-import { SavedObjectsClientProvider } from 'ui/saved_objects';
import uiRoutes from 'ui/routes';
import angularTemplate from './angular_template.html';
import { npStart } from 'ui/new_platform';
@@ -32,18 +31,17 @@ uiRoutes.when('/management/kibana/index_pattern', {
controller: function($scope, $injector) {
// Wait for the directives to execute
const kbnUrl = $injector.get('kbnUrl');
- const Private = $injector.get('Private');
$scope.$$postDigest(() => {
const $routeParams = $injector.get('$routeParams');
const indexPatternCreationType = managementSetup.indexPattern.creation.getType(
$routeParams.type
);
const services = {
- config: $injector.get('config'),
- es: $injector.get('es'),
+ config: npStart.core.uiSettings,
+ es: npStart.plugins.data.search.__LEGACY.esClient,
indexPatterns: npStart.plugins.data.indexPatterns,
- $http: $injector.get('$http'),
- savedObjectsClient: Private(SavedObjectsClientProvider),
+ $http: npStart.core.http,
+ savedObjectsClient: npStart.core.savedObjects.client,
indexPatternCreationType,
confirmModalPromise: $injector.get('confirmModalPromise'),
changeUrl: url => {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js
index a663e9d626f88..8ab26f8c0d1c8 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js
@@ -26,7 +26,7 @@ import { uiModules } from 'ui/modules';
import indexTemplate from './index.html';
import indexPatternListTemplate from './list.html';
import { IndexPatternTable } from './index_pattern_table';
-import { SavedObjectsClientProvider } from 'ui/saved_objects';
+import { npStart } from 'ui/new_platform';
import {
FeatureCatalogueRegistryProvider,
FeatureCatalogueCategory,
@@ -67,8 +67,8 @@ export const destroyIndexPatternList = () => {
};
const indexPatternsResolutions = {
- indexPatterns: function(Private) {
- const savedObjectsClient = Private(SavedObjectsClientProvider);
+ indexPatterns: function() {
+ const savedObjectsClient = npStart.core.savedObjects.client;
return savedObjectsClient
.find({
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js
index 6d3980d9d53f9..c9698f6e1f48b 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_objects.js
@@ -21,7 +21,6 @@ import { savedObjectManagementRegistry } from '../../saved_object_registry';
import objectIndexHTML from './_objects.html';
import uiRoutes from 'ui/routes';
import chrome from 'ui/chrome';
-import { SavedObjectsClientProvider } from 'ui/saved_objects';
import { uiModules } from 'ui/modules';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
@@ -35,14 +34,13 @@ import { getIndexBreadcrumbs } from './breadcrumbs';
const REACT_OBJECTS_TABLE_DOM_ELEMENT_ID = 'reactSavedObjectsTable';
function updateObjectsTable($scope, $injector) {
- const Private = $injector.get('Private');
const indexPatterns = npStart.plugins.data.indexPatterns;
const $http = $injector.get('$http');
const kbnUrl = $injector.get('kbnUrl');
const config = $injector.get('config');
const confirmModalPromise = $injector.get('confirmModalPromise');
- const savedObjectsClient = Private(SavedObjectsClientProvider);
+ const savedObjectsClient = npStart.core.savedObjects.client;
const services = savedObjectManagementRegistry.all().map(obj => $injector.get(obj.service));
const uiCapabilites = npStart.core.application.capabilities;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js
index f7e654fd3c76d..540f25c63a861 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js
@@ -28,9 +28,9 @@ import uiRoutes from 'ui/routes';
import { uiModules } from 'ui/modules';
import { fatalError, toastNotifications } from 'ui/notify';
import 'ui/accessibility/kbn_ui_ace_keyboard_mode';
-import { SavedObjectsClientProvider } from 'ui/saved_objects';
import { isNumeric } from './lib/numeric';
import { canViewInApp } from './lib/in_app_url';
+import { npStart } from 'ui/new_platform';
import { castEsToKbnFieldTypeName } from '../../../../../../../plugins/data/public';
@@ -56,12 +56,11 @@ uiModules
$location,
$window,
$rootScope,
- Private,
uiCapabilities
) {
const serviceObj = savedObjectManagementRegistry.get($routeParams.service);
const service = $injector.get(serviceObj.service);
- const savedObjectsClient = Private(SavedObjectsClientProvider);
+ const savedObjectsClient = npStart.core.savedObjects.client;
/**
* Creates a field definition and pushes it to the memo stack. This function
@@ -177,11 +176,13 @@ uiModules
// sorts twice since we want numerical sort to prioritize over name,
// and sortBy will do string comparison if trying to match against strings
const nameSortedFields = _.sortBy(fields, 'name');
- $scope.fields = _.sortBy(nameSortedFields, field => {
- const orderIndex = service.Class.fieldOrder
- ? service.Class.fieldOrder.indexOf(field.name)
- : -1;
- return orderIndex > -1 ? orderIndex : Infinity;
+ $scope.$evalAsync(() => {
+ $scope.fields = _.sortBy(nameSortedFields, field => {
+ const orderIndex = service.Class.fieldOrder
+ ? service.Class.fieldOrder.indexOf(field.name)
+ : -1;
+ return orderIndex > -1 ? orderIndex : Infinity;
+ });
});
})
.catch(error => fatalError(error, location));
diff --git a/src/legacy/core_plugins/kibana/server/lib/system_api.js b/src/legacy/core_plugins/kibana/server/lib/system_api.js
index 24c901a3db0a4..3e2ab667dd98b 100644
--- a/src/legacy/core_plugins/kibana/server/lib/system_api.js
+++ b/src/legacy/core_plugins/kibana/server/lib/system_api.js
@@ -24,6 +24,7 @@ const SYSTEM_API_HEADER_NAME = 'kbn-system-api';
*
* @param request HAPI request object
* @return true if request is a system API request; false, otherwise
+ * @deprecated Use KibanaRequest#isSystemApi
*/
export function isSystemApiRequest(request) {
return !!request.headers[SYSTEM_API_HEADER_NAME];
diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
index 0c12b3d928815..60f1bed35b518 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
+++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js
@@ -43,6 +43,7 @@ import { SearchCache } from '../data_model/search_cache';
import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy';
import { createVegaTypeDefinition } from '../vega_type';
+import { npStart } from 'ui/new_platform';
const THRESHOLD = 0.1;
const PIXEL_DIFF = 30;
@@ -60,10 +61,9 @@ describe('VegaVisualizations', () => {
beforeEach(
ngMock.inject((Private, $injector) => {
vegaVisualizationDependencies = {
- es: $injector.get('es'),
serviceSettings: $injector.get('serviceSettings'),
core: {
- uiSettings: $injector.get('config'),
+ uiSettings: npStart.core.uiSettings,
},
plugins: {
data: {
@@ -72,6 +72,9 @@ describe('VegaVisualizations', () => {
timefilter: {},
},
},
+ __LEGACY: {
+ esClient: npStart.plugins.data.search.__LEGACY.esClient,
+ },
},
},
};
diff --git a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts
index af4425e3d5548..5cf65d62a6aed 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/shim/legacy_dependencies_plugin.ts
@@ -19,12 +19,10 @@
import chrome from 'ui/chrome';
import 'ui/vis/map/service_settings';
-import 'ui/es'; // required for $injector.get('es') below
import { CoreStart, Plugin } from 'kibana/public';
/** @internal */
export interface LegacyDependenciesPluginSetup {
- es: any;
serviceSettings: any;
}
@@ -34,9 +32,6 @@ export class LegacyDependenciesPlugin
const $injector = await chrome.dangerouslyGetActiveInjector();
return {
- // Client of Elastic Search.
- es: $injector.get('es'),
-
// Settings for EMSClient.
// EMSClient, which currently lives in the tile_map vis,
// will probably end up being exposed from the future vis_type_maps plugin,
diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
index 089cc3215d87d..576786567a6f9 100644
--- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
+++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts
@@ -16,6 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
+
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getSearchService } from '../../../../plugins/data/public/services';
import { esFilters, esQuery, TimeRange, Query } from '../../../../plugins/data/public';
// @ts-ignore
@@ -36,13 +39,13 @@ interface VegaRequestHandlerParams {
}
export function createVegaRequestHandler({
- es,
- plugins,
+ plugins: { data },
core: { uiSettings },
serviceSettings,
}: VegaVisualizationDependencies) {
- const searchCache = new SearchCache(es, { max: 10, maxAge: 4 * 1000 });
- const { timefilter } = plugins.data.query.timefilter;
+ const { esClient } = getSearchService().__LEGACY;
+ const searchCache = new SearchCache(esClient, { max: 10, maxAge: 4 * 1000 });
+ const { timefilter } = data.query.timefilter;
const timeCache = new TimeCache(timefilter, 3 * 1000);
return ({ timeRange, filters, query, visParams }: VegaRequestHandlerParams) => {
diff --git a/src/legacy/ui/public/autoload/modules.js b/src/legacy/ui/public/autoload/modules.js
index e1d897236297e..938796ed279ea 100644
--- a/src/legacy/ui/public/autoload/modules.js
+++ b/src/legacy/ui/public/autoload/modules.js
@@ -20,7 +20,6 @@
import 'angular';
import '../chrome';
import '../config';
-import '../es';
import '../notify';
import '../private';
import '../promises';
diff --git a/src/legacy/ui/public/es.js b/src/legacy/ui/public/es.js
deleted file mode 100644
index c734717b82438..0000000000000
--- a/src/legacy/ui/public/es.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * @name es
- *
- * @description This is the result of calling esFactory. esFactory is exposed by the
- * elasticsearch.angular.js client.
- */
-
-import 'elasticsearch-browser';
-import _ from 'lodash';
-import { uiModules } from './modules';
-
-const plugins = [
- function(Client, config) {
- // esFactory automatically injects the AngularConnector to the config
- // https://github.com/elastic/elasticsearch-js/blob/master/src/lib/connectors/angular.js
- class CustomAngularConnector extends config.connectionClass {
- request = _.wrap(this.request, function(request, params, cb) {
- if (String(params.method).toUpperCase() === 'GET') {
- params.query = _.defaults({ _: Date.now() }, params.query);
- }
-
- return request.call(this, params, cb);
- });
- }
-
- config.connectionClass = CustomAngularConnector;
- },
-];
-
-export function createEsService(esFactory, esUrl, esApiVersion, esRequestTimeout) {
- return esFactory({
- host: esUrl,
- log: 'info',
- requestTimeout: esRequestTimeout,
- apiVersion: esApiVersion,
- plugins,
- });
-}
-
-uiModules
- .get('kibana', ['elasticsearch', 'kibana/config'])
- //Elasticsearch client used for requesting data. Connects to the /elasticsearch proxy
- .service('es', createEsService);
diff --git a/src/legacy/ui/public/kfetch/kfetch.ts b/src/legacy/ui/public/kfetch/kfetch.ts
index cb96e03eb1328..02be7a32db296 100644
--- a/src/legacy/ui/public/kfetch/kfetch.ts
+++ b/src/legacy/ui/public/kfetch/kfetch.ts
@@ -32,6 +32,7 @@ export interface KFetchQuery {
export interface KFetchOptions extends HttpRequestInit {
pathname: string;
query?: KFetchQuery;
+ asSystemRequest?: boolean;
}
export interface KFetchKibanaOptions {
diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
index dfd26bc4be039..6f4b20c425fc9 100644
--- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
+++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
@@ -102,6 +102,12 @@ export const npSetup = {
getSavedQueryCount: sinon.fake(),
},
},
+ __LEGACY: {
+ esClient: {
+ search: sinon.fake(),
+ msearch: sinon.fake(),
+ },
+ },
fieldFormats: getFieldFormatsRegistry(mockCore),
},
share: {
diff --git a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js
index d9622ac3dc6d2..ace87a15f7b58 100644
--- a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js
+++ b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js
@@ -34,7 +34,6 @@ describe('Saved Object', function() {
require('test_utils/no_digest_promises').activateForSuite();
let SavedObject;
- let esDataStub;
let savedObjectsClientStub;
let window;
@@ -90,10 +89,6 @@ describe('Saved Object', function() {
obj[fName].restore && obj[fName].restore();
}
- const mock409FetchError = {
- res: { status: 409 },
- };
-
beforeEach(
ngMock.module(
'kibana',
@@ -108,10 +103,9 @@ describe('Saved Object', function() {
);
beforeEach(
- ngMock.inject(function(es, $window) {
+ ngMock.inject(function($window) {
savedObjectsClientStub = npStart.core.savedObjects.client;
SavedObject = createSavedObjectClass({ savedObjectsClient: savedObjectsClientStub });
- esDataStub = es;
window = $window;
})
);
@@ -130,7 +124,6 @@ describe('Saved Object', function() {
describe('with confirmOverwrite', function() {
function stubConfirmOverwrite() {
window.confirm = sinon.stub().returns(true);
- sinon.stub(esDataStub, 'create').returns(Bluebird.reject(mock409FetchError));
}
it('when false does not request overwrite', function() {
diff --git a/src/legacy/ui/public/saved_objects/index.ts b/src/legacy/ui/public/saved_objects/index.ts
index 129938ebe0509..03c5a64fdd45e 100644
--- a/src/legacy/ui/public/saved_objects/index.ts
+++ b/src/legacy/ui/public/saved_objects/index.ts
@@ -17,6 +17,5 @@
* under the License.
*/
-export { SavedObjectsClientProvider } from './saved_objects_client_provider';
export { SavedObjectLoader } from './saved_object_loader';
export { findObjectByTitle } from './helpers/find_object_by_title';
diff --git a/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts b/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts
deleted file mode 100644
index 0375eb21c19f0..0000000000000
--- a/src/legacy/ui/public/saved_objects/saved_objects_client_provider.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { SavedObjectsClient } from 'src/core/public';
-import chrome from '../chrome';
-import { PromiseService } from '../promises';
-
-type Args any> = T extends (...args: infer X) => any ? X : never;
-
-// Provide an angular wrapper around savedObjectClient so all actions get resolved in an Angular Promise
-// If you do not need the promise to execute in an angular digest cycle then you should not use this
-// and get savedObjectClient directly from chrome.
-export function SavedObjectsClientProvider(Promise: PromiseService) {
- const savedObjectsClient = chrome.getSavedObjectsClient();
-
- return {
- create: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.create(...args));
- },
- bulkCreate: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.bulkCreate(...args));
- },
- delete: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.delete(...args));
- },
- find: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.find(...args));
- },
- get: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.get(...args));
- },
- bulkGet: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.bulkGet(...args));
- },
- update: (...args: Args) => {
- return Promise.resolve(savedObjectsClient.update(...args));
- },
- };
-}
diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js
index a62ebdc25ca2c..1cb23d2ad2a23 100644
--- a/src/legacy/ui/ui_exports/ui_export_defaults.js
+++ b/src/legacy/ui/ui_exports/ui_export_defaults.js
@@ -47,6 +47,5 @@ export const UI_EXPORT_DEFAULTS = {
appExtensions: {
fieldFormatEditors: ['ui/field_editor/components/field_format_editor/register'],
- search: ['ui/courier/search_strategy/default_search_strategy'],
},
};
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index f44d40f533eed..0f1c9eeb9d11a 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -59,6 +59,12 @@ const createSetupContract = (): Setup => {
search: searchSetupMock,
fieldFormats: fieldFormatsMock as FieldFormatsSetup,
query: querySetupMock,
+ __LEGACY: {
+ esClient: {
+ search: jest.fn(),
+ msearch: jest.fn(),
+ },
+ },
};
return setupContract;
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index ce8ca0317bd7d..2077e899d1a01 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -76,7 +76,7 @@ export class DataPublicPlugin implements Plugin LegacyApiCallerResponse;
+ msearch: (searchRequest: SearchRequest) => LegacyApiCallerResponse;
+}
-export { defaultSearchStrategy };
+interface LegacyApiCallerResponse extends Promise {
+ abort: () => void;
+}
diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts
index d36202debd9b9..7d62b3823771d 100644
--- a/src/plugins/data/public/search/index.ts
+++ b/src/plugins/data/public/search/index.ts
@@ -40,3 +40,5 @@ export { SYNC_SEARCH_STRATEGY } from './sync_search_strategy';
export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search';
export { ISearchStart } from './search_service';
+
+export { LegacyApiCaller } from './es_client';
diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts
index 69e05e8df48e2..9ed02e1d61018 100644
--- a/src/plugins/data/public/search/search_service.test.ts
+++ b/src/plugins/data/public/search/search_service.test.ts
@@ -33,7 +33,9 @@ describe('Search service', () => {
describe('setup()', () => {
it('exposes proper contract', async () => {
- const setup = searchService.setup(mockCoreSetup);
+ const setup = searchService.setup(mockCoreSetup, {
+ version: '8',
+ } as any);
expect(setup).toHaveProperty('registerSearchStrategyContext');
expect(setup).toHaveProperty('registerSearchStrategyProvider');
});
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index 6f3e228939d6d..dad2b3d078aa0 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -39,7 +39,7 @@ import {
import { TStrategyTypes } from './strategy_types';
import { esSearchService } from './es_search';
import { ISearchGeneric } from './i_search';
-import { getEsClient } from './es_client';
+import { getEsClient, LegacyApiCaller } from './es_client';
/**
* Extends the AppMountContext so other plugins have access
@@ -54,7 +54,7 @@ declare module 'kibana/public' {
export interface ISearchStart {
search: ISearchGeneric;
__LEGACY: {
- esClient: any;
+ esClient: LegacyApiCaller;
};
}
@@ -78,13 +78,13 @@ export class SearchService implements Plugin {
* Exposes context to the search strategies.
*/
private contextContainer?: IContextContainer>;
-
+ private esClient?: LegacyApiCaller;
private search?: ISearchGeneric;
private readonly loadingCount$ = new BehaviorSubject(0);
constructor(private initializerContext: PluginInitializerContext) {}
- public setup(core: CoreSetup): ISearchSetup {
+ public setup(core: CoreSetup, packageInfo: PackageInfo): ISearchSetup {
core.http.addLoadingCountSource(this.loadingCount$);
const search = (this.search = createAppMountSearchContext(
this.searchStrategies,
@@ -95,6 +95,7 @@ export class SearchService implements Plugin {
});
this.contextContainer = core.context.createContextContainer();
+ this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo, this.loadingCount$);
const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = <
T extends TStrategyTypes
@@ -109,6 +110,9 @@ export class SearchService implements Plugin {
const api = {
registerSearchStrategyContext: this.contextContainer!.registerContext,
registerSearchStrategyProvider,
+ __LEGACY: {
+ esClient: this.esClient,
+ },
};
api.registerSearchStrategyContext(this.initializerContext.opaqueId, 'core', () => core);
@@ -126,14 +130,14 @@ export class SearchService implements Plugin {
return api;
}
- public start(core: CoreStart, packageInfo: PackageInfo) {
+ public start(core: CoreStart): ISearchStart {
if (!this.search) {
throw new Error('Search should always be defined');
}
return {
search: this.search,
__LEGACY: {
- esClient: getEsClient(core.injectedMetadata, core.http, packageInfo, this.loadingCount$),
+ esClient: this.esClient!,
},
};
}
diff --git a/src/plugins/data/public/search/sync_search_strategy.test.ts b/src/plugins/data/public/search/sync_search_strategy.test.ts
index 2737a4033a015..cd19c4d84dce1 100644
--- a/src/plugins/data/public/search/sync_search_strategy.test.ts
+++ b/src/plugins/data/public/search/sync_search_strategy.test.ts
@@ -44,10 +44,8 @@ describe('Sync search strategy', () => {
},
{}
);
- expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toBe(
- `/internal/search/${SYNC_SEARCH_STRATEGY}`
- );
- expect(mockCoreSetup.http.fetch.mock.calls[0][1]).toEqual({
+ expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({
+ path: `/internal/search/${SYNC_SEARCH_STRATEGY}`,
body: JSON.stringify({
serverStrategy: 'SYNC_SEARCH_STRATEGY',
}),
diff --git a/src/plugins/data/public/search/sync_search_strategy.ts b/src/plugins/data/public/search/sync_search_strategy.ts
index 3885a97a98571..65fe10f39aaa0 100644
--- a/src/plugins/data/public/search/sync_search_strategy.ts
+++ b/src/plugins/data/public/search/sync_search_strategy.ts
@@ -36,14 +36,12 @@ export const syncSearchStrategyProvider: TSearchStrategyProvider {
- const response: Promise = context.core.http.fetch(
- `/internal/search/${request.serverStrategy}`,
- {
- method: 'POST',
- body: JSON.stringify(request),
- signal: options.signal,
- }
- );
+ const response: Promise = context.core.http.fetch({
+ path: `/internal/search/${request.serverStrategy}`,
+ method: 'POST',
+ body: JSON.stringify(request),
+ signal: options.signal,
+ });
return from(response);
};
diff --git a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx
index bda1557bdaf91..39290b066fab7 100644
--- a/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx
+++ b/test/plugin_functional/plugins/core_plugin_b/public/plugin.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import { CoreSetup, Plugin, PluginInitializerContext } from 'kibana/public';
+import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
import { CorePluginAPluginSetup } from '../../core_plugin_a/public/plugin';
declare global {
@@ -52,7 +52,17 @@ export class CorePluginBPlugin
};
}
- public start() {}
+ public async start(core: CoreStart, deps: {}) {
+ return {
+ sendSystemRequest: async (asSystemRequest: boolean) => {
+ const response = await core.http.post('/core_plugin_b/system_request', {
+ asSystemRequest,
+ });
+ return `/core_plugin_b/system_request says: "${response}"`;
+ },
+ };
+ }
+
public stop() {}
}
diff --git a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
index 91a4fe89c1c57..d2bb9222a23fa 100644
--- a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
+++ b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
@@ -54,6 +54,16 @@ export class CorePluginBPlugin implements Plugin {
return res.ok({ body: `ID: ${req.query.id} - ${req.body.bar.toUpperCase()}` });
}
);
+
+ router.post(
+ {
+ path: '/core_plugin_b/system_request',
+ validate: false,
+ },
+ async (context, req, res) => {
+ return res.ok({ body: `System request? ${req.isSystemRequest}` });
+ }
+ );
}
public start() {}
diff --git a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
index eb232b1458991..f5b45d36944e1 100644
--- a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
+++ b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
@@ -54,5 +54,15 @@ export default function({ getService }: PluginFunctionalProviderContext) {
statusCode: 400,
});
});
+
+ it('sets request.isSystemRequest when kbn-system-request header is set', async () => {
+ await supertest
+ .post('/core_plugin_b/system_request')
+ .set('kbn-xsrf', 'anything')
+ .set('kbn-system-request', 'true')
+ .send()
+ .expect(200)
+ .expect('System request? true');
+ });
});
}
diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
index b76463ee76739..82267d73782af 100644
--- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
+++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
@@ -64,7 +64,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
});
});
- describe('have env data provided', function describeIndexTests() {
+ describe('have env data provided', () => {
before(async () => {
await PageObjects.common.navigateToApp('bar');
});
@@ -75,5 +75,27 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
expect(envData.packageInfo.version).to.be.a('string');
});
});
+
+ describe('http fetching', () => {
+ before(async () => {
+ await PageObjects.common.navigateToApp('settings');
+ });
+
+ it('should send kbn-system-request header when asSystemRequest: true', async () => {
+ expect(
+ await browser.executeAsync(async cb => {
+ window.__coreProvider.start.plugins.core_plugin_b.sendSystemRequest(true).then(cb);
+ })
+ ).to.be('/core_plugin_b/system_request says: "System request? true"');
+ });
+
+ it('should not send kbn-system-request header when asSystemRequest: false', async () => {
+ expect(
+ await browser.executeAsync(async cb => {
+ window.__coreProvider.start.plugins.core_plugin_b.sendSystemRequest(false).then(cb);
+ })
+ ).to.be('/core_plugin_b/system_request says: "System request? false"');
+ });
+ });
});
}
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx
index 9491fe3024f94..6153ebce5e0da 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx
+++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx
@@ -118,7 +118,7 @@ export const MetricsExplorerChart = ({
)}
- {series.rows.length > 0 ? (
+ {metrics.length && series.rows.length > 0 ? (
{metrics.map((metric, id) => (
{
function testProps() {
const setState = jest.fn();
- core.http.get.mockImplementation(async (url: string) => {
- const parts = url.split('/');
+ core.http.get.mockImplementation(async ({ path }) => {
+ const parts = path.split('/');
const indexPatternTitle = parts[parts.length - 1];
return {
indexPatternTitle: `${indexPatternTitle}_testtitle`,
@@ -397,7 +397,8 @@ describe('IndexPattern Data Panel', () => {
expect(setState).toHaveBeenCalledTimes(2);
expect(core.http.get).toHaveBeenCalledTimes(2);
- expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', {
+ expect(core.http.get).toHaveBeenCalledWith({
+ path: '/api/lens/existing_fields/a',
query: {
fromDate: '2019-01-01',
toDate: '2020-01-01',
@@ -405,7 +406,8 @@ describe('IndexPattern Data Panel', () => {
},
});
- expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', {
+ expect(core.http.get).toHaveBeenCalledWith({
+ path: '/api/lens/existing_fields/a',
query: {
fromDate: '2019-01-01',
toDate: '2020-01-02',
@@ -436,7 +438,8 @@ describe('IndexPattern Data Panel', () => {
expect(setState).toHaveBeenCalledTimes(2);
- expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', {
+ expect(core.http.get).toHaveBeenCalledWith({
+ path: '/api/lens/existing_fields/a',
query: {
fromDate: '2019-01-01',
toDate: '2020-01-01',
@@ -444,7 +447,8 @@ describe('IndexPattern Data Panel', () => {
},
});
- expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/b', {
+ expect(core.http.get).toHaveBeenCalledWith({
+ path: '/api/lens/existing_fields/b',
query: {
fromDate: '2019-01-01',
toDate: '2020-01-01',
@@ -484,13 +488,13 @@ describe('IndexPattern Data Panel', () => {
let overlapCount = 0;
const props = testProps();
- core.http.get.mockImplementation((url: string) => {
+ core.http.get.mockImplementation(({ path }) => {
if (queryCount) {
++overlapCount;
}
++queryCount;
- const parts = url.split('/');
+ const parts = path.split('/');
const indexPatternTitle = parts[parts.length - 1];
const result = Promise.resolve({
indexPatternTitle,
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts
index 6bea13c32830f..f8961c30d14ee 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts
@@ -537,8 +537,8 @@ describe('loader', () => {
describe('syncExistingFields', () => {
it('should call once for each index pattern', async () => {
const setState = jest.fn();
- const fetchJson = jest.fn(async (url: string) => {
- const indexPatternTitle = _.last(url.split('/'));
+ const fetchJson = jest.fn(({ path }: { path: string }) => {
+ const indexPatternTitle = _.last(path.split('/'));
return {
indexPatternTitle,
existingFieldNames: ['field_1', 'field_2'].map(
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts
index c196cb886e575..5b994890deb7a 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts
@@ -234,7 +234,8 @@ export async function syncExistingFields({
query.timeFieldName = pattern.timeFieldName;
}
- return fetchJson(`${BASE_API_URL}/existing_fields/${pattern.id}`, {
+ return fetchJson({
+ path: `${BASE_API_URL}/existing_fields/${pattern.id}`,
query,
}) as Promise;
})
diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
index ed73405134224..819db630c0609 100644
--- a/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
+++ b/x-pack/legacy/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
@@ -12,11 +12,11 @@ import { forkJoin, of, Observable, Subject } from 'rxjs';
import { mergeMap, switchMap, tap } from 'rxjs/operators';
import { anomalyDataChange } from '../explorer_charts/explorer_charts_container_service';
-import { VIEW_BY_JOB_LABEL } from '../explorer_constants';
import { explorerService } from '../explorer_dashboard_service';
import {
getDateFormatTz,
getSelectionInfluencers,
+ getSelectionJobIds,
getSelectionTimeRange,
loadAnnotationsTableData,
loadAnomaliesTableData,
@@ -114,12 +114,7 @@ function loadExplorerData(config: LoadExplorerDataConfig): Observable d.id);
-
+ const jobIds = getSelectionJobIds(selectedCells, selectedJobs);
const timerange = getSelectionTimeRange(
selectedCells,
swimlaneBucketInterval.asSeconds(),
diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts
index 0ab75b1db2972..c60b2d55d8686 100644
--- a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts
+++ b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.d.ts
@@ -35,10 +35,15 @@ export declare const getDefaultSwimlaneData: () => SwimlaneData;
export declare const getInfluencers: (selectedJobs: any[]) => string[];
+export declare const getSelectionJobIds: (
+ selectedCells: AppStateSelectedCells | undefined,
+ selectedJobs: ExplorerJob[]
+) => string[];
+
export declare const getSelectionInfluencers: (
selectedCells: AppStateSelectedCells | undefined,
fieldName: string
-) => any[];
+) => string[];
interface SelectionTimeRange {
earliestMs: number;
diff --git a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js
index 14d356c0d1c81..4818856b8a8d2 100644
--- a/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js
+++ b/x-pack/legacy/plugins/ml/public/application/explorer/explorer_utils.js
@@ -222,6 +222,19 @@ export function getSelectionInfluencers(selectedCells, fieldName) {
return [];
}
+export function getSelectionJobIds(selectedCells, selectedJobs) {
+ if (
+ selectedCells !== undefined &&
+ selectedCells.type !== SWIMLANE_TYPE.OVERALL &&
+ selectedCells.viewByFieldName !== undefined &&
+ selectedCells.viewByFieldName === VIEW_BY_JOB_LABEL
+ ) {
+ return selectedCells.lanes;
+ }
+
+ return selectedJobs.map(d => d.id);
+}
+
export function getSwimlaneBucketInterval(selectedJobs, swimlaneContainerWidth) {
// Bucketing interval should be the maximum of the chart related interval (i.e. time range related)
// and the max bucket span for the jobs shown in the chart.
@@ -587,10 +600,7 @@ export async function loadAnomaliesTableData(
tableSeverity,
influencersFilterQuery
) {
- const jobIds =
- selectedCells !== undefined && selectedCells.viewByFieldName === VIEW_BY_JOB_LABEL
- ? selectedCells.lanes
- : selectedJobs.map(d => d.id);
+ const jobIds = getSelectionJobIds(selectedCells, selectedJobs);
const influencers = getSelectionInfluencers(selectedCells, fieldName);
const timeRange = getSelectionTimeRange(selectedCells, interval, bounds);
diff --git a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts
index 173a4e31cfef6..0f68c56a18bf6 100644
--- a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts
+++ b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts
@@ -5,8 +5,6 @@
*/
import { kfetch } from 'ui/kfetch';
-// @ts-ignore
-import { addSystemApiHeader } from 'ui/system_api';
const API_BASE_URL = '/api/reporting/jobs';
@@ -64,7 +62,7 @@ class JobQueueClient {
method: 'GET',
pathname: `${API_BASE_URL}/list`,
query,
- headers: addSystemApiHeader({}),
+ asSystemRequest: true,
});
};
@@ -72,7 +70,7 @@ class JobQueueClient {
return kfetch({
method: 'GET',
pathname: `${API_BASE_URL}/count`,
- headers: addSystemApiHeader({}),
+ asSystemRequest: true,
});
}
@@ -80,7 +78,7 @@ class JobQueueClient {
return kfetch({
method: 'GET',
pathname: `${API_BASE_URL}/output/${jobId}`,
- headers: addSystemApiHeader({}),
+ asSystemRequest: true,
});
}
@@ -88,7 +86,7 @@ class JobQueueClient {
return kfetch({
method: 'GET',
pathname: `${API_BASE_URL}/info/${jobId}`,
- headers: addSystemApiHeader({}),
+ asSystemRequest: true,
});
}
}
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index 163b345da40bd..f0f28f1dc246c 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -5,6 +5,7 @@
*/
import { EuiPanel } from '@elastic/eui';
+import deepEqual from 'fast-deep-equal';
import { getOr, isEmpty, isEqual, union } from 'lodash/fp';
import React, { useMemo } from 'react';
import styled from 'styled-components';
@@ -34,6 +35,7 @@ import {
IIndexPattern,
Query,
} from '../../../../../../../src/plugins/data/public';
+import { inputsModel } from '../../store';
const DEFAULT_EVENTS_VIEWER_HEIGHT = 500;
@@ -67,7 +69,7 @@ interface Props {
sort: Sort;
timelineTypeContext: TimelineTypeContextProps;
toggleColumn: (column: ColumnHeader) => void;
- utilityBar?: (totalCount: number) => React.ReactNode;
+ utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
}
const EventsViewerComponent: React.FC = ({
@@ -171,7 +173,7 @@ const EventsViewerComponent: React.FC = ({
{headerFilterGroup}
- {utilityBar?.(totalCountMinusDeleted)}
+ {utilityBar?.(refetch, totalCountMinusDeleted)}
= ({
export const EventsViewer = React.memo(
EventsViewerComponent,
(prevProps, nextProps) =>
- prevProps.browserFields === nextProps.browserFields &&
+ isEqual(prevProps.browserFields, nextProps.browserFields) &&
prevProps.columns === nextProps.columns &&
prevProps.dataProviders === nextProps.dataProviders &&
prevProps.deletedEventIds === nextProps.deletedEventIds &&
prevProps.end === nextProps.end &&
- isEqual(prevProps.filters, nextProps.filters) &&
+ deepEqual(prevProps.filters, nextProps.filters) &&
prevProps.height === nextProps.height &&
prevProps.id === nextProps.id &&
- prevProps.indexPattern === nextProps.indexPattern &&
+ deepEqual(prevProps.indexPattern, nextProps.indexPattern) &&
prevProps.isLive === nextProps.isLive &&
prevProps.itemsPerPage === nextProps.itemsPerPage &&
prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions &&
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
index 99d174d74f3f8..d56898cae7d23 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
@@ -5,7 +5,7 @@
*/
import { isEqual } from 'lodash/fp';
-import React, { useCallback, useEffect } from 'react';
+import React, { useCallback, useMemo, useEffect } from 'react';
import { connect } from 'react-redux';
import { ActionCreator } from 'typescript-fsa';
import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store';
@@ -35,7 +35,7 @@ export interface OwnProps {
headerFilterGroup?: React.ReactNode;
pageFilters?: esFilters.Filter[];
timelineTypeContext?: TimelineTypeContextProps;
- utilityBar?: (totalCount: number) => React.ReactNode;
+ utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
}
interface StateReduxProps {
@@ -84,6 +84,10 @@ interface DispatchProps {
type Props = OwnProps & StateReduxProps & DispatchProps;
+const defaultTimelineTypeContext = {
+ loadingText: i18n.LOADING_EVENTS,
+};
+
const StatefulEventsViewerComponent: React.FC
= ({
createTimeline,
columns,
@@ -99,16 +103,14 @@ const StatefulEventsViewerComponent: React.FC = ({
itemsPerPage,
itemsPerPageOptions,
kqlMode,
- pageFilters = [],
+ pageFilters,
query,
removeColumn,
start,
showCheckboxes,
showRowRenderers,
sort,
- timelineTypeContext = {
- loadingText: i18n.LOADING_EVENTS,
- },
+ timelineTypeContext = defaultTimelineTypeContext,
updateItemsPerPage,
upsertColumn,
utilityBar,
@@ -153,18 +155,20 @@ const StatefulEventsViewerComponent: React.FC = ({
[columns, id, upsertColumn, removeColumn]
);
+ const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]);
+
return (
{
const getGlobalQuerySelector = inputsSelectors.globalQuerySelector();
const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector();
const getEvents = timelineSelectors.getEventsByIdSelector();
- const mapStateToProps = (state: State, { id, pageFilters = [], defaultModel }: OwnProps) => {
+ const mapStateToProps = (state: State, { id, defaultModel }: OwnProps) => {
const input: inputsModel.InputsRange = getInputsTimeline(state);
const events: TimelineModel = getEvents(state, id) ?? defaultModel;
const {
@@ -205,7 +209,7 @@ const makeMapStateToProps = () => {
columns,
dataProviders,
deletedEventIds,
- filters: [...getGlobalFiltersQuerySelector(state), ...pageFilters],
+ filters: getGlobalFiltersQuerySelector(state),
id,
isLive: input.policy.kind === 'interval',
itemsPerPage,
diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
index 528f02f0a845b..9fb59e2034b08 100644
--- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
@@ -17,7 +17,6 @@ import { FlyoutButton } from './button';
import { Pane } from './pane';
import { timelineActions } from '../../store/actions';
import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/helpers';
-import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../lib/track_usage';
/** The height in pixels of the flyout header, exported for use in height calculations */
export const flyoutHeaderHeight: number = 60;
@@ -101,10 +100,7 @@ export const FlyoutComponent = React.memo(
dataProviders={dataProviders!}
show={!show}
timelineId={timelineId}
- onOpen={() => {
- track(METRIC_TYPE.LOADED, TELEMETRY_EVENT.TIMELINE_OPENED);
- showTimeline({ id: timelineId, show: true });
- }}
+ onOpen={() => showTimeline({ id: timelineId, show: true })}
/>
>
)
diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
index 0d503e2db3d9d..39c48413737e2 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
@@ -21,7 +21,7 @@ StaticSwitch.displayName = 'StaticSwitch';
export interface JobSwitchProps {
job: SiemJob;
isSiemJobsLoading: boolean;
- onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => void;
+ onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise;
}
// Based on ML Job/Datafeed States from x-pack/legacy/plugins/ml/common/constants/states.js
@@ -48,9 +48,10 @@ export const JobSwitchComponent = ({
}: JobSwitchProps) => {
const [isLoading, setIsLoading] = useState(false);
const handleChange = useCallback(
- e => {
+ async e => {
setIsLoading(true);
- onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked);
+ await onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked);
+ setIsLoading(false);
},
[job, setIsLoading, onJobStateChange]
);
@@ -58,7 +59,7 @@ export const JobSwitchComponent = ({
return (
- {isSiemJobsLoading || isLoading || isJobLoading(job.jobState, job.datafeedId) ? (
+ {isSiemJobsLoading || isLoading || isJobLoading(job.jobState, job.datafeedState) ? (
) : (
void
+ onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise
) => [
{
name: i18n.COLUMN_JOB_NAME,
@@ -92,7 +92,7 @@ const getPaginatedItems = (items: SiemJob[], pageIndex: number, pageSize: number
export interface JobTableProps {
isLoading: boolean;
jobs: SiemJob[];
- onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => void;
+ onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise;
}
export const JobsTableComponent = ({ isLoading, jobs, onJobStateChange }: JobTableProps) => {
diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx
index 307be06424ee3..a41e84c163663 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/ml_popover.tsx
@@ -11,7 +11,7 @@ import React, { useContext, useReducer, useState } from 'react';
import styled from 'styled-components';
import { useKibana } from '../../lib/kibana';
-import { METRIC_TYPE, TELEMETRY_EVENT, trackUiAction as track } from '../../lib/track_usage';
+import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../lib/telemetry';
import { errorToToaster } from '../ml/api/error_to_toaster';
import { hasMlAdminPermissions } from '../ml/permissions/has_ml_admin_permissions';
import { MlCapabilitiesContext } from '../ml/permissions/ml_capabilities_provider';
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
index b653624ec1f67..cebf9b90656ca 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx
@@ -7,7 +7,7 @@ import { EuiTab, EuiTabs } from '@elastic/eui';
import { getOr } from 'lodash/fp';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
-import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage';
+import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/telemetry';
import { getSearch } from '../helpers';
import { TabNavigationProps, TabNavigationItemProps } from './types';
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
index ff556a1a9bdfc..a224e0355b5d3 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
@@ -166,7 +166,7 @@ const StatefulTimelineComponent = React.memo(
updateItemsPerPage,
upsertColumn,
}) => {
- const [loading, signalIndexExists, signalIndexName] = useSignalIndex();
+ const { loading, signalIndexExists, signalIndexName } = useSignalIndex();
const indexToAdd = useMemo(() => {
if (signalIndexExists && signalIndexName != null && ['signal', 'all'].includes(eventType)) {
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
index 22fb837ffb801..6b3578bacf24c 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
@@ -389,6 +389,7 @@ export const getPrePackagedRulesStatus = async ({
}: {
signal: AbortSignal;
}): Promise<{
+ rules_custom_installed: number;
rules_installed: number;
rules_not_installed: number;
rules_not_updated: number;
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
index f7a30766ad7d8..d376a1d6ad178 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
@@ -22,11 +22,11 @@ import { useApolloClient } from '../../../utils/apollo_context';
import * as i18n from './translations';
interface FetchIndexPatternReturn {
- browserFields: BrowserFields | null;
+ browserFields: BrowserFields;
isLoading: boolean;
indices: string[];
indicesExists: boolean;
- indexPatterns: IIndexPattern | null;
+ indexPatterns: IIndexPattern;
}
type Return = [FetchIndexPatternReturn, Dispatch>];
@@ -35,8 +35,8 @@ export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return =>
const apolloClient = useApolloClient();
const [indices, setIndices] = useState(defaultIndices);
const [indicesExists, setIndicesExists] = useState(false);
- const [indexPatterns, setIndexPatterns] = useState(null);
- const [browserFields, setBrowserFields] = useState(null);
+ const [indexPatterns, setIndexPatterns] = useState({ fields: [], title: '' });
+ const [browserFields, setBrowserFields] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [, dispatchToaster] = useStateToaster();
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
index 2e776738547df..b30c3b211b1b8 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
@@ -30,7 +30,7 @@ export const NewRuleSchema = t.intersection([
rule_id: t.string,
saved_id: t.string,
tags: t.array(t.string),
- threats: t.array(t.unknown),
+ threat: t.array(t.unknown),
to: t.string,
updated_by: t.string,
}),
@@ -73,7 +73,7 @@ export const RuleSchema = t.intersection([
tags: t.array(t.string),
type: t.string,
to: t.string,
- threats: t.array(t.unknown),
+ threat: t.array(t.unknown),
updated_at: t.string,
updated_by: t.string,
}),
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
index ee34cad873021..14d40f9ffbc37 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { useEffect, useState, useRef } from 'react';
+import { useEffect, useState } from 'react';
import { useStateToaster, displaySuccessToast } from '../../../components/toasters';
import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
@@ -18,6 +18,7 @@ interface Return {
loading: boolean;
loadingCreatePrePackagedRules: boolean;
refetchPrePackagedRulesStatus: Func | null;
+ rulesCustomInstalled: number | null;
rulesInstalled: number | null;
rulesNotInstalled: number | null;
rulesNotUpdated: number | null;
@@ -47,13 +48,26 @@ export const usePrePackagedRules = ({
isAuthenticated,
isSignalIndexExists,
}: UsePrePackagedRuleProps): Return => {
- const [rulesInstalled, setRulesInstalled] = useState(null);
- const [rulesNotInstalled, setRulesNotInstalled] = useState(null);
- const [rulesNotUpdated, setRulesNotUpdated] = useState(null);
+ const [rulesStatus, setRuleStatus] = useState<
+ Pick<
+ Return,
+ | 'createPrePackagedRules'
+ | 'refetchPrePackagedRulesStatus'
+ | 'rulesCustomInstalled'
+ | 'rulesInstalled'
+ | 'rulesNotInstalled'
+ | 'rulesNotUpdated'
+ >
+ >({
+ createPrePackagedRules: null,
+ refetchPrePackagedRulesStatus: null,
+ rulesCustomInstalled: null,
+ rulesInstalled: null,
+ rulesNotInstalled: null,
+ rulesNotUpdated: null,
+ });
const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false);
const [loading, setLoading] = useState(true);
- const createPrePackagedRules = useRef(null);
- const refetchPrePackagedRules = useRef(null);
const [, dispatchToaster] = useStateToaster();
useEffect(() => {
@@ -68,15 +82,25 @@ export const usePrePackagedRules = ({
});
if (isSubscribed) {
- setRulesInstalled(prePackagedRuleStatusResponse.rules_installed);
- setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed);
- setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated);
+ setRuleStatus({
+ createPrePackagedRules: createElasticRules,
+ refetchPrePackagedRulesStatus: fetchPrePackagedRules,
+ rulesCustomInstalled: prePackagedRuleStatusResponse.rules_custom_installed,
+ rulesInstalled: prePackagedRuleStatusResponse.rules_installed,
+ rulesNotInstalled: prePackagedRuleStatusResponse.rules_not_installed,
+ rulesNotUpdated: prePackagedRuleStatusResponse.rules_not_updated,
+ });
}
} catch (error) {
if (isSubscribed) {
- setRulesInstalled(null);
- setRulesNotInstalled(null);
- setRulesNotUpdated(null);
+ setRuleStatus({
+ createPrePackagedRules: null,
+ refetchPrePackagedRulesStatus: null,
+ rulesCustomInstalled: null,
+ rulesInstalled: null,
+ rulesNotInstalled: null,
+ rulesNotUpdated: null,
+ });
errorToToaster({ title: i18n.RULE_FETCH_FAILURE, error, dispatchToaster });
}
}
@@ -122,9 +146,14 @@ export const usePrePackagedRules = ({
iterationTryOfFetchingPrePackagedCount > 100)
) {
setLoadingCreatePrePackagedRules(false);
- setRulesInstalled(prePackagedRuleStatusResponse.rules_installed);
- setRulesNotInstalled(prePackagedRuleStatusResponse.rules_not_installed);
- setRulesNotUpdated(prePackagedRuleStatusResponse.rules_not_updated);
+ setRuleStatus({
+ createPrePackagedRules: createElasticRules,
+ refetchPrePackagedRulesStatus: fetchPrePackagedRules,
+ rulesCustomInstalled: prePackagedRuleStatusResponse.rules_custom_installed,
+ rulesInstalled: prePackagedRuleStatusResponse.rules_installed,
+ rulesNotInstalled: prePackagedRuleStatusResponse.rules_not_installed,
+ rulesNotUpdated: prePackagedRuleStatusResponse.rules_not_updated,
+ });
displaySuccessToast(i18n.RULE_PREPACKAGED_SUCCESS, dispatchToaster);
stopTimeOut();
resolve(true);
@@ -146,8 +175,7 @@ export const usePrePackagedRules = ({
};
fetchPrePackagedRules();
- createPrePackagedRules.current = createElasticRules;
- refetchPrePackagedRules.current = fetchPrePackagedRules;
+
return () => {
isSubscribed = false;
abortCtrl.abort();
@@ -157,10 +185,6 @@ export const usePrePackagedRules = ({
return {
loading,
loadingCreatePrePackagedRules,
- refetchPrePackagedRulesStatus: refetchPrePackagedRules.current,
- rulesInstalled,
- rulesNotInstalled,
- rulesNotUpdated,
- createPrePackagedRules: createPrePackagedRules.current,
+ ...rulesStatus,
};
};
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
index 254e8cbdc9a88..af6e437255acd 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
@@ -36,7 +36,7 @@ export const useRules = (pagination: PaginationOptions, filterOptions: FilterOpt
let isSubscribed = true;
const abortCtrl = new AbortController();
- async function fetchData() {
+ async function fetchData(forceReload: boolean = false) {
try {
setLoading(true);
const fetchRulesResult = await fetchRules({
@@ -59,7 +59,7 @@ export const useRules = (pagination: PaginationOptions, filterOptions: FilterOpt
}
fetchData();
- reFetchRules.current = fetchData;
+ reFetchRules.current = fetchData.bind(null, true);
return () => {
isSubscribed = false;
abortCtrl.abort();
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx
index d225241875d4b..b93009c8ce2c2 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx
@@ -24,10 +24,14 @@ interface Return {
*/
export const usePrivilegeUser = (): Return => {
const [loading, setLoading] = useState(true);
- const [isAuthenticated, setAuthenticated] = useState(null);
- const [hasIndexManage, setHasIndexManage] = useState(null);
- const [hasIndexWrite, setHasIndexWrite] = useState(null);
- const [hasManageApiKey, setHasManageApiKey] = useState(null);
+ const [privilegeUser, setPrivilegeUser] = useState<
+ Pick
+ >({
+ isAuthenticated: null,
+ hasIndexManage: null,
+ hasManageApiKey: null,
+ hasIndexWrite: null,
+ });
const [, dispatchToaster] = useStateToaster();
useEffect(() => {
@@ -42,29 +46,31 @@ export const usePrivilegeUser = (): Return => {
});
if (isSubscribed && privilege != null) {
- setAuthenticated(privilege.is_authenticated);
if (privilege.index != null && Object.keys(privilege.index).length > 0) {
const indexName = Object.keys(privilege.index)[0];
- setHasIndexManage(privilege.index[indexName].manage);
- setHasIndexWrite(
- privilege.index[indexName].create ||
+ setPrivilegeUser({
+ isAuthenticated: privilege.is_authenticated,
+ hasIndexManage: privilege.index[indexName].manage,
+ hasIndexWrite:
+ privilege.index[indexName].create ||
privilege.index[indexName].create_doc ||
privilege.index[indexName].index ||
- privilege.index[indexName].write
- );
- setHasManageApiKey(
- privilege.cluster.manage_security ||
+ privilege.index[indexName].write,
+ hasManageApiKey:
+ privilege.cluster.manage_security ||
privilege.cluster.manage_api_key ||
- privilege.cluster.manage_own_api_key
- );
+ privilege.cluster.manage_own_api_key,
+ });
}
}
} catch (error) {
if (isSubscribed) {
- setAuthenticated(false);
- setHasIndexManage(false);
- setHasIndexWrite(false);
- setHasManageApiKey(false);
+ setPrivilegeUser({
+ isAuthenticated: false,
+ hasIndexManage: false,
+ hasManageApiKey: false,
+ hasIndexWrite: false,
+ });
errorToToaster({ title: i18n.PRIVILEGE_FETCH_FAILURE, error, dispatchToaster });
}
}
@@ -80,5 +86,5 @@ export const usePrivilegeUser = (): Return => {
};
}, []);
- return { loading, isAuthenticated, hasIndexManage, hasManageApiKey, hasIndexWrite };
+ return { loading, ...privilegeUser };
};
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx
index fa88a84fb1187..3dc6bac07be34 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx
@@ -9,11 +9,16 @@ import React, { SetStateAction, useEffect, useState } from 'react';
import { fetchQuerySignals } from './api';
import { SignalSearchResponse } from './types';
-type Return = [
- boolean,
- SignalSearchResponse | null,
- React.Dispatch>
-];
+type Func = () => void;
+
+interface Return {
+ loading: boolean;
+ data: SignalSearchResponse | null;
+ setQuery: React.Dispatch>;
+ response: string;
+ request: string;
+ refetch: Func | null;
+}
/**
* Hook for using to get a Signals from the Detection Engine API
@@ -21,9 +26,20 @@ type Return = [
* @param initialQuery query dsl object
*
*/
-export const useQuerySignals = (initialQuery: object): Return => {
+export const useQuerySignals = (
+ initialQuery: object,
+ indexName?: string | null
+): Return => {
const [query, setQuery] = useState(initialQuery);
- const [signals, setSignals] = useState | null>(null);
+ const [signals, setSignals] = useState<
+ Pick, 'data' | 'setQuery' | 'response' | 'request' | 'refetch'>
+ >({
+ data: null,
+ response: '',
+ request: '',
+ setQuery,
+ refetch: null,
+ });
const [loading, setLoading] = useState(true);
useEffect(() => {
@@ -39,11 +55,23 @@ export const useQuerySignals = (initialQuery: object): Return(initialQuery: object): Return void;
-type Return = [boolean, boolean | null, string | null, Func | null];
+interface Return {
+ loading: boolean;
+ signalIndexExists: boolean | null;
+ signalIndexName: string | null;
+ createDeSignalIndex: Func | null;
+}
/**
* Hook for managing signal index
@@ -23,9 +28,13 @@ type Return = [boolean, boolean | null, string | null, Func | null];
*/
export const useSignalIndex = (): Return => {
const [loading, setLoading] = useState(true);
- const [signalIndexName, setSignalIndexName] = useState(null);
- const [signalIndexExists, setSignalIndexExists] = useState(null);
- const createDeSignalIndex = useRef(null);
+ const [signalIndex, setSignalIndex] = useState<
+ Pick
+ >({
+ signalIndexExists: null,
+ signalIndexName: null,
+ createDeSignalIndex: null,
+ });
const [, dispatchToaster] = useStateToaster();
useEffect(() => {
@@ -38,13 +47,19 @@ export const useSignalIndex = (): Return => {
const signal = await getSignalIndex({ signal: abortCtrl.signal });
if (isSubscribed && signal != null) {
- setSignalIndexName(signal.name);
- setSignalIndexExists(true);
+ setSignalIndex({
+ signalIndexExists: true,
+ signalIndexName: signal.name,
+ createDeSignalIndex: createIndex,
+ });
}
} catch (error) {
if (isSubscribed) {
- setSignalIndexName(null);
- setSignalIndexExists(false);
+ setSignalIndex({
+ signalIndexExists: false,
+ signalIndexName: null,
+ createDeSignalIndex: createIndex,
+ });
if (error instanceof SignalIndexError && error.statusCode !== 404) {
errorToToaster({ title: i18n.SIGNAL_GET_NAME_FAILURE, error, dispatchToaster });
}
@@ -70,8 +85,11 @@ export const useSignalIndex = (): Return => {
if (error instanceof PostSignalError && error.statusCode === 409) {
fetchData();
} else {
- setSignalIndexName(null);
- setSignalIndexExists(false);
+ setSignalIndex({
+ signalIndexExists: false,
+ signalIndexName: null,
+ createDeSignalIndex: createIndex,
+ });
errorToToaster({ title: i18n.SIGNAL_POST_FAILURE, error, dispatchToaster });
}
}
@@ -82,12 +100,11 @@ export const useSignalIndex = (): Return => {
};
fetchData();
- createDeSignalIndex.current = createIndex;
return () => {
isSubscribed = false;
abortCtrl.abort();
};
}, []);
- return [loading, signalIndexExists, signalIndexName, createDeSignalIndex.current];
+ return { loading, ...signalIndex };
};
diff --git a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
index e995d123b1b44..0336e4a9a977b 100644
--- a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
@@ -89,7 +89,8 @@ export const WithSource = React.memo(({ children, indexToAdd, s
return [...configIndex, ...indexToAdd];
}
return configIndex;
- }, [configIndex, DEFAULT_INDEX_KEY, indexToAdd]);
+ }, [configIndex, indexToAdd]);
+
return (
query={sourceQuery}
diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
index 03fe68ca1398d..f4eb088b6ad94 100644
--- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { getOr, isEmpty } from 'lodash/fp';
+import { getOr } from 'lodash/fp';
import memoizeOne from 'memoize-one';
import React from 'react';
import { Query } from 'react-apollo';
@@ -84,12 +84,13 @@ class TimelineQueryComponent extends QueryTemplate<
sortField,
} = this.props;
const defaultKibanaIndex = kibana.services.uiSettings.get(DEFAULT_INDEX_KEY);
- const defaultIndex = isEmpty(indexPattern)
- ? [
- ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []),
- ...(['all', 'signal'].includes(eventType) ? indexToAdd : []),
- ]
- : indexPattern?.title.split(',') ?? [];
+ const defaultIndex =
+ indexPattern == null || (indexPattern != null && indexPattern.title === '')
+ ? [
+ ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []),
+ ...(['all', 'signal'].includes(eventType) ? indexToAdd : []),
+ ]
+ : indexPattern?.title.split(',') ?? [];
const variables: GetTimelineQuery.Variables = {
fieldRequested: fields,
filterQuery: createFilter(filterQuery),
diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json
index 35599827ffe42..a9247403bf22c 100644
--- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json
+++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json
@@ -4940,7 +4940,7 @@
"deprecationReason": null
},
{
- "name": "threats",
+ "name": "threat",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "ToAny", "ofType": null },
diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts
index 4b6825268403c..6a24ffcc13020 100644
--- a/x-pack/legacy/plugins/siem/public/graphql/types.ts
+++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts
@@ -1015,7 +1015,7 @@ export interface RuleField {
tags?: Maybe;
- threats?: Maybe;
+ threat?: Maybe;
type?: Maybe;
diff --git a/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts b/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts
new file mode 100644
index 0000000000000..856b7783a5367
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/lib/telemetry/index.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics';
+
+import { SetupPlugins } from '../../plugin';
+export { telemetryMiddleware } from './middleware';
+
+export { METRIC_TYPE };
+
+type TrackFn = (type: UiStatsMetricType, event: string | string[], count?: number) => void;
+
+let _track: TrackFn;
+
+export const track: TrackFn = (type, event, count) => {
+ try {
+ _track(type, event, count);
+ } catch (error) {
+ // ignore failed tracking call
+ }
+};
+
+export const initTelemetry = (usageCollection: SetupPlugins['usageCollection'], appId: string) => {
+ try {
+ _track = usageCollection.reportUiStats.bind(null, appId);
+ } catch (error) {
+ // ignore failed setup here, as we'll just have an inert tracker
+ }
+};
+
+export enum TELEMETRY_EVENT {
+ // Detections
+ SIEM_RULE_ENABLED = 'siem_rule_enabled',
+ SIEM_RULE_DISABLED = 'siem_rule_disabled',
+ CUSTOM_RULE_ENABLED = 'custom_rule_enabled',
+ CUSTOM_RULE_DISABLED = 'custom_rule_disabled',
+
+ // ML
+ SIEM_JOB_ENABLED = 'siem_job_enabled',
+ SIEM_JOB_DISABLED = 'siem_job_disabled',
+ CUSTOM_JOB_ENABLED = 'custom_job_enabled',
+ CUSTOM_JOB_DISABLED = 'custom_job_disabled',
+ JOB_ENABLE_FAILURE = 'job_enable_failure',
+ JOB_DISABLE_FAILURE = 'job_disable_failure',
+
+ // Timeline
+ TIMELINE_OPENED = 'open_timeline',
+ TIMELINE_SAVED = 'timeline_saved',
+ TIMELINE_NAMED = 'timeline_named',
+
+ // UI Interactions
+ TAB_CLICKED = 'tab_',
+}
diff --git a/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts b/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts
new file mode 100644
index 0000000000000..59c6cb3566907
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/lib/telemetry/middleware.ts
@@ -0,0 +1,24 @@
+/*
+ * 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 { Action, Dispatch, MiddlewareAPI } from 'redux';
+
+import { track, METRIC_TYPE, TELEMETRY_EVENT } from './';
+import { timelineActions } from '../../store/actions';
+
+export const telemetryMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => (action: Action) => {
+ if (timelineActions.endTimelineSaving.match(action)) {
+ track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.TIMELINE_SAVED);
+ } else if (timelineActions.updateTitle.match(action)) {
+ track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.TIMELINE_NAMED);
+ } else if (timelineActions.showTimeline.match(action)) {
+ if (action.payload.show) {
+ track(METRIC_TYPE.LOADED, TELEMETRY_EVENT.TIMELINE_OPENED);
+ }
+ }
+
+ return next(action);
+};
diff --git a/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts b/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts
deleted file mode 100644
index 47277653c816f..0000000000000
--- a/x-pack/legacy/plugins/siem/public/lib/track_usage/index.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-// @ts-ignore
-import {
- createUiStatsReporter,
- METRIC_TYPE,
-} from '../../../../../../../src/legacy/core_plugins/ui_metric/public';
-import { APP_ID } from '../../../common/constants';
-
-export const trackUiAction = createUiStatsReporter(APP_ID);
-export { METRIC_TYPE };
-
-export enum TELEMETRY_EVENT {
- // ML
- SIEM_JOB_ENABLED = 'siem_job_enabled',
- SIEM_JOB_DISABLED = 'siem_job_disabled',
- CUSTOM_JOB_ENABLED = 'custom_job_enabled',
- CUSTOM_JOB_DISABLED = 'custom_job_disabled',
- JOB_ENABLE_FAILURE = 'job_enable_failure',
- JOB_DISABLE_FAILURE = 'job_disable_failure',
-
- // Timeline
- TIMELINE_OPENED = 'open_timeline',
-
- // UI Interactions
- TAB_CLICKED = 'tab_',
-}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
index 51d8e2630459c..e65adcf3a6920 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
@@ -20,8 +20,7 @@ import { DispatchUpdateTimeline } from '../../../../components/open_timeline/typ
import { combineQueries } from '../../../../components/timeline/helpers';
import { TimelineNonEcsData } from '../../../../graphql/types';
import { useKibana } from '../../../../lib/kibana';
-import { inputsSelectors, State } from '../../../../store';
-import { InputsRange } from '../../../../store/inputs/model';
+import { inputsSelectors, State, inputsModel } from '../../../../store';
import { timelineActions, timelineSelectors } from '../../../../store/timeline';
import { timelineDefaults, TimelineModel } from '../../../../store/timeline/model';
import { useApolloClient } from '../../../../utils/apollo_context';
@@ -46,7 +45,7 @@ import {
CreateTimelineProps,
SetEventsDeletedProps,
SetEventsLoadingProps,
- UpdateSignalsStatus,
+ UpdateSignalsStatusCallback,
UpdateSignalsStatusProps,
} from './types';
import { dispatchUpdateTimeline } from '../../../../components/open_timeline/helpers';
@@ -97,7 +96,7 @@ const SignalsTableComponent: React.FC = ({
clearEventsDeleted,
clearEventsLoading,
clearSelected,
- defaultFilters = [],
+ defaultFilters,
from,
globalFilters,
globalQuery,
@@ -118,7 +117,9 @@ const SignalsTableComponent: React.FC = ({
const [showClearSelectionAction, setShowClearSelectionAction] = useState(false);
const [filterGroup, setFilterGroup] = useState(FILTER_OPEN);
- const [{ browserFields, indexPatterns }] = useFetchIndexPatterns([signalsIndex]);
+ const [{ browserFields, indexPatterns }] = useFetchIndexPatterns(
+ signalsIndex !== '' ? [signalsIndex] : []
+ );
const kibana = useKibana();
const getGlobalQuery = useCallback(() => {
@@ -207,8 +208,8 @@ const SignalsTableComponent: React.FC = ({
setShowClearSelectionAction(true);
}, [setSelectAll, setShowClearSelectionAction]);
- const updateSignalsStatusCallback: UpdateSignalsStatus = useCallback(
- async ({ signalIds, status }: UpdateSignalsStatusProps) => {
+ const updateSignalsStatusCallback: UpdateSignalsStatusCallback = useCallback(
+ async (refetchQuery: inputsModel.Refetch, { signalIds, status }: UpdateSignalsStatusProps) => {
await updateSignalStatusAction({
query: showClearSelectionAction ? getGlobalQuery()?.filterQuery : undefined,
signalIds: Object.keys(selectedEventIds),
@@ -216,6 +217,7 @@ const SignalsTableComponent: React.FC = ({
setEventsDeleted: setEventsDeletedCallback,
setEventsLoading: setEventsLoadingCallback,
});
+ refetchQuery();
},
[
getGlobalQuery,
@@ -228,7 +230,7 @@ const SignalsTableComponent: React.FC = ({
// Callback for creating the SignalUtilityBar which receives totalCount from EventsViewer component
const utilityBarCallback = useCallback(
- (totalCount: number) => {
+ (refetchQuery: inputsModel.Refetch, totalCount: number) => {
return (
= ({
selectedEventIds={selectedEventIds}
showClearSelection={showClearSelectionAction}
totalCount={totalCount}
- updateSignalsStatus={updateSignalsStatusCallback}
+ updateSignalsStatus={updateSignalsStatusCallback.bind(null, refetchQuery)}
/>
);
},
@@ -283,13 +285,16 @@ const SignalsTableComponent: React.FC = ({
);
const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]);
- const defaultFiltersMemo = useMemo(
- () => [
- ...defaultFilters,
- ...(filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters),
- ],
- [defaultFilters, filterGroup]
- );
+ const defaultFiltersMemo = useMemo(() => {
+ if (isEmpty(defaultFilters)) {
+ return filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters;
+ } else if (defaultFilters != null && !isEmpty(defaultFilters)) {
+ return [
+ ...defaultFilters,
+ ...(filterGroup === FILTER_OPEN ? signalsOpenFilters : signalsClosedFilters),
+ ];
+ }
+ }, [defaultFilters, filterGroup]);
const timelineTypeContext = useMemo(
() => ({
@@ -304,6 +309,11 @@ const SignalsTableComponent: React.FC = ({
[additionalActions, canUserCRUD, selectAll]
);
+ const headerFilterGroup = useMemo(
+ () => ,
+ [onFilterGroupChangedCallback]
+ );
+
if (loading || isEmpty(signalsIndex)) {
return (
@@ -319,9 +329,7 @@ const SignalsTableComponent: React.FC = ({
pageFilters={defaultFiltersMemo}
defaultModel={signalsDefaultModel}
end={to}
- headerFilterGroup={
-
- }
+ headerFilterGroup={headerFilterGroup}
id={SIGNALS_PAGE_TIMELINE_ID}
start={from}
timelineTypeContext={timelineTypeContext}
@@ -338,9 +346,8 @@ const makeMapStateToProps = () => {
getTimeline(state, SIGNALS_PAGE_TIMELINE_ID) ?? timelineDefaults;
const { deletedEventIds, isSelectAllChecked, loadingEventIds, selectedEventIds } = timeline;
- const globalInputs: InputsRange = getGlobalInputs(state);
+ const globalInputs: inputsModel.InputsRange = getGlobalInputs(state);
const { query, filters } = globalInputs;
-
return {
globalQuery: query,
globalFilters: filters,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts
index 2366a103492ec..b3e7ed75cfb99 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/types.ts
@@ -8,6 +8,7 @@ import ApolloClient from 'apollo-client';
import { Ecs } from '../../../../graphql/types';
import { TimelineModel } from '../../../../store/timeline/model';
+import { inputsModel } from '../../../../store';
export interface SetEventsLoadingProps {
eventIds: string[];
@@ -24,6 +25,10 @@ export interface UpdateSignalsStatusProps {
status: 'open' | 'closed';
}
+export type UpdateSignalsStatusCallback = (
+ refetchQuery: inputsModel.Refetch,
+ { signalIds, status }: UpdateSignalsStatusProps
+) => void;
export type UpdateSignalsStatus = ({ signalIds, status }: UpdateSignalsStatusProps) => void;
export interface UpdateSignalStatusActionProps {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts
index d475fd155ea25..2c5a1ddd9a010 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/config.ts
@@ -9,6 +9,7 @@ import { SignalsHistogramOption } from './types';
export const signalsHistogramOptions: SignalsHistogramOption[] = [
{ text: 'signal.rule.risk_score', value: 'signal.rule.risk_score' },
{ text: 'signal.rule.severity', value: 'signal.rule.severity' },
+ { text: 'signal.rule.threat.tactic.name', value: 'signal.rule.threat.tactic.name' },
{ text: 'destination.ip', value: 'destination.ip' },
{ text: 'event.action', value: 'event.action' },
{ text: 'event.category', value: 'event.category' },
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx
index 64bc7ba24c689..c716c5512c9c7 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx
@@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Position } from '@elastic/charts';
-import { EuiButton, EuiPanel, EuiSelect } from '@elastic/eui';
+import { EuiButton, EuiSelect } from '@elastic/eui';
import numeral from '@elastic/numeral';
-import React, { memo, useCallback, useMemo, useState } from 'react';
+import React, { memo, useCallback, useMemo, useState, useEffect } from 'react';
import { HeaderSection } from '../../../../components/header_section';
import { SignalsHistogram } from './signals_histogram';
@@ -14,24 +14,31 @@ import { SignalsHistogram } from './signals_histogram';
import * as i18n from './translations';
import { Query } from '../../../../../../../../../src/plugins/data/common/query';
import { esFilters } from '../../../../../../../../../src/plugins/data/common/es_query';
-import { SignalsHistogramOption, SignalsTotal } from './types';
+import { RegisterQuery, SignalsHistogramOption, SignalsTotal } from './types';
import { signalsHistogramOptions } from './config';
import { getDetectionEngineUrl } from '../../../../components/link_to';
import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants';
import { useUiSetting$ } from '../../../../lib/kibana';
+import { InspectButtonContainer } from '../../../../components/inspect';
+import { Panel } from '../../../../components/panel';
const defaultTotalSignalsObj: SignalsTotal = {
value: 0,
relation: 'eq',
};
+export const DETECTIONS_HISTOGRAM_ID = 'detections-histogram';
+
interface SignalsHistogramPanelProps {
defaultStackByOption?: SignalsHistogramOption;
+ deleteQuery?: ({ id }: { id: string }) => void;
filters?: esFilters.Filter[];
from: number;
query?: Query;
legendPosition?: Position;
loadingInitial?: boolean;
+ signalIndexName: string | null;
+ setQuery: (params: RegisterQuery) => void;
showLinkToSignals?: boolean;
showTotalSignalsCount?: boolean;
stackByOptions?: SignalsHistogramOption[];
@@ -43,11 +50,14 @@ interface SignalsHistogramPanelProps {
export const SignalsHistogramPanel = memo(
({
defaultStackByOption = signalsHistogramOptions[0],
+ deleteQuery,
filters,
query,
from,
legendPosition = 'right',
loadingInitial = false,
+ setQuery,
+ signalIndexName,
showLinkToSignals = false,
showTotalSignalsCount = false,
stackByOptions,
@@ -55,12 +65,21 @@ export const SignalsHistogramPanel = memo(
title = i18n.HISTOGRAM_HEADER,
updateDateRange,
}) => {
+ const [showInspect, setShowInspect] = useState(true);
const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT);
const [totalSignalsObj, setTotalSignalsObj] = useState(defaultTotalSignalsObj);
const [selectedStackByOption, setSelectedStackByOption] = useState(
defaultStackByOption
);
+ useEffect(() => {
+ return () => {
+ if (deleteQuery) {
+ deleteQuery({ id: DETECTIONS_HISTOGRAM_ID });
+ }
+ };
+ }, []);
+
const totalSignals = useMemo(
() =>
i18n.SHOWING_SIGNALS(
@@ -77,34 +96,53 @@ export const SignalsHistogramPanel = memo(
);
}, []);
+ const handleOnMouseEnter = useCallback(() => {
+ if (!showInspect) {
+ setShowInspect(true);
+ }
+ }, [showInspect, setShowInspect]);
+ const handleOnMouseLeave = useCallback(() => {
+ if (showInspect) {
+ setShowInspect(false);
+ }
+ }, [showInspect, setShowInspect]);
+
return (
-
-
- {stackByOptions && (
-
- )}
- {showLinkToSignals && (
- {i18n.VIEW_SIGNALS}
- )}
-
+
+
+
+ {stackByOptions && (
+
+ )}
+ {showLinkToSignals && (
+ {i18n.VIEW_SIGNALS}
+ )}
+
-
-
+
+
+
);
}
);
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx
index d4db8cc7c37e8..5e1c8aa095166 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram/index.tsx
@@ -21,10 +21,11 @@ import { isEmpty } from 'lodash/fp';
import { useQuerySignals } from '../../../../../containers/detection_engine/signals/use_query';
import { Query } from '../../../../../../../../../../src/plugins/data/common/query';
import { esFilters, esQuery } from '../../../../../../../../../../src/plugins/data/common/es_query';
-import { SignalsAggregation, SignalsTotal } from '../types';
+import { RegisterQuery, SignalsAggregation, SignalsTotal } from '../types';
import { formatSignalsData, getSignalsHistogramQuery } from './helpers';
import { useTheme } from '../../../../../components/charts/common';
import { useKibana } from '../../../../../lib/kibana';
+import { DETECTIONS_HISTOGRAM_ID } from '..';
interface HistogramSignalsProps {
filters?: esFilters.Filter[];
@@ -32,6 +33,8 @@ interface HistogramSignalsProps {
legendPosition?: Position;
loadingInitial: boolean;
query?: Query;
+ registerQuery: (params: RegisterQuery) => void;
+ signalIndexName: string | null;
setTotalSignalsCount: React.Dispatch;
stackByField: string;
to: number;
@@ -46,18 +49,42 @@ export const SignalsHistogram = React.memo(
filters,
legendPosition = 'right',
loadingInitial,
+ registerQuery,
+ signalIndexName,
setTotalSignalsCount,
stackByField,
updateDateRange,
}) => {
- const [isLoadingSignals, signalsData, setQuery] = useQuerySignals<{}, SignalsAggregation>(
- getSignalsHistogramQuery(stackByField, from, to, [])
+ const {
+ loading: isLoadingSignals,
+ data: signalsData,
+ setQuery,
+ response,
+ request,
+ refetch,
+ } = useQuerySignals<{}, SignalsAggregation>(
+ getSignalsHistogramQuery(stackByField, from, to, []),
+ signalIndexName
);
const theme = useTheme();
const kibana = useKibana();
const formattedSignalsData = useMemo(() => formatSignalsData(signalsData), [signalsData]);
+ useEffect(() => {
+ if (refetch != null && registerQuery != null) {
+ registerQuery({
+ id: DETECTIONS_HISTOGRAM_ID,
+ inspect: {
+ dsl: [request],
+ response: [response],
+ },
+ loading: isLoadingSignals,
+ refetch,
+ });
+ }
+ }, [registerQuery, isLoadingSignals, signalsData, response, request, refetch]);
+
useEffect(() => {
setTotalSignalsCount(
signalsData?.hits.total ?? {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts
index 4eb10852450ad..6ef4cecc4ec8b 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/types.ts
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { inputsModel } from '../../../../store';
+
export interface SignalsHistogramOption {
text: string;
value: string;
@@ -38,3 +40,10 @@ export interface SignalsTotal {
value: number;
relation: string;
}
+
+export interface RegisterQuery {
+ id: string;
+ inspect: inputsModel.InspectQuery | null;
+ loading: boolean;
+ refetch: inputsModel.Refetch;
+}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx
index fc1110e382847..e7cdc3345c031 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_info/index.tsx
@@ -26,7 +26,7 @@ export const useSignalInfo = ({ ruleId = null }: SignalInfo): Return => {
);
- const [loading, signals] = useQuerySignals(buildLastSignalsQuery(ruleId));
+ const { loading, data: signals } = useQuerySignals(buildLastSignalsQuery(ruleId));
useEffect(() => {
if (signals != null) {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx
index bbaccb7882484..0f6a51e52cd2e 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx
@@ -154,12 +154,12 @@ export const useUserInfo = (): State => {
hasIndexWrite: hasApiIndexWrite,
hasManageApiKey: hasApiManageApiKey,
} = usePrivilegeUser();
- const [
- indexNameLoading,
- isApiSignalIndexExists,
- apiSignalIndexName,
- createSignalIndex,
- ] = useSignalIndex();
+ const {
+ loading: indexNameLoading,
+ signalIndexExists: isApiSignalIndexExists,
+ signalIndexName: apiSignalIndexName,
+ createDeSignalIndex: createSignalIndex,
+ } = useSignalIndex();
const uiCapabilities = useKibana().services.application.capabilities;
const capabilitiesCanUserCRUD: boolean =
@@ -172,46 +172,50 @@ export const useUserInfo = (): State => {
}, [loading, privilegeLoading, indexNameLoading]);
useEffect(() => {
- if (hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) {
+ if (!loading && hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) {
dispatch({ type: 'updateHasIndexManage', hasIndexManage: hasApiIndexManage });
}
- }, [hasIndexManage, hasApiIndexManage]);
+ }, [loading, hasIndexManage, hasApiIndexManage]);
useEffect(() => {
- if (hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) {
+ if (!loading && hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) {
dispatch({ type: 'updateHasIndexWrite', hasIndexWrite: hasApiIndexWrite });
}
- }, [hasIndexWrite, hasApiIndexWrite]);
+ }, [loading, hasIndexWrite, hasApiIndexWrite]);
useEffect(() => {
- if (hasManageApiKey !== hasApiManageApiKey && hasApiManageApiKey != null) {
+ if (!loading && hasManageApiKey !== hasApiManageApiKey && hasApiManageApiKey != null) {
dispatch({ type: 'updateHasManageApiKey', hasManageApiKey: hasApiManageApiKey });
}
- }, [hasManageApiKey, hasApiManageApiKey]);
+ }, [loading, hasManageApiKey, hasApiManageApiKey]);
useEffect(() => {
- if (isSignalIndexExists !== isApiSignalIndexExists && isApiSignalIndexExists != null) {
+ if (
+ !loading &&
+ isSignalIndexExists !== isApiSignalIndexExists &&
+ isApiSignalIndexExists != null
+ ) {
dispatch({ type: 'updateIsSignalIndexExists', isSignalIndexExists: isApiSignalIndexExists });
}
- }, [isSignalIndexExists, isApiSignalIndexExists]);
+ }, [loading, isSignalIndexExists, isApiSignalIndexExists]);
useEffect(() => {
- if (isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) {
+ if (!loading && isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) {
dispatch({ type: 'updateIsAuthenticated', isAuthenticated: isApiAuthenticated });
}
- }, [isAuthenticated, isApiAuthenticated]);
+ }, [loading, isAuthenticated, isApiAuthenticated]);
useEffect(() => {
- if (canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) {
+ if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) {
dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD });
}
- }, [canUserCRUD, capabilitiesCanUserCRUD]);
+ }, [loading, canUserCRUD, capabilitiesCanUserCRUD]);
useEffect(() => {
- if (signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) {
+ if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) {
dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName });
}
- }, [signalIndexName, apiSignalIndexName]);
+ }, [loading, signalIndexName, apiSignalIndexName]);
useEffect(() => {
if (
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
index 9e292fa69b2c4..e49cd2b36c7b8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx
@@ -111,6 +111,10 @@ const DetectionEnginePageComponent: React.FC
[detectionsTabs, tabName]
);
+ const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [
+ signalIndexName,
+ ]);
+
if (isUserAuthenticated != null && !isUserAuthenticated && !loading) {
return (
@@ -131,7 +135,7 @@ const DetectionEnginePageComponent: React.FC
return (
<>
{hasIndexWrite != null && !hasIndexWrite && }
-
+
{({ indicesExist, indexPattern }) => {
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
@@ -164,10 +168,13 @@ const DetectionEnginePageComponent: React.FC
{tabName === DetectionEngineTab.signals && (
<>
({
tags: [],
to: 'now',
type: 'saved_query',
- threats: [],
+ threat: [],
version: 1,
});
@@ -87,7 +87,7 @@ export const mockTableData: TableData[] = [
saved_id: "Garrett's IP",
severity: 'low',
tags: [],
- threats: [],
+ threat: [],
timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
timeline_title: 'Untitled timeline',
to: 'now',
@@ -136,7 +136,7 @@ export const mockTableData: TableData[] = [
saved_id: "Garrett's IP",
severity: 'low',
tags: [],
- threats: [],
+ threat: [],
timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
timeline_title: 'Untitled timeline',
to: 'now',
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx
index d6bf8643fff1c..6212c2067384d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/actions.tsx
@@ -21,6 +21,7 @@ import {
displayErrorToast,
displaySuccessToast,
} from '../../../../components/toasters';
+import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../../lib/telemetry';
import * as i18n from '../translations';
import { bucketRulesResponse } from './helpers';
@@ -38,12 +39,7 @@ export const duplicateRulesAction = async (
const ruleIds = rules.map(r => r.id);
dispatch({ type: 'updateLoading', ids: ruleIds, isLoading: true });
const duplicatedRules = await duplicateRules({ rules });
- dispatch({ type: 'updateLoading', ids: ruleIds, isLoading: false });
- dispatch({
- type: 'updateRules',
- rules: duplicatedRules,
- appendRuleId: rules[rules.length - 1].id,
- });
+ dispatch({ type: 'refresh' });
displaySuccessToast(
i18n.SUCCESSFULLY_DUPLICATED_RULES(duplicatedRules.length),
dispatchToaster
@@ -114,6 +110,19 @@ export const enableRulesAction = async (
dispatchToaster
);
}
+
+ if (rules.some(rule => rule.immutable)) {
+ track(
+ METRIC_TYPE.COUNT,
+ enabled ? TELEMETRY_EVENT.SIEM_RULE_ENABLED : TELEMETRY_EVENT.SIEM_RULE_DISABLED
+ );
+ }
+ if (rules.some(rule => !rule.immutable)) {
+ track(
+ METRIC_TYPE.COUNT,
+ enabled ? TELEMETRY_EVENT.CUSTOM_RULE_ENABLED : TELEMETRY_EVENT.CUSTOM_RULE_DISABLED
+ );
+ }
} catch (e) {
displayErrorToast(errorTitle, [e.message], dispatchToaster);
dispatch({ type: 'updateLoading', ids, isLoading: false });
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts
index 3616d4dbaad24..9a523536694d9 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/helpers.ts
@@ -50,3 +50,16 @@ export const bucketRulesResponse = (response: Array) =>
},
{ rules: [], errors: [] }
);
+
+export const showRulesTable = ({
+ isInitialLoad,
+ rulesCustomInstalled,
+ rulesInstalled,
+}: {
+ isInitialLoad: boolean;
+ rulesCustomInstalled: number | null;
+ rulesInstalled: number | null;
+}) =>
+ !isInitialLoad &&
+ ((rulesCustomInstalled != null && rulesCustomInstalled > 0) ||
+ (rulesInstalled != null && rulesInstalled > 0));
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx
index a4e7d7a3615cc..b304d77f2e276 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx
@@ -6,7 +6,6 @@
import {
EuiBasicTable,
- EuiButton,
EuiContextMenuPanel,
EuiEmptyPrompt,
EuiLoadingContent,
@@ -40,9 +39,9 @@ import * as i18n from '../translations';
import { EuiBasicTableOnChange, TableData } from '../types';
import { getBatchItems } from './batch_actions';
import { getColumns } from './columns';
+import { showRulesTable } from './helpers';
import { allRulesReducer, State } from './reducer';
import { RulesTableFilters } from './rules_table_filters/rules_table_filters';
-import { DETECTION_ENGINE_PAGE_NAME } from '../../../../components/link_to/redirect_to_detection_engine';
const initialState: State = {
isLoading: true,
@@ -69,6 +68,7 @@ interface AllRulesProps {
loading: boolean;
loadingCreatePrePackagedRules: boolean;
refetchPrePackagedRulesStatus: () => void;
+ rulesCustomInstalled: number | null;
rulesInstalled: number | null;
rulesNotInstalled: number | null;
rulesNotUpdated: number | null;
@@ -91,6 +91,7 @@ export const AllRules = React.memo(
loading,
loadingCreatePrePackagedRules,
refetchPrePackagedRulesStatus,
+ rulesCustomInstalled,
rulesInstalled,
rulesNotInstalled,
rulesNotUpdated,
@@ -109,6 +110,7 @@ export const AllRules = React.memo(
dispatch,
] = useReducer(allRulesReducer, initialState);
const history = useHistory();
+ const [oldRefreshToggle, setOldRefreshToggle] = useState(refreshToggle);
const [isInitialLoad, setIsInitialLoad] = useState(true);
const [isGlobalLoading, setIsGlobalLoad] = useState(false);
const [, dispatchToaster] = useStateToaster();
@@ -131,20 +133,16 @@ export const AllRules = React.memo(
const tableOnChangeCallback = useCallback(
({ page, sort }: EuiBasicTableOnChange) => {
- dispatch({
- type: 'updatePagination',
- pagination: { ...pagination, page: page.index + 1, perPage: page.size },
- });
dispatch({
type: 'updateFilterOptions',
filterOptions: {
- ...filterOptions,
sortField: 'enabled', // Only enabled is supported for sorting currently
sortOrder: sort?.direction ?? 'desc',
},
+ pagination: { page: page.index + 1, perPage: page.size },
});
},
- [dispatch, filterOptions, pagination]
+ [dispatch]
);
const columns = useMemo(() => {
@@ -176,11 +174,18 @@ export const AllRules = React.memo(
}, [importCompleteToggle]);
useEffect(() => {
- if (reFetchRulesData != null) {
+ if (!isInitialLoad && reFetchRulesData != null && oldRefreshToggle !== refreshToggle) {
+ setOldRefreshToggle(refreshToggle);
reFetchRulesData();
+ refetchPrePackagedRulesStatus();
}
- refetchPrePackagedRulesStatus();
- }, [refreshToggle, reFetchRulesData, refetchPrePackagedRulesStatus]);
+ }, [
+ isInitialLoad,
+ refreshToggle,
+ oldRefreshToggle,
+ reFetchRulesData,
+ refetchPrePackagedRulesStatus,
+ ]);
useEffect(() => {
if (reFetchRulesData != null) {
@@ -220,16 +225,18 @@ export const AllRules = React.memo(
dispatch({
type: 'updateFilterOptions',
filterOptions: {
- ...filterOptions,
...newFilterOptions,
},
- });
- dispatch({
- type: 'updatePagination',
- pagination: { ...pagination, page: 1 },
+ pagination: { page: 1 },
});
}, []);
+ const emptyPrompt = useMemo(() => {
+ return (
+ {i18n.NO_RULES}} titleSize="xs" body={i18n.NO_RULES_BODY} />
+ );
+ }, []);
+
return (
<>
(
<>
- {rulesInstalled != null && rulesInstalled > 0 && (
+ {((rulesCustomInstalled && rulesCustomInstalled > 0) ||
+ (rulesInstalled != null && rulesInstalled > 0)) && (
-
+
)}
- {isInitialLoad && isEmpty(tableData) && (
+ {isInitialLoad && (
)}
- {isGlobalLoading && !isEmpty(tableData) && (
+ {isGlobalLoading && !isEmpty(tableData) && !isInitialLoad && (
)}
- {isEmpty(tableData) && prePackagedRuleStatus === 'ruleNotInstalled' && (
-
- )}
- {!isEmpty(tableData) && (
+ {rulesCustomInstalled != null &&
+ rulesCustomInstalled === 0 &&
+ prePackagedRuleStatus === 'ruleNotInstalled' && (
+
+ )}
+ {showRulesTable({ isInitialLoad, rulesCustomInstalled, rulesInstalled }) && (
<>
@@ -304,24 +318,7 @@ export const AllRules = React.memo(
isSelectable={!hasNoPermissions ?? false}
itemId="id"
items={tableData}
- noItemsMessage={
- {i18n.NO_RULES}}
- titleSize="xs"
- body={i18n.NO_RULES_BODY}
- actions={
-
- {i18n.ADD_NEW_RULE}
-
- }
- />
- }
+ noItemsMessage={emptyPrompt}
onChange={tableOnChangeCallback}
pagination={{
pageIndex: pagination.page - 1,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts
index 74ce8f2847faa..3634a16cbf6ac 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts
@@ -31,9 +31,13 @@ export type Action =
| { type: 'setExportPayload'; exportPayload?: Rule[] }
| { type: 'setSelected'; selectedItems: TableData[] }
| { type: 'updateLoading'; ids: string[]; isLoading: boolean }
- | { type: 'updateRules'; rules: Rule[]; appendRuleId?: string; pagination?: PaginationOptions }
- | { type: 'updatePagination'; pagination: PaginationOptions }
- | { type: 'updateFilterOptions'; filterOptions: FilterOptions }
+ | { type: 'updateRules'; rules: Rule[]; pagination?: PaginationOptions }
+ | { type: 'updatePagination'; pagination: Partial }
+ | {
+ type: 'updateFilterOptions';
+ filterOptions: Partial;
+ pagination: Partial;
+ }
| { type: 'failure' };
export const allRulesReducer = (state: State, action: Action): State => {
@@ -56,18 +60,10 @@ export const allRulesReducer = (state: State, action: Action): State => {
}
const ruleIds = state.rules.map(r => r.rule_id);
- const appendIdx =
- action.appendRuleId != null ? state.rules.findIndex(r => r.id === action.appendRuleId) : -1;
const updatedRules = action.rules.reverse().reduce((rules, updatedRule) => {
let newRules = rules;
if (ruleIds.includes(updatedRule.rule_id)) {
newRules = newRules.map(r => (updatedRule.rule_id === r.rule_id ? updatedRule : r));
- } else if (appendIdx !== -1) {
- newRules = [
- ...newRules.slice(0, appendIdx + 1),
- updatedRule,
- ...newRules.slice(appendIdx + 1, newRules.length),
- ];
} else {
newRules = [...newRules, updatedRule];
}
@@ -90,25 +86,28 @@ export const allRulesReducer = (state: State, action: Action): State => {
rules: updatedRules,
tableData: formatRules(updatedRules),
selectedItems: updatedSelectedItems,
- pagination: {
- ...state.pagination,
- total:
- action.appendRuleId != null
- ? state.pagination.total + action.rules.length
- : state.pagination.total,
- },
};
}
case 'updatePagination': {
return {
...state,
- pagination: action.pagination,
+ pagination: {
+ ...state.pagination,
+ ...action.pagination,
+ },
};
}
case 'updateFilterOptions': {
return {
...state,
- filterOptions: action.filterOptions,
+ filterOptions: {
+ ...state.filterOptions,
+ ...action.filterOptions,
+ },
+ pagination: {
+ ...state.pagination,
+ ...action.pagination,
+ },
};
}
case 'deleteRules': {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
index daf519f5af695..c54a2e8d49844 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/rules_table_filters/rules_table_filters.tsx
@@ -21,6 +21,8 @@ import { TagsFilterPopover } from './tags_filter_popover';
interface RulesTableFiltersProps {
onFilterChanged: (filterOptions: Partial) => void;
+ rulesCustomInstalled: number | null;
+ rulesInstalled: number | null;
}
/**
@@ -29,7 +31,11 @@ interface RulesTableFiltersProps {
*
* @param onFilterChanged change listener to be notified on filter changes
*/
-const RulesTableFiltersComponent = ({ onFilterChanged }: RulesTableFiltersProps) => {
+const RulesTableFiltersComponent = ({
+ onFilterChanged,
+ rulesCustomInstalled,
+ rulesInstalled,
+}: RulesTableFiltersProps) => {
const [filter, setFilter] = useState('');
const [selectedTags, setSelectedTags] = useState([]);
const [showCustomRules, setShowCustomRules] = useState(false);
@@ -84,13 +90,17 @@ const RulesTableFiltersComponent = ({ onFilterChanged }: RulesTableFiltersProps)
withNext
>
{i18n.ELASTIC_RULES}
+ {rulesInstalled != null ? ` (${rulesInstalled})` : ''}
- {i18n.CUSTOM_RULES}
+ <>
+ {i18n.CUSTOM_RULES}
+ {rulesCustomInstalled != null ? ` (${rulesCustomInstalled})` : ''}
+ >
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx
index 011c008c5b2d2..e1cbc6ee92393 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx
@@ -24,7 +24,7 @@ import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_
import { FilterLabel } from './filter_label';
import * as i18n from './translations';
-import { BuildQueryBarDescription, BuildThreatsDescription, ListItems } from './types';
+import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './types';
import { SeverityBadge } from '../severity_badge';
import ListTreeIcon from './assets/list_tree_icon.svg';
@@ -94,7 +94,7 @@ export const buildQueryBarDescription = ({
return items;
};
-const ThreatsEuiFlexGroup = styled(EuiFlexGroup)`
+const ThreatEuiFlexGroup = styled(EuiFlexGroup)`
.euiFlexItem {
margin-bottom: 0px;
}
@@ -114,25 +114,22 @@ const ReferenceLinkItem = styled(EuiButtonEmpty)`
}
`;
-export const buildThreatsDescription = ({
- label,
- threats,
-}: BuildThreatsDescription): ListItems[] => {
- if (threats.length > 0) {
+export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => {
+ if (threat.length > 0) {
return [
{
title: label,
description: (
-
- {threats.map((threat, index) => {
- const tactic = tacticsOptions.find(t => t.id === threat.tactic.id);
+
+ {threat.map((singleThreat, index) => {
+ const tactic = tacticsOptions.find(t => t.id === singleThreat.tactic.id);
return (
-
-
+
+
{tactic != null ? tactic.text : ''}
- {threat.techniques.map(technique => {
+ {singleThreat.technique.map(technique => {
const myTechnique = techniquesOptions.find(t => t.id === technique.id);
return (
@@ -153,7 +150,7 @@ export const buildThreatsDescription = ({
);
})}
-
+
),
},
];
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
index 8cf1601e2c4b6..f1d2609cde8fe 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx
@@ -24,7 +24,7 @@ import {
buildQueryBarDescription,
buildSeverityDescription,
buildStringArrayDescription,
- buildThreatsDescription,
+ buildThreatDescription,
buildUnorderedListArrayDescription,
buildUrlsDescription,
} from './helpers';
@@ -116,11 +116,11 @@ const getDescriptionItem = (
savedId,
indexPatterns,
});
- } else if (field === 'threats') {
- const threats: IMitreEnterpriseAttack[] = get(field, value).filter(
- (threat: IMitreEnterpriseAttack) => threat.tactic.name !== 'none'
+ } else if (field === 'threat') {
+ const threat: IMitreEnterpriseAttack[] = get(field, value).filter(
+ (singleThreat: IMitreEnterpriseAttack) => singleThreat.tactic.name !== 'none'
);
- return buildThreatsDescription({ label, threats });
+ return buildThreatDescription({ label, threat });
} else if (field === 'description') {
return [
{
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts
index d32fbcd725d12..c120d4a4106d0 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/types.ts
@@ -27,7 +27,7 @@ export interface BuildQueryBarDescription {
indexPatterns?: IIndexPattern;
}
-export interface BuildThreatsDescription {
+export interface BuildThreatDescription {
label: string;
- threats: IMitreEnterpriseAttack[];
+ threat: IMitreEnterpriseAttack[];
}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts
index 1202fe54ad194..7a28a16214df6 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/helpers.ts
@@ -9,9 +9,9 @@ import { IMitreAttack } from '../../types';
export const isMitreAttackInvalid = (
tacticName: string | null | undefined,
- techniques: IMitreAttack[] | null | undefined
+ technique: IMitreAttack[] | null | undefined
) => {
- if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(techniques))) {
+ if (isEmpty(tacticName) || (tacticName !== 'none' && isEmpty(technique))) {
return true;
}
return false;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
index f9a22c37cfdf0..d85be053065fc 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/index.tsx
@@ -21,7 +21,7 @@ import styled from 'styled-components';
import { tacticsOptions, techniquesOptions } from '../../../mitre/mitre_tactics_techniques';
import * as Rulei18n from '../../translations';
import { FieldHook, getFieldValidityAndErrorMessage } from '../shared_imports';
-import { threatsDefault } from '../step_about_rule/default_value';
+import { threatDefault } from '../step_about_rule/default_value';
import { IMitreEnterpriseAttack } from '../../types';
import { MyAddItemButton } from '../add_item_form';
import { isMitreAttackInvalid } from './helpers';
@@ -49,7 +49,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
const values = field.value as string[];
const newValues = [...values.slice(0, index), ...values.slice(index + 1)];
if (isEmpty(newValues)) {
- field.setValue(threatsDefault);
+ field.setValue(threatDefault);
} else {
field.setValue(newValues);
}
@@ -62,10 +62,10 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
if (!isEmpty(values[values.length - 1])) {
field.setValue([
...values,
- { tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] },
+ { tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] },
]);
} else {
- field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, techniques: [] }]);
+ field.setValue([{ tactic: { id: 'none', name: 'none', reference: 'none' }, technique: [] }]);
}
}, [field]);
@@ -82,7 +82,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
{
...values[index],
tactic: { id, reference, name },
- techniques: [],
+ technique: [],
},
...values.slice(index + 1),
]);
@@ -96,7 +96,7 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
...values.slice(0, index),
{
...values[index],
- techniques: selectedOptions,
+ technique: selectedOptions,
},
...values.slice(index + 1),
]);
@@ -133,9 +133,9 @@ export const AddMitreThreat = ({ dataTestSubj, field, idAria, isDisabled }: AddI
);
const getSelectTechniques = (item: IMitreEnterpriseAttack, index: number, disabled: boolean) => {
- const invalid = isMitreAttackInvalid(item.tactic.name, item.techniques);
+ const invalid = isMitreAttackInvalid(item.tactic.name, item.technique);
const options = techniquesOptions.filter(t => t.tactics.includes(kebabCase(item.tactic.name)));
- const selectedOptions = item.techniques.map(technic => ({
+ const selectedOptions = item.technique.map(technic => ({
...technic,
label: `${technic.name} (${technic.id})`, // API doesn't allow for label field
}));
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts
index 557e91691b6c7..bbc12800b7df5 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/mitre/translations.ts
@@ -13,7 +13,7 @@ export const TACTIC = i18n.translate('xpack.siem.detectionEngine.mitreAttack.tac
export const TECHNIQUE = i18n.translate(
'xpack.siem.detectionEngine.mitreAttack.techniquesDescription',
{
- defaultMessage: 'technique',
+ defaultMessage: 'techniques',
}
);
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts
index e03cc252ad729..d128696ec7253 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/translations.ts
@@ -7,7 +7,7 @@
import { i18n } from '@kbn/i18n';
export const STATUS = i18n.translate('xpack.siem.detectionEngine.ruleStatus.statusDescription', {
- defaultMessage: 'Status',
+ defaultMessage: 'Last response',
});
export const STATUS_AT = i18n.translate(
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
index 92aca1cecf9f3..70bfc2bcddd62 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts
@@ -7,11 +7,11 @@
import { AboutStepRule } from '../../types';
import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/search_super_select/translations';
-export const threatsDefault = [
+export const threatDefault = [
{
framework: 'MITRE ATT&CK',
tactic: { id: 'none', name: 'none', reference: 'none' },
- techniques: [],
+ technique: [],
},
];
@@ -28,5 +28,5 @@ export const stepAboutDefaultValue: AboutStepRule = {
id: null,
title: DEFAULT_TIMELINE_TITLE,
},
- threats: threatsDefault,
+ threat: threatDefault,
};
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
index 8370af397bfec..4ae88b3014d19 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
@@ -227,12 +227,12 @@ const StepAboutRuleComponent: FC = ({
}}
/>
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
index 15b793a502840..22033dcf6b0f7 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx
@@ -139,7 +139,7 @@ export const schema: FormSchema = {
),
labelAppend: OptionalFieldLabel,
},
- threats: {
+ threat: {
label: i18n.translate(
'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldMitreThreatLabel',
{
@@ -155,7 +155,7 @@ export const schema: FormSchema = {
const [{ value, path }] = args;
let hasError = false;
(value as IMitreEnterpriseAttack[]).forEach(v => {
- if (isMitreAttackInvalid(v.tactic.name, v.techniques)) {
+ if (isMitreAttackInvalid(v.tactic.name, v.technique)) {
hasError = true;
}
});
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
index ce91e15cdcf0d..de6678b42df6f 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts
@@ -72,15 +72,7 @@ const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRul
};
const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => {
- const {
- falsePositives,
- references,
- riskScore,
- threats,
- timeline,
- isNew,
- ...rest
- } = aboutStepData;
+ const { falsePositives, references, riskScore, threat, timeline, isNew, ...rest } = aboutStepData;
return {
false_positives: falsePositives.filter(item => !isEmpty(item)),
references: references.filter(item => !isEmpty(item)),
@@ -91,12 +83,12 @@ const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson =>
timeline_title: timeline.title,
}
: {}),
- threats: threats
- .filter(threat => threat.tactic.name !== 'none')
- .map(threat => ({
- ...threat,
+ threat: threat
+ .filter(singleThreat => singleThreat.tactic.name !== 'none')
+ .map(singleThreat => ({
+ ...singleThreat,
framework: 'MITRE ATT&CK',
- techniques: threat.techniques.map(technique => {
+ technique: singleThreat.technique.map(technique => {
const { id, name, reference } = technique;
return { id, name, reference };
}),
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
index 3406d5bcd6950..1914f967813a1 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx
@@ -119,22 +119,17 @@ const RuleDetailsPageComponent: FC = ({
// This is used to re-trigger api rule status when user de/activate rule
const [ruleEnabled, setRuleEnabled] = useState(null);
const [ruleDetailTab, setRuleDetailTab] = useState(RuleDetailTabs.signals);
- const { aboutRuleData, defineRuleData, scheduleRuleData } = getStepsData({
- rule,
- detailsView: true,
- });
+ const { aboutRuleData, defineRuleData, scheduleRuleData } =
+ rule != null
+ ? getStepsData({
+ rule,
+ detailsView: true,
+ })
+ : { aboutRuleData: null, defineRuleData: null, scheduleRuleData: null };
const [lastSignals] = useSignalInfo({ ruleId });
const userHasNoPermissions =
canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false;
- if (
- isSignalIndexExists != null &&
- isAuthenticated != null &&
- (!isSignalIndexExists || !isAuthenticated)
- ) {
- return ;
- }
-
const title = isLoading === true || rule === null ? : rule.name;
const subTitle = useMemo(
() =>
@@ -217,6 +212,10 @@ const RuleDetailsPageComponent: FC = ({
[rule, ruleDetailTab]
);
+ const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [
+ signalIndexName,
+ ]);
+
const updateDateRangeCallback = useCallback(
(min: number, max: number) => {
setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
@@ -233,15 +232,23 @@ const RuleDetailsPageComponent: FC = ({
[ruleEnabled, setRuleEnabled]
);
+ if (
+ isSignalIndexExists != null &&
+ isAuthenticated != null &&
+ (!isSignalIndexExists || !isAuthenticated)
+ ) {
+ return ;
+ }
+
return (
<>
{hasIndexWrite != null && !hasIndexWrite && }
{userHasNoPermissions && }
-
+
{({ indicesExist, indexPattern }) => {
return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? (
- {({ to, from }) => (
+ {({ to, from, deleteQuery, setQuery }) => (
@@ -348,9 +355,12 @@ const RuleDetailsPageComponent: FC = ({
{
const defineRuleData: DefineStepRule | null =
@@ -41,9 +43,9 @@ export const getStepsData = ({
rule != null
? {
isNew: false,
- ...pick(['description', 'name', 'references', 'severity', 'tags', 'threats'], rule),
+ ...pick(['description', 'name', 'references', 'severity', 'tags', 'threat'], rule),
...(detailsView ? { name: '' } : {}),
- threats: rule.threats as IMitreEnterpriseAttack[],
+ threat: rule.threat as IMitreEnterpriseAttack[],
falsePositives: rule.false_positives,
riskScore: rule.risk_score,
timeline: {
@@ -52,15 +54,25 @@ export const getStepsData = ({
},
}
: null;
+
+ const from = dateMath.parse(rule.from) ?? moment();
+ const interval = dateMath.parse(`now-${rule.interval}`) ?? moment();
+
+ const fromDuration = moment.duration(interval.diff(from));
+ let fromHumanize = `${Math.floor(fromDuration.asHours())}h`;
+
+ if (fromDuration.asSeconds() < 60) {
+ fromHumanize = `${Math.floor(fromDuration.asSeconds())}s`;
+ } else if (fromDuration.asMinutes() < 60) {
+ fromHumanize = `${Math.floor(fromDuration.asMinutes())}m`;
+ }
+
const scheduleRuleData: ScheduleStepRule | null =
rule != null
? {
isNew: false,
...pick(['enabled', 'interval'], rule),
- from:
- rule?.meta?.from != null
- ? rule.meta.from.replace('now-', '')
- : rule.from.replace('now-', ''),
+ from: fromHumanize,
}
: null;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
index 75b1ce71efbb6..1c0ed34e92793 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx
@@ -5,14 +5,14 @@
*/
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback, useRef, useState } from 'react';
import { Redirect } from 'react-router-dom';
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
-import { DETECTION_ENGINE_PAGE_NAME } from '../../../components/link_to/redirect_to_detection_engine';
-import { FormattedRelativePreferenceDate } from '../../../components/formatted_date';
-import { getEmptyTagValue } from '../../../components/empty_value';
+import {
+ getDetectionEngineUrl,
+ getCreateRuleUrl,
+} from '../../../components/link_to/redirect_to_detection_engine';
import { DetectionEngineHeaderPage } from '../components/detection_engine_header_page';
import { WrapperPage } from '../../../components/wrapper_page';
import { SpyRoute } from '../../../utils/route/spy_routes';
@@ -44,6 +44,7 @@ const RulesPageComponent: React.FC = () => {
loading: prePackagedRuleLoading,
loadingCreatePrePackagedRules,
refetchPrePackagedRulesStatus,
+ rulesCustomInstalled,
rulesInstalled,
rulesNotInstalled,
rulesNotUpdated,
@@ -62,7 +63,6 @@ const RulesPageComponent: React.FC = () => {
const userHasNoPermissions =
canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false;
- const lastCompletedRun = undefined;
const handleCreatePrePackagedRules = useCallback(async () => {
if (createPrePackagedRules != null) {
@@ -88,7 +88,7 @@ const RulesPageComponent: React.FC = () => {
isAuthenticated != null &&
(!isSignalIndexExists || !isAuthenticated)
) {
- return ;
+ return ;
}
return (
@@ -102,22 +102,9 @@ const RulesPageComponent: React.FC = () => {
,
- }}
- />
- ) : (
- getEmptyTagValue()
- )
- }
title={i18n.PAGE_TITLE}
>
@@ -159,7 +146,7 @@ const RulesPageComponent: React.FC = () => {
@@ -182,6 +169,7 @@ const RulesPageComponent: React.FC = () => {
hasNoPermissions={userHasNoPermissions}
importCompleteToggle={importCompleteToggle}
refetchPrePackagedRulesStatus={handleRefetchPrePackagedRulesStatus}
+ rulesCustomInstalled={rulesCustomInstalled}
rulesInstalled={rulesInstalled}
rulesNotInstalled={rulesNotInstalled}
rulesNotUpdated={rulesNotUpdated}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
index 2b50e32a367ec..effaa90d685df 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts
@@ -76,7 +76,7 @@ export interface AboutStepRule extends StepRuleData {
falsePositives: string[];
tags: string[];
timeline: FieldValueTimeline;
- threats: IMitreEnterpriseAttack[];
+ threat: IMitreEnterpriseAttack[];
}
export interface DefineStepRule extends StepRuleData {
@@ -109,7 +109,7 @@ export interface AboutStepRuleJson {
tags: string[];
timeline_id?: string;
timeline_title?: string;
- threats: IMitreEnterpriseAttack[];
+ threat: IMitreEnterpriseAttack[];
}
export interface ScheduleStepRuleJson {
@@ -134,5 +134,5 @@ export interface IMitreAttack {
export interface IMitreEnterpriseAttack {
framework: string;
tactic: IMitreAttack;
- techniques: IMitreAttack[];
+ technique: IMitreAttack[];
}
diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
index d3c9df8ca0980..9ce7b8b0f71dc 100644
--- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx
@@ -103,6 +103,7 @@ const OverviewComponent: React.FC = ({
void;
filters?: esFilters.Filter[];
from: number;
indexPattern: IIndexPattern;
@@ -32,7 +33,15 @@ interface Props {
}
export const SignalsByCategory = React.memo(
- ({ filters = NO_FILTERS, from, query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, to }) => {
+ ({
+ deleteQuery,
+ filters = NO_FILTERS,
+ from,
+ query = DEFAULT_QUERY,
+ setAbsoluteRangeDatePicker,
+ setQuery,
+ to,
+ }) => {
const updateDateRangeCallback = useCallback(
(min: number, max: number) => {
setAbsoluteRangeDatePicker!({ id: 'global', from: min, to: max });
@@ -40,16 +49,21 @@ export const SignalsByCategory = React.memo(
[setAbsoluteRangeDatePicker]
);
+ const { signalIndexName } = useSignalIndex();
+
return (
{
) {}
public setup(core: CoreSetup, plugins: SetupPlugins) {
+ initTelemetry(plugins.usageCollection, this.id);
+
core.application.register({
id: this.id,
title: this.name,
diff --git a/x-pack/legacy/plugins/siem/public/store/store.ts b/x-pack/legacy/plugins/siem/public/store/store.ts
index b56132967d1e3..d3559e7a7adde 100644
--- a/x-pack/legacy/plugins/siem/public/store/store.ts
+++ b/x-pack/legacy/plugins/siem/public/store/store.ts
@@ -10,6 +10,7 @@ import { createEpicMiddleware } from 'redux-observable';
import { Observable } from 'rxjs';
import { AppApolloClient } from '../lib/lib';
+import { telemetryMiddleware } from '../lib/telemetry';
import { appSelectors } from './app';
import { timelineSelectors } from './timeline';
import { inputsSelectors } from './inputs';
@@ -42,7 +43,11 @@ export const createStore = (
}
);
- store = createReduxStore(reducer, state, composeEnhancers(applyMiddleware(epicMiddleware)));
+ store = createReduxStore(
+ reducer,
+ state,
+ composeEnhancers(applyMiddleware(epicMiddleware, telemetryMiddleware))
+ );
epicMiddleware.run(createRootEpic());
diff --git a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts
index 9f57155d4d189..730e6b884a182 100644
--- a/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts
+++ b/x-pack/legacy/plugins/siem/server/graphql/ecs/schema.gql.ts
@@ -399,7 +399,7 @@ export const ecsSchema = gql`
references: ToStringArray
severity: ToStringArray
tags: ToStringArray
- threats: ToAny
+ threat: ToAny
type: ToStringArray
size: ToStringArray
to: ToStringArray
diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts
index bc7486777b990..303262ece5c7f 100644
--- a/x-pack/legacy/plugins/siem/server/graphql/types.ts
+++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts
@@ -1017,7 +1017,7 @@ export interface RuleField {
tags?: Maybe;
- threats?: Maybe;
+ threat?: Maybe;
type?: Maybe;
@@ -4994,7 +4994,7 @@ export namespace RuleFieldResolvers {
tags?: TagsResolver, TypeParent, TContext>;
- threats?: ThreatsResolver, TypeParent, TContext>;
+ threat?: ThreatResolver, TypeParent, TContext>;
type?: TypeResolver, TypeParent, TContext>;
@@ -5112,7 +5112,7 @@ export namespace RuleFieldResolvers {
Parent = RuleField,
TContext = SiemContext
> = Resolver;
- export type ThreatsResolver<
+ export type ThreatResolver<
R = Maybe,
Parent = RuleField,
TContext = SiemContext
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
index 4a48301ee4b9c..d950d89eb22a6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
@@ -34,11 +34,11 @@ export const mockPrepackagedRule = (): PrepackagedRules => ({
severity: 'high',
query: 'user.name: root or user.name: admin',
language: 'kuery',
- threats: [
+ threat: [
{
framework: 'fake',
tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' },
- techniques: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }],
+ technique: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }],
},
],
enabled: true,
@@ -69,11 +69,11 @@ export const typicalPayload = (): Partial => ({
severity: 'high',
query: 'user.name: root or user.name: admin',
language: 'kuery',
- threats: [
+ threat: [
{
framework: 'fake',
tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' },
- techniques: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }],
+ technique: [{ id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }],
},
],
});
@@ -298,7 +298,7 @@ export const getResult = (): RuleAlertType => ({
severity: 'high',
to: 'now',
type: 'query',
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -306,7 +306,7 @@ export const getResult = (): RuleAlertType => ({
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json
index 4f3ba768b17b0..4986c100f1b0b 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json
@@ -87,8 +87,38 @@
"tags": {
"type": "keyword"
},
- "threats": {
- "type": "object"
+ "threat": {
+ "properties": {
+ "framework": {
+ "type": "keyword"
+ },
+ "tactic": {
+ "properties": {
+ "id": {
+ "type": "keyword"
+ },
+ "name": {
+ "type": "keyword"
+ },
+ "reference": {
+ "type": "keyword"
+ }
+ }
+ },
+ "technique": {
+ "properties": {
+ "id": {
+ "type": "keyword"
+ },
+ "name": {
+ "type": "keyword"
+ },
+ "reference": {
+ "type": "keyword"
+ }
+ }
+ }
+ }
},
"type": {
"type": "keyword"
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts
index 0c98507bc6fa8..68375043070f8 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts
@@ -70,7 +70,7 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
name,
severity,
tags,
- threats,
+ threat,
to,
type,
updated_at: updatedAt,
@@ -128,7 +128,7 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
tags,
to,
type,
- threats,
+ threat,
updatedAt,
references,
version,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts
index 4480186d9a7a6..060659d0e1897 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts
@@ -56,7 +56,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
name,
severity,
tags,
- threats,
+ threat,
to,
type,
updated_at: updatedAt,
@@ -115,7 +115,7 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
tags,
to,
type,
- threats,
+ threat,
updatedAt,
references,
version: 1,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts
index 67680a8f86eec..de7f0fe26cc74 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts
@@ -74,26 +74,28 @@ describe('get_prepackaged_rule_status_route', () => {
});
describe('payload', () => {
- test('0 rules installed, 1 rules not installed, and 1 rule not updated', async () => {
+ test('0 rules installed, 0 custom rules, 1 rules not installed, and 1 rule not updated', async () => {
alertsClient.find.mockResolvedValue(getFindResult());
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
const { payload } = await server.inject(getPrepackagedRulesStatusRequest());
expect(JSON.parse(payload)).toEqual({
+ rules_custom_installed: 0,
rules_installed: 0,
rules_not_installed: 1,
rules_not_updated: 0,
});
});
- test('1 rule installed, 0 rules not installed, and 1 rule to not updated', async () => {
+ test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => {
alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
const { payload } = await server.inject(getPrepackagedRulesStatusRequest());
expect(JSON.parse(payload)).toEqual({
+ rules_custom_installed: 1,
rules_installed: 1,
rules_not_installed: 0,
rules_not_updated: 1,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts
index 0208a209c5eae..ab6ee8e97a70f 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts
@@ -13,6 +13,7 @@ import { transformError } from '../utils';
import { getPrepackagedRules } from '../../rules/get_prepackaged_rules';
import { getRulesToInstall } from '../../rules/get_rules_to_install';
import { getRulesToUpdate } from '../../rules/get_rules_to_update';
+import { findRules } from '../../rules/find_rules';
import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules';
export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => {
@@ -36,10 +37,19 @@ export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => {
try {
const rulesFromFileSystem = getPrepackagedRules();
+ const customRules = await findRules({
+ alertsClient,
+ perPage: 1,
+ page: 1,
+ sortField: 'enabled',
+ sortOrder: 'desc',
+ filter: 'alert.attributes.tags:"__internal_immutable:false"',
+ });
const prepackagedRules = await getExistingPrepackagedRules({ alertsClient });
const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules);
const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules);
return {
+ rules_custom_installed: customRules.total,
rules_installed: prepackagedRules.length,
rules_not_installed: rulesToInstall.length,
rules_not_updated: rulesToUpdate.length,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
index 0dfdee2d71375..88a31c36a87fc 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
@@ -105,7 +105,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
name,
severity,
tags,
- threats,
+ threat,
to,
type,
references,
@@ -154,7 +154,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
tags,
to,
type,
- threats,
+ threat,
updatedAt: new Date().toISOString(),
references,
version,
@@ -189,7 +189,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
tags,
to,
type,
- threats,
+ threat,
references,
version,
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts
index cf98043529bc0..8c7558d6d4fb5 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts
@@ -70,7 +70,7 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
tags,
to,
type,
- threats,
+ threat,
references,
version,
} = payloadRule;
@@ -103,7 +103,7 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
tags,
to,
type,
- threats,
+ threat,
references,
version,
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts
index cbb66317186a1..f51cea0753f1a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts
@@ -54,7 +54,7 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
tags,
to,
type,
- threats,
+ threat,
references,
version,
} = request.payload;
@@ -98,7 +98,7 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
tags,
to,
type,
- threats,
+ threat,
references,
version,
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
index c1b4c7de73f68..ec11a8fb2da39 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
@@ -52,7 +52,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -60,7 +60,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -115,7 +115,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -123,7 +123,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -180,7 +180,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -188,7 +188,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -245,7 +245,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -253,7 +253,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -308,7 +308,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -316,7 +316,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -374,7 +374,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -382,7 +382,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -440,7 +440,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -448,7 +448,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -506,7 +506,7 @@ describe('utils', () => {
severity: 'high',
updated_by: 'elastic',
tags: ['tag 1', 'tag 2'],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -514,7 +514,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -623,7 +623,7 @@ describe('utils', () => {
tags: [],
to: 'now',
type: 'query',
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -631,7 +631,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -696,7 +696,7 @@ describe('utils', () => {
tags: [],
to: 'now',
type: 'query',
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -704,7 +704,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -858,7 +858,7 @@ describe('utils', () => {
tags: [],
to: 'now',
type: 'query',
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -866,7 +866,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -979,7 +979,7 @@ describe('utils', () => {
saved_id: 'some-id',
severity: 'high',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -987,7 +987,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -1039,7 +1039,7 @@ describe('utils', () => {
saved_id: 'some-id',
severity: 'high',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -1047,7 +1047,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
@@ -1088,7 +1088,7 @@ describe('utils', () => {
saved_id: 'some-id',
severity: 'high',
tags: [],
- threats: [
+ threat: [
{
framework: 'MITRE ATT&CK',
tactic: {
@@ -1096,7 +1096,7 @@ describe('utils', () => {
name: 'impact',
reference: 'https://attack.mitre.org/tactics/TA0040/',
},
- techniques: [
+ technique: [
{
id: 'T1499',
name: 'endpoint denial of service',
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
index ae79b571b2b62..663ddf3a835a6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
@@ -110,7 +110,7 @@ export const transformAlertToRule = (
tags: transformTags(alert.tags),
to: alert.params.to,
type: alert.params.type,
- threats: alert.params.threats,
+ threat: alert.params.threat,
version: alert.params.version,
status: ruleStatus?.attributes.status,
status_date: ruleStatus?.attributes.statusDate,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts
index 2a04c15b8cd9f..b536cfac05df3 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts
@@ -224,7 +224,7 @@ describe('add prepackaged rules schema', () => {
).toBeFalsy();
});
- test('You can send in an empty array to threats', () => {
+ test('You can send in an empty array to threat', () => {
expect(
addPrepackagedRulesSchema.validate>({
rule_id: 'rule-1',
@@ -241,12 +241,12 @@ describe('add prepackaged rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [],
+ threat: [],
version: 1,
}).error
).toBeFalsy();
});
- test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version, threats] does validate', () => {
+ test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version, s] does validate', () => {
expect(
addPrepackagedRulesSchema.validate>({
rule_id: 'rule-1',
@@ -259,7 +259,7 @@ describe('add prepackaged rules schema', () => {
severity: 'low',
interval: '5m',
type: 'query',
- threats: [
+ threat: [
{
framework: 'someFramework',
tactic: {
@@ -267,7 +267,7 @@ describe('add prepackaged rules schema', () => {
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -342,28 +342,7 @@ describe('add prepackaged rules schema', () => {
).toEqual(true);
});
- test('immutable cannot be false', () => {
- expect(
- addPrepackagedRulesSchema.validate>({
- rule_id: 'rule-1',
- risk_score: 50,
- description: 'some description',
- from: 'now-5m',
- to: 'now',
- index: ['index-1'],
- immutable: false,
- name: 'some-name',
- severity: 'low',
- interval: '5m',
- type: 'query',
- query: 'some-query',
- language: 'kuery',
- version: 1,
- }).error.message
- ).toEqual('child "immutable" fails because ["immutable" must be one of [true]]');
- });
-
- test('immutable can be true', () => {
+ test('immutable cannot be set in a pre-packaged rule', () => {
expect(
addPrepackagedRulesSchema.validate>({
rule_id: 'rule-1',
@@ -380,8 +359,8 @@ describe('add prepackaged rules schema', () => {
query: 'some-query',
language: 'kuery',
version: 1,
- }).error
- ).toBeFalsy();
+ }).error.message
+ ).toEqual('child "immutable" fails because ["immutable" is not allowed]');
});
test('defaults enabled to false', () => {
@@ -765,11 +744,11 @@ describe('add prepackaged rules schema', () => {
);
});
- test('You cannot send in an array of threats that are missing "framework"', () => {
+ test('You cannot send in an array of threat that are missing "framework"', () => {
expect(
addPrepackagedRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -786,14 +765,14 @@ describe('add prepackaged rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
tactic: {
id: 'fakeId',
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -805,15 +784,15 @@ describe('add prepackaged rules schema', () => {
version: 1,
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "tactic"', () => {
+ test('You cannot send in an array of threat that are missing "tactic"', () => {
expect(
addPrepackagedRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -830,10 +809,10 @@ describe('add prepackaged rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -845,15 +824,15 @@ describe('add prepackaged rules schema', () => {
version: 1,
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "techniques"', () => {
+ test('You cannot send in an array of threat that are missing "technique"', () => {
expect(
addPrepackagedRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -870,7 +849,7 @@ describe('add prepackaged rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
tactic: {
@@ -883,7 +862,7 @@ describe('add prepackaged rules schema', () => {
version: 1,
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]'
);
});
@@ -937,54 +916,6 @@ describe('add prepackaged rules schema', () => {
);
});
- test('You can optionally set the immutable to be true', () => {
- expect(
- addPrepackagedRulesSchema.validate>({
- rule_id: 'rule-1',
- risk_score: 50,
- description: 'some description',
- from: 'now-5m',
- to: 'now',
- immutable: true,
- index: ['index-1'],
- name: 'some-name',
- severity: 'low',
- interval: '5m',
- type: 'query',
- references: ['index-1'],
- query: 'some query',
- language: 'kuery',
- max_signals: 1,
- version: 1,
- }).error
- ).toBeFalsy();
- });
-
- test('You cannot set the immutable to be a number', () => {
- expect(
- addPrepackagedRulesSchema.validate<
- Partial> & { immutable: number }
- >({
- rule_id: 'rule-1',
- risk_score: 50,
- description: 'some description',
- from: 'now-5m',
- to: 'now',
- immutable: 5,
- index: ['index-1'],
- name: 'some-name',
- severity: 'low',
- interval: '5m',
- type: 'query',
- references: ['index-1'],
- query: 'some query',
- language: 'kuery',
- max_signals: 1,
- version: 1,
- }).error.message
- ).toEqual('child "immutable" fails because ["immutable" must be a boolean]');
- });
-
test('You cannot set the risk_score to 101', () => {
expect(
addPrepackagedRulesSchema.validate>({
@@ -993,7 +924,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1016,7 +946,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1039,7 +968,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1062,7 +990,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1085,7 +1012,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1113,7 +1039,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1137,7 +1062,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1183,7 +1107,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1207,7 +1130,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1232,7 +1154,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1257,7 +1178,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1282,7 +1202,6 @@ describe('add prepackaged rules schema', () => {
description: 'some description',
from: 'now-5m',
to: 'now',
- immutable: true,
index: ['index-1'],
name: 'some-name',
severity: 'low',
@@ -1322,6 +1241,7 @@ describe('add prepackaged rules schema', () => {
rule_id: 'rule-1',
risk_score: 50,
description: 'some description',
+ index: ['auditbeat-*'],
name: 'some-name',
severity: 'low',
type: 'query',
@@ -1340,6 +1260,7 @@ describe('add prepackaged rules schema', () => {
rule_id: 'rule-1',
risk_score: 50,
description: 'some description',
+ index: ['auditbeat-*'],
name: 'some-name',
severity: 'junk',
type: 'query',
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts
index d254f83243491..b62c480492c84 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts
@@ -30,7 +30,7 @@ import {
tags,
to,
type,
- threats,
+ threat,
references,
version,
} from './schemas';
@@ -42,9 +42,10 @@ import { DEFAULT_MAX_SIGNALS } from '../../../../../common/constants';
* Big differences between this schema and the createRulesSchema
* - rule_id is required here
* - output_index is not allowed (and instead the space index must be used)
- * - immutable defaults to true instead of to false and if it is there can only be true
+ * - immutable is forbidden but defaults to true instead of to false and it can only ever be true
* - enabled defaults to false instead of true
* - version is a required field that must exist
+ * - index is a required field that must exist
*/
export const addPrepackagedRulesSchema = Joi.object({
description: description.required(),
@@ -53,8 +54,11 @@ export const addPrepackagedRulesSchema = Joi.object({
filters,
from: from.default('now-6m'),
rule_id: rule_id.required(),
- immutable: immutable.default(true).valid(true),
- index,
+ immutable: immutable
+ .forbidden()
+ .default(true)
+ .valid(true),
+ index: index.required(),
interval: interval.default('5m'),
query: query.allow('').default(''),
language: language.default('kuery'),
@@ -73,7 +77,7 @@ export const addPrepackagedRulesSchema = Joi.object({
tags: tags.default([]),
to: to.default('now'),
type: type.required(),
- threats: threats.default([]),
+ threat: threat.default([]),
references: references.default([]),
version: version.required(),
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts
index f765f01300c58..d9605a265d28b 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts
@@ -239,7 +239,7 @@ describe('create rules schema', () => {
).toBeFalsy();
});
- test('You can send in an empty array to threats', () => {
+ test('You can send in an empty array to threat', () => {
expect(
createRulesSchema.validate>({
rule_id: 'rule-1',
@@ -257,12 +257,12 @@ describe('create rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [],
+ threat: [],
}).error
).toBeFalsy();
});
- test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => {
+ test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => {
expect(
createRulesSchema.validate>({
rule_id: 'rule-1',
@@ -276,7 +276,7 @@ describe('create rules schema', () => {
severity: 'low',
interval: '5m',
type: 'query',
- threats: [
+ threat: [
{
framework: 'someFramework',
tactic: {
@@ -284,7 +284,7 @@ describe('create rules schema', () => {
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -678,11 +678,11 @@ describe('create rules schema', () => {
);
});
- test('You cannot send in an array of threats that are missing "framework"', () => {
+ test('You cannot send in an array of threat that are missing "framework"', () => {
expect(
createRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -700,14 +700,14 @@ describe('create rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
tactic: {
id: 'fakeId',
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -718,15 +718,15 @@ describe('create rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "tactic"', () => {
+ test('You cannot send in an array of threat that are missing "tactic"', () => {
expect(
createRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -744,10 +744,10 @@ describe('create rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -758,15 +758,15 @@ describe('create rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "techniques"', () => {
+ test('You cannot send in an array of threat that are missing "technique"', () => {
expect(
createRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -784,7 +784,7 @@ describe('create rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
tactic: {
@@ -796,7 +796,7 @@ describe('create rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]'
);
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts
index 06dbb0cbb48f3..eb79e06c8efa6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts
@@ -30,7 +30,7 @@ import {
tags,
to,
type,
- threats,
+ threat,
references,
version,
} from './schemas';
@@ -65,7 +65,7 @@ export const createRulesSchema = Joi.object({
tags: tags.default([]),
to: to.default('now'),
type: type.required(),
- threats: threats.default([]),
+ threat: threat.default([]),
references: references.default([]),
version: version.default(1),
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts
index b19a91d18c3ff..c72e1b7ef2a63 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts
@@ -244,7 +244,7 @@ describe('import rules schema', () => {
).toBeFalsy();
});
- test('You can send in an empty array to threats', () => {
+ test('You can send in an empty array to threat', () => {
expect(
importRulesSchema.validate>({
rule_id: 'rule-1',
@@ -262,12 +262,12 @@ describe('import rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [],
+ threat: [],
}).error
).toBeFalsy();
});
- test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => {
+ test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threat] does validate', () => {
expect(
importRulesSchema.validate>({
rule_id: 'rule-1',
@@ -281,7 +281,7 @@ describe('import rules schema', () => {
severity: 'low',
interval: '5m',
type: 'query',
- threats: [
+ threat: [
{
framework: 'someFramework',
tactic: {
@@ -289,7 +289,7 @@ describe('import rules schema', () => {
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -685,11 +685,11 @@ describe('import rules schema', () => {
);
});
- test('You cannot send in an array of threats that are missing "framework"', () => {
+ test('You cannot send in an array of threat that are missing "framework"', () => {
expect(
importRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -707,14 +707,14 @@ describe('import rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
tactic: {
id: 'fakeId',
name: 'fakeName',
reference: 'fakeRef',
},
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -725,15 +725,15 @@ describe('import rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "framework" fails because ["framework" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "tactic"', () => {
+ test('You cannot send in an array of threat that are missing "tactic"', () => {
expect(
importRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -751,10 +751,10 @@ describe('import rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
- techniques: [
+ technique: [
{
id: 'techniqueId',
name: 'techniqueName',
@@ -765,15 +765,15 @@ describe('import rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]'
);
});
- test('You cannot send in an array of threats that are missing "techniques"', () => {
+ test('You cannot send in an array of threat that are missing "technique"', () => {
expect(
importRulesSchema.validate<
- Partial> & {
- threats: Array>>;
+ Partial> & {
+ threat: Array>>;
}
>({
rule_id: 'rule-1',
@@ -791,7 +791,7 @@ describe('import rules schema', () => {
query: 'some query',
language: 'kuery',
max_signals: 1,
- threats: [
+ threat: [
{
framework: 'fake',
tactic: {
@@ -803,7 +803,7 @@ describe('import rules schema', () => {
],
}).error.message
).toEqual(
- 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]'
+ 'child "threat" fails because ["threat" at position 0 fails because [child "technique" fails because ["technique" is required]]]'
);
});
@@ -857,7 +857,7 @@ describe('import rules schema', () => {
);
});
- test('You can optionally set the immutable to be true', () => {
+ test('You can optionally set the immutable to be false', () => {
expect(
importRulesSchema.validate