({ darkMode: false });
+ core.theme.theme$ = subject.asObservable();
+
+ return [core, subject];
+ };
+
+ test('returns the value from the theme', () => {
+ const [core] = mock();
+ const { Provider } = createKibanaReactContext(core);
+
+ ReactDOM.render(
+
+
+ ,
+ container
+ );
+
+ const div = container!.querySelector('div');
+ expect(div!.textContent).toBe('false');
+ });
+
+ test('value changes if the theme changes', () => {
+ const [core, subject] = mock();
+ const { Provider } = createKibanaReactContext(core);
+
+ ReactDOM.render(
+
+
+ ,
+ container
+ );
+
+ let div = container!.querySelector('div');
+ expect(div!.textContent).toBe('false');
+
+ act(() => {
+ subject.next({ darkMode: true });
+ });
+
+ div = container!.querySelector('div');
+ expect(div!.textContent).toBe('true');
+ });
+});
diff --git a/src/plugins/kibana_react/public/dark_mode/use_dark_mode.ts b/src/plugins/kibana_react/public/dark_mode/use_dark_mode.ts
new file mode 100644
index 00000000000000..c53a7b8687aad6
--- /dev/null
+++ b/src/plugins/kibana_react/public/dark_mode/use_dark_mode.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import useObservable from 'react-use/lib/useObservable';
+import { useKibana } from '../context';
+
+export const useDarkMode = (defaultValue?: boolean): boolean => {
+ const {
+ services: { theme },
+ } = useKibana();
+
+ if (!theme) {
+ if (defaultValue !== undefined) {
+ return defaultValue;
+ }
+ throw new TypeError('theme service not available in kibana-react context.');
+ }
+
+ const currentTheme = useObservable(theme.theme$, theme.getTheme());
+ return currentTheme.darkMode;
+};
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index 05abdbd84e91c6..5b4e8a9bcbae0c 100644
--- a/src/plugins/kibana_react/public/index.ts
+++ b/src/plugins/kibana_react/public/index.ts
@@ -42,6 +42,8 @@ export {
useGlobalUiSetting$,
} from './ui_settings';
+export { useDarkMode } from './dark_mode';
+
export { useExecutionContext } from './use_execution_context';
export { reactRouterNavigate, reactRouterOnClickHandler } from './react_router_navigate';
@@ -79,6 +81,7 @@ export { KibanaThemeProvider, wrapWithTheme, type KibanaThemeProviderProps } fro
export function plugin() {
return new (class KibanaReactPlugin {
setup() {}
+
start() {}
})();
}
diff --git a/src/plugins/kibana_react/public/markdown/_markdown.scss b/src/plugins/kibana_react/public/markdown/_markdown.scss
index a3bba38509bcdb..c11aefe1f4d97c 100644
--- a/src/plugins/kibana_react/public/markdown/_markdown.scss
+++ b/src/plugins/kibana_react/public/markdown/_markdown.scss
@@ -14,7 +14,7 @@
$kbnDefaultFontSize: 14px;
@function canvasToEm($size) {
- @return #{calc($size / $kbnDefaultFontSize)}em;
+ @return #{$size / $kbnDefaultFontSize}em;
}
.kbnMarkdown__body {
diff --git a/src/plugins/share/common/url_service/locators/redirect/parse_search_params.ts b/src/plugins/share/common/url_service/locators/redirect/parse_search_params.ts
index a4711c30db5d09..c66bf56b1858fc 100644
--- a/src/plugins/share/common/url_service/locators/redirect/parse_search_params.ts
+++ b/src/plugins/share/common/url_service/locators/redirect/parse_search_params.ts
@@ -22,7 +22,9 @@ import type { RedirectOptions } from './types';
* @param urlSearch Search part of URL path.
* @returns Parsed out locator ID, version, and locator params.
*/
-export function parseSearchParams(urlSearch: string): RedirectOptions {
+export function parseSearchParams(
+ urlSearch: string
+): RedirectOptions
{
const search = new URLSearchParams(urlSearch);
const id = search.get('l');
@@ -66,7 +68,7 @@ export function parseSearchParams(urlSearch: string): RedirectOptions {
throw new Error(message);
}
- let params: unknown & SerializableRecord;
+ let params: P;
try {
params = JSON.parse(paramsJson);
} catch {
diff --git a/src/plugins/unified_search/public/filter_bar/filter_item/filter_item.scss b/src/plugins/unified_search/public/filter_bar/filter_item/filter_item.scss
index 362aec7264983e..1c16adbfc8c133 100644
--- a/src/plugins/unified_search/public/filter_bar/filter_item/filter_item.scss
+++ b/src/plugins/unified_search/public/filter_bar/filter_item/filter_item.scss
@@ -8,8 +8,8 @@
line-height: $euiSize;
border: none;
color: $euiTextColor;
- padding-top: calc($euiSizeM / 2) + 1px;
- padding-bottom: calc($euiSizeM / 2) + 1px;
+ padding-top: $euiSizeM / 2 + 1px;
+ padding-bottom: $euiSizeM / 2 + 1px;
white-space: normal; /* 1 */
&:not(.globalFilterItem-isDisabled) {
@@ -54,8 +54,8 @@
left: 0;
width: $euiSizeXS;
background-color: $kbnGlobalFilterItemBorderColor;
- border-top-left-radius: calc($euiBorderRadius / 2);
- border-bottom-left-radius: calc($euiBorderRadius / 2);
+ border-top-left-radius: $euiBorderRadius / 2;
+ border-bottom-left-radius: $euiBorderRadius / 2;
}
}
diff --git a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.scss b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.scss
index 2e6f639ea792d7..7ce304310ae56a 100644
--- a/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.scss
+++ b/src/plugins/unified_search/public/saved_query_management/saved_query_management_list.scss
@@ -5,7 +5,7 @@
}
.kbnSavedQueryManagement__text {
- padding: $euiSizeM $euiSizeM calc($euiSizeM / 2) $euiSizeM;
+ padding: $euiSizeM $euiSizeM ($euiSizeM / 2) $euiSizeM;
}
.kbnSavedQueryManagement__list {
@@ -13,5 +13,5 @@
max-height: inherit; // Fixes overflow for applied max-height
// Left/Right padding is calculated to match the left alignment of the
// popover text and buttons
- padding: calc($euiSizeM / 2) $euiSizeXS !important; // Override flush
+ padding: ($euiSizeM / 2) $euiSizeXS !important; // Override flush
}
diff --git a/src/plugins/vis_default_editor/public/_agg_params.scss b/src/plugins/vis_default_editor/public/_agg_params.scss
index c56ef94c3a4baf..81faa06681c0d9 100644
--- a/src/plugins/vis_default_editor/public/_agg_params.scss
+++ b/src/plugins/vis_default_editor/public/_agg_params.scss
@@ -1,7 +1,7 @@
.visEditorAggParam--half {
margin: $euiSize 0;
display: inline-block;
- width: calc(50% - #{calc($euiSizeS / 2)});
+ width: calc(50% - #{$euiSizeS / 2});
}
.visEditorAggParam--half-size {
diff --git a/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss
index 036cf3f6a8fbdc..9e09a6c3477f3c 100644
--- a/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss
+++ b/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss
@@ -20,7 +20,7 @@
> .tvbVis {
// Apply the minimum height on the vis itself so it doesn't interfere with flex calculations
// Gauges are not completely square, so the height is just slightly less than the width
- min-height: calc($euiSize * 12 / 1.25);
+ min-height: $euiSize * 12 / 1.25;
}
}
diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss
index d5eb056dd172e4..bc2ce4f1a9e441 100644
--- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss
+++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss
@@ -101,7 +101,7 @@
.tvbVisMetric__label--additional {
@include euiTextTruncate;
font-size: .25em; /* 1 */
- padding: calc($euiSizeXS / 2) 0 0;
+ padding: ($euiSizeXS / 2) 0 0;
text-align: center;
color: $tvbValueColor;
line-height: 1.2; // Ensure the descenders don't get cut off
diff --git a/src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss b/src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss
index 8b92af5a4fdcf9..4612602d93f1cc 100644
--- a/src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss
+++ b/src/plugins/vis_types/vislib/public/vislib/lib/layout/_layout.scss
@@ -203,7 +203,7 @@
}
.slice {
- stroke-width: calc($euiSizeXS / 2);
+ stroke-width: $euiSizeXS / 2;
stroke: $euiColorEmptyShade;
&:hover {
diff --git a/test/scripts/checks/baseline_plugin_public_api_docs.sh b/test/scripts/checks/baseline_plugin_public_api_docs.sh
deleted file mode 100755
index 72de7c0980a5fc..00000000000000
--- a/test/scripts/checks/baseline_plugin_public_api_docs.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-###
-### rebuild plugin api docs to ensure it's not out of date
-###
-echo " -- building api docs"
-node --max-old-space-size=12000 scripts/build_api_docs
diff --git a/test/scripts/checks/bundle_limits.sh b/test/scripts/checks/bundle_limits.sh
deleted file mode 100755
index 10d9d9343fda4e..00000000000000
--- a/test/scripts/checks/bundle_limits.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/build_kibana_platform_plugins --validate-limits
diff --git a/test/scripts/checks/commit/commit.sh b/test/scripts/checks/commit/commit.sh
deleted file mode 100755
index 180f6dfb56e29b..00000000000000
--- a/test/scripts/checks/commit/commit.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-# Runs pre-commit hook script for the files touched in the last commit.
-# That way we can ensure a set of quick commit checks earlier as we removed
-# the pre-commit hook installation by default.
-# If files are more than 200 we will skip it and just use
-# the further ci steps that already check linting and file casing for the entire repo.
-"$(dirname "${0}")/commit_check_runner.sh"
diff --git a/test/scripts/checks/commit/commit_check_runner.sh b/test/scripts/checks/commit/commit_check_runner.sh
deleted file mode 100755
index 65ca9a6ecef06e..00000000000000
--- a/test/scripts/checks/commit/commit_check_runner.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-run_quick_commit_checks() {
- echo "!!!!!!!! ATTENTION !!!!!!!!
-That check is intended to provide earlier CI feedback after we remove the automatic install for the local pre-commit hook.
-If you want, you can still manually install the pre-commit hook locally by running 'node scripts/register_git_hook locally'
-!!!!!!!!!!!!!!!!!!!!!!!!!!!
-"
-
- node scripts/precommit_hook.js --ref HEAD~1..HEAD --max-files 200
-}
-
-run_quick_commit_checks
diff --git a/test/scripts/checks/file_casing.sh b/test/scripts/checks/file_casing.sh
deleted file mode 100755
index 1a2240d0562ff2..00000000000000
--- a/test/scripts/checks/file_casing.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/check_file_casing --quiet
diff --git a/test/scripts/checks/i18n.sh b/test/scripts/checks/i18n.sh
deleted file mode 100755
index 468b8394081e1b..00000000000000
--- a/test/scripts/checks/i18n.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/i18n_check --ignore-missing
diff --git a/test/scripts/checks/jest_configs.sh b/test/scripts/checks/jest_configs.sh
deleted file mode 100755
index cebcbc63bb3961..00000000000000
--- a/test/scripts/checks/jest_configs.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/check_jest_configs
diff --git a/test/scripts/checks/licenses.sh b/test/scripts/checks/licenses.sh
deleted file mode 100755
index 8a19cdc2fc126e..00000000000000
--- a/test/scripts/checks/licenses.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/check_licenses --dev
diff --git a/test/scripts/checks/plugin_list_docs.sh b/test/scripts/checks/plugin_list_docs.sh
deleted file mode 100644
index b0f49d78458410..00000000000000
--- a/test/scripts/checks/plugin_list_docs.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-###
-### rebuild plugin list to ensure it's not out of date
-###
-echo " -- building plugin list docs"
-node scripts/build_plugin_list_docs
-
-###
-### verify no git modifications
-###
-GIT_CHANGES="$(git ls-files --modified)"
-if [ "$GIT_CHANGES" ]; then
- echo -e "\n${RED}ERROR: 'node scripts/build_plugin_list_docs' caused changes to the following files:${C_RESET}\n"
- echo -e "$GIT_CHANGES\n"
- exit 1
-fi
diff --git a/test/scripts/checks/telemetry.sh b/test/scripts/checks/telemetry.sh
deleted file mode 100755
index 09b2305f9d607b..00000000000000
--- a/test/scripts/checks/telemetry.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/telemetry_check
diff --git a/test/scripts/checks/test_hardening.sh b/test/scripts/checks/test_hardening.sh
deleted file mode 100755
index 332edb0fcde685..00000000000000
--- a/test/scripts/checks/test_hardening.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/test_hardening
diff --git a/test/scripts/checks/test_projects.sh b/test/scripts/checks/test_projects.sh
deleted file mode 100755
index 6a1a8b958c4aac..00000000000000
--- a/test/scripts/checks/test_projects.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-yarn kbn run-in-packages test
diff --git a/test/scripts/checks/ts_projects.sh b/test/scripts/checks/ts_projects.sh
deleted file mode 100755
index 9963d10792f948..00000000000000
--- a/test/scripts/checks/ts_projects.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/check_ts_projects
diff --git a/test/scripts/checks/type_check_plugin_public_api_docs.sh b/test/scripts/checks/type_check_plugin_public_api_docs.sh
deleted file mode 100755
index b5fed38e192d28..00000000000000
--- a/test/scripts/checks/type_check_plugin_public_api_docs.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/build_ts_refs \
- --clean \
- --no-cache \
- --force
-
-node scripts/type_check
-
-echo " -- building api docs"
-node --max-old-space-size=12000 scripts/build_api_docs
diff --git a/test/scripts/checks/verify_notice.sh b/test/scripts/checks/verify_notice.sh
deleted file mode 100755
index 55dd1c04aaf8a1..00000000000000
--- a/test/scripts/checks/verify_notice.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/notice --validate
diff --git a/test/scripts/jenkins_accessibility.sh b/test/scripts/jenkins_accessibility.sh
deleted file mode 100755
index fa582cf2d97d0a..00000000000000
--- a/test/scripts/jenkins_accessibility.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/accessibility/config.ts;
diff --git a/test/scripts/jenkins_apm_cypress.sh b/test/scripts/jenkins_apm_cypress.sh
deleted file mode 100755
index 2ccd7d760fba52..00000000000000
--- a/test/scripts/jenkins_apm_cypress.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running APM cypress tests"
-cd "$XPACK_DIR"
-
-node plugins/apm/scripts/test/e2e.js
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_build_kbn_sample_panel_action.sh b/test/scripts/jenkins_build_kbn_sample_panel_action.sh
deleted file mode 100755
index 67c3da246ed7cf..00000000000000
--- a/test/scripts/jenkins_build_kbn_sample_panel_action.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-cd test/plugin_functional/plugins/kbn_sample_panel_action;
-if [[ ! -d "target" ]]; then
- yarn build;
-fi
-cd -;
diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh
deleted file mode 100755
index 28d4feef3a4b90..00000000000000
--- a/test/scripts/jenkins_build_kibana.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-
-cd "$KIBANA_DIR"
-source src/dev/ci_setup/setup_env.sh
-
-if [[ ! "$TASK_QUEUE_PROCESS_ID" ]]; then
- ./test/scripts/jenkins_build_plugins.sh
-fi
-
-# doesn't persist, also set in kibanaPipeline.groovy
-export KBN_NP_PLUGINS_BUILT=true
-
-# Do not build kibana for code coverage run
-if [[ -z "$CODE_COVERAGE" ]] ; then
- echo " -> building and extracting default Kibana distributable for use in functional tests"
- node scripts/build --debug
-
- echo " -> shipping metrics from build to ci-stats"
- node scripts/ship_ci_stats \
- --metrics target/optimizer_bundle_metrics.json \
- --metrics build/kibana/node_modules/@kbn/ui-shared-deps-src/shared_built_assets/metrics.json
-
- linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')"
- installDir="$KIBANA_DIR/install/kibana"
- mkdir -p "$installDir"
- tar -xzf "$linuxBuild" -C "$installDir" --strip=1
- cp "$linuxBuild" "$WORKSPACE/kibana-default.tar.gz"
-
- mkdir -p "$WORKSPACE/kibana-build"
- cp -pR install/kibana/. $WORKSPACE/kibana-build/
-
- echo " -> Archive built plugins"
- shopt -s globstar
- tar -zcf \
- "$WORKSPACE/kibana-default-plugins.tar.gz" \
- x-pack/plugins/**/target/public \
- x-pack/test/**/target/public \
- examples/**/target/public \
- x-pack/examples/**/target/public \
- test/**/target/public
- shopt -u globstar
-fi
diff --git a/test/scripts/jenkins_build_load_testing.sh b/test/scripts/jenkins_build_load_testing.sh
deleted file mode 100755
index f64caa3c02cabb..00000000000000
--- a/test/scripts/jenkins_build_load_testing.sh
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env bash
-
-while getopts s: flag
-do
- case "${flag}" in
- s) simulations=${OPTARG};;
- esac
-done
-echo "Simulation classes: $simulations";
-
-cd "$KIBANA_DIR"
-source src/dev/ci_setup/setup_env.sh
-
-if [[ ! "$TASK_QUEUE_PROCESS_ID" ]]; then
- ./test/scripts/jenkins_xpack_build_plugins.sh
-fi
-
-echo " -> Configure Metricbeat monitoring"
-# Configure Metricbeat monitoring for Kibana and ElasticSearch, ingest monitoring data into Kibana Stats cluster
-# Getting the URL
-TOP="$(curl -L http://snapshots.elastic.co/latest/master.json)"
-MB_BUILD=$(echo $TOP | sed 's/.*"version" : "\(.*\)", "build_id.*/\1/')
-echo $MB_BUILD
-MB_BUILD_ID=$(echo $TOP | sed 's/.*"build_id" : "\(.*\)", "manifest_url.*/\1/')
-
-URL=https://snapshots.elastic.co/${MB_BUILD_ID}/downloads/beats/metricbeat/metricbeat-${MB_BUILD}-linux-x86_64.tar.gz
-URL=https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-7.11.0-linux-x86_64.tar.gz
-echo $URL
-# Downloading the Metricbeat package
-while [ 1 ]; do
- wget -q --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 0 --continue --no-check-certificate --tries=3 $URL
- if [ $? = 0 ]; then break; fi; # check return value, break if successful (0)
- sleep 1s;
-done;
-
-# Install Metricbeat
-echo "untar metricbeat and config"
-#tar -xzf metricbeat-${MB_BUILD}-linux-x86_64.tar.gz
-tar -xzf metricbeat-7.11.0-linux-x86_64.tar.gz
-#mv metricbeat-${MB_BUILD}-linux-x86_64 metricbeat-install
-mv metricbeat-7.11.0-linux-x86_64 metricbeat-install
-
-# Configure Metricbeat
-echo " -> Changing metricbeat config"
-pushd ../kibana-load-testing
-cp cfg/metricbeat/elasticsearch-xpack.yml $KIBANA_DIR/metricbeat-install/modules.d/elasticsearch-xpack.yml
-cp cfg/metricbeat/kibana-xpack.yml $KIBANA_DIR/metricbeat-install/modules.d/kibana-xpack.yml
-echo "fields.build: ${BUILD_ID}" >> cfg/metricbeat/metricbeat.yml
-echo "path.config: ${KIBANA_DIR}/metricbeat-install" >> cfg/metricbeat/metricbeat.yml
-echo "cloud.auth: ${USER_FROM_VAULT}:${PASS_FROM_VAULT}" >> cfg/metricbeat/metricbeat.yml
-cp cfg/metricbeat/metricbeat.yml $KIBANA_DIR/metricbeat-install/metricbeat.yml
-# Disable system monitoring: enabled for now to have more data
-#mv $KIBANA_DIR/metricbeat-install/modules.d/system.yml $KIBANA_DIR/metricbeat-install/modules.d/system.yml.disabled
-echo " -> Building puppeteer project"
-cd puppeteer
-yarn install && yarn build
-popd
-
-# doesn't persist, also set in kibanaPipeline.groovy
-export KBN_NP_PLUGINS_BUILT=true
-
-echo " -> Building and extracting default Kibana distributable for use in functional tests"
-cd "$KIBANA_DIR"
-node scripts/build --debug
-linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')"
-installDir="$KIBANA_DIR/install/kibana"
-mkdir -p "$installDir"
-tar -xzf "$linuxBuild" -C "$installDir" --strip=1
-
-mkdir -p "$WORKSPACE/kibana-build"
-cp -pR install/kibana/. $WORKSPACE/kibana-build/
-
-echo " -> Setup env for tests"
-source test/scripts/jenkins_test_setup_xpack.sh
-
-# Start Metricbeat
-echo " -> Starting metricbeat"
-pushd $KIBANA_DIR/metricbeat-install
-nohup ./metricbeat > metricbeat.log 2>&1 &
-popd
-
-echo " -> Running gatling load testing"
-export GATLING_SIMULATIONS="$simulations"
-node scripts/functional_tests \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/load/config.ts;
-
-
-echo " -> Simulations run is finished"
-
-# Show output of Metricbeat. Disabled. Enable for debug purposes
-#echo "output of metricbeat.log"
-#cat $KIBANA_DIR/metricbeat-install/metricbeat.log
diff --git a/test/scripts/jenkins_build_plugins.sh b/test/scripts/jenkins_build_plugins.sh
deleted file mode 100755
index dd1715065e7996..00000000000000
--- a/test/scripts/jenkins_build_plugins.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-echo " -> building kibana platform plugins"
-node scripts/build_kibana_platform_plugins \
- --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
- --scan-dir "$KIBANA_DIR/test/health_gateway/plugins" \
- --scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \
- --scan-dir "$KIBANA_DIR/test/common/plugins" \
- --scan-dir "$KIBANA_DIR/examples" \
- --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
- --scan-dir "$KIBANA_DIR/test/common/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \
- --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/plugins" \
- --scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \
- --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \
- --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \
- --scan-dir "$XPACK_DIR/test/security_functional/fixtures/common" \
- --scan-dir "$XPACK_DIR/examples" \
- --workers 12
diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh
deleted file mode 100755
index dde224823789b3..00000000000000
--- a/test/scripts/jenkins_ci_group.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-if [[ -z "$CODE_COVERAGE" ]]; then
- echo " -> Running functional and api tests"
-
- node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --include-tag "ciGroup$CI_GROUP"
-
- if [[ ! "$TASK_QUEUE_PROCESS_ID" && "$CI_GROUP" == "1" ]]; then
- source test/scripts/jenkins_build_kbn_sample_panel_action.sh
- ./test/scripts/test/plugin_functional.sh
- ./test/scripts/test/health_gateway.sh
- ./test/scripts/test/interpreter_functional.sh
- fi
-else
- echo " -> Running Functional tests with code coverage"
- export NODE_OPTIONS=--max_old_space_size=8192
-
- echo " -> making hard link clones"
- cd ..
- cp -RlP kibana "kibana${CI_GROUP}"
- cd "kibana${CI_GROUP}"
-
- echo " -> running tests from the clone folder"
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
-
- echo " -> moving junit output, silently fail in case of no report"
- mkdir -p ../kibana/target/junit
- mv target/junit/* ../kibana/target/junit/ || echo "copying junit failed"
-
- echo " -> copying screenshots and html for failures"
- cp -r test/functional/screenshots/* ../kibana/test/functional/screenshots/ || echo "copying screenshots failed"
- cp -r test/functional/failure_debug ../kibana/test/functional/ || echo "copying html failed"
-fi
diff --git a/test/scripts/jenkins_cloud.sh b/test/scripts/jenkins_cloud.sh
deleted file mode 100755
index 57798a9afcac17..00000000000000
--- a/test/scripts/jenkins_cloud.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-# This script runs kibana tests compatible with cloud.
-#
-# The cloud instance setup is done in the elastic/elastic-stack-testing framework,
-# where the following environment variables are set pointing to the cloud instance.
-#
-# export TEST_KIBANA_HOSTNAME
-# export TEST_KIBANA_PROTOCOL=
-# export TEST_KIBANA_PORT=
-# export TEST_KIBANA_USER=
-# export TEST_KIBANA_PASS=
-#
-# export TEST_ES_HOSTNAME=
-# export TEST_ES_PROTOCOL=
-# export TEST_ES_PORT=
-# export TEST_ES_USER=
-# export TEST_ES_PASS=
-#
-
-set -e
-
-source "$(dirname $0)/../../src/dev/ci_setup/setup.sh"
-
-export TEST_BROWSER_HEADLESS=1
-node scripts/functional_test_runner --debug --exclude-tag skipCloud $@
diff --git a/test/scripts/jenkins_firefox_smoke.sh b/test/scripts/jenkins_firefox_smoke.sh
deleted file mode 100755
index 4566b11822bf58..00000000000000
--- a/test/scripts/jenkins_firefox_smoke.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --bail --debug \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --include-tag "includeFirefox" \
- --config test/functional/config.firefox.js;
diff --git a/test/scripts/jenkins_fleet_cypress.sh b/test/scripts/jenkins_fleet_cypress.sh
deleted file mode 100755
index e43259c1c1c3f6..00000000000000
--- a/test/scripts/jenkins_fleet_cypress.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running fleet cypress tests"
-cd "$XPACK_DIR"
-
-cd x-pack/plugins/fleet
-yarn --cwd x-pack/plugins/fleet cypress:run
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_osquery_cypress.sh b/test/scripts/jenkins_osquery_cypress.sh
deleted file mode 100755
index b4a9420ff94402..00000000000000
--- a/test/scripts/jenkins_osquery_cypress.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running osquery cypress tests"
-cd "$XPACK_DIR"
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/osquery_cypress/cli_config.ts
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_plugin_functional.sh b/test/scripts/jenkins_plugin_functional.sh
deleted file mode 100755
index 984e648bf6b848..00000000000000
--- a/test/scripts/jenkins_plugin_functional.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-cd test/plugin_functional/plugins/kbn_sample_panel_action;
-if [[ ! -d "target" ]]; then
- yarn build;
-fi
-cd -;
-
-pwd
-
-./test/scripts/test/plugin_functional.sh
-./test/scripts/test/health_gateway.sh
-./test/scripts/test/interpreter_functional.sh
diff --git a/test/scripts/jenkins_runbld_junit.sh b/test/scripts/jenkins_runbld_junit.sh
deleted file mode 100755
index bcb6accd5f8cd4..00000000000000
--- a/test/scripts/jenkins_runbld_junit.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-# This file just exists to give runbld something to invoke before processing junit reports
-echo 'Processing junit reports with runbld...'
diff --git a/test/scripts/jenkins_security_solution_cypress_chrome.sh b/test/scripts/jenkins_security_solution_cypress_chrome.sh
deleted file mode 100755
index 0605a319896ce7..00000000000000
--- a/test/scripts/jenkins_security_solution_cypress_chrome.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running security solution cypress tests"
-cd "$XPACK_DIR"
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/security_solution_cypress/cli_config.ts
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_setup.sh b/test/scripts/jenkins_setup.sh
deleted file mode 100755
index 8c8492d10e6026..00000000000000
--- a/test/scripts/jenkins_setup.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/load_env_keys.sh
-source src/dev/ci_setup/extract_bootstrap_cache.sh
-source src/dev/ci_setup/setup.sh
-source src/dev/ci_setup/checkout_sibling_es.sh
\ No newline at end of file
diff --git a/test/scripts/jenkins_setup_parallel_workspace.sh b/test/scripts/jenkins_setup_parallel_workspace.sh
deleted file mode 100755
index 5274d05572e713..00000000000000
--- a/test/scripts/jenkins_setup_parallel_workspace.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-CURRENT_DIR=$(pwd)
-
-# Copy everything except node_modules into the current workspace
-rsync -a ${WORKSPACE}/kibana/* . --exclude node_modules
-rsync -a ${WORKSPACE}/kibana/.??* .
-
-# Symlink all non-root, non-fixture node_modules into our new workspace
-cd ${WORKSPACE}/kibana
-find . -type d -name node_modules -not -path '*__fixtures__*' -not -path './node_modules*' -prune -print0 | xargs -0I % ln -s "${WORKSPACE}/kibana/%" "${CURRENT_DIR}/%"
-find . -type d -wholename '*__fixtures__*node_modules' -not -path './node_modules*' -prune -print0 | xargs -0I % cp -R "${WORKSPACE}/kibana/%" "${CURRENT_DIR}/%"
-cd "${CURRENT_DIR}"
-
-# Symlink all of the individual root-level node_modules into the node_modules/ directory
-mkdir -p node_modules
-ln -s ${WORKSPACE}/kibana/node_modules/* node_modules/
-ln -s ${WORKSPACE}/kibana/node_modules/.??* node_modules/
-
-# Copy a few node_modules instead of symlinking them. They don't work correctly if symlinked
-unlink node_modules/@kbn
-unlink node_modules/css-loader
-unlink node_modules/style-loader
-
-# packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts will fail if this is a symlink
-unlink node_modules/val-loader
-
-cp -R ${WORKSPACE}/kibana/node_modules/@kbn node_modules/
-cp -R ${WORKSPACE}/kibana/node_modules/css-loader node_modules/
-cp -R ${WORKSPACE}/kibana/node_modules/style-loader node_modules/
-cp -R ${WORKSPACE}/kibana/node_modules/val-loader node_modules/
diff --git a/test/scripts/jenkins_storybook.sh b/test/scripts/jenkins_storybook.sh
deleted file mode 100755
index 058c58ed922eba..00000000000000
--- a/test/scripts/jenkins_storybook.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-cd "$KIBANA_DIR"
-
-yarn storybook --site apm
-yarn storybook --site canvas
-yarn storybook --site cell_actions
-yarn storybook --site ci_composite
-yarn storybook --site content_management
-yarn storybook --site custom_integrations
-yarn storybook --site dashboard
-yarn storybook --site dashboard_enhanced
-yarn storybook --site data
-yarn storybook --site embeddable
-yarn storybook --site expression_error
-yarn storybook --site expression_image
-yarn storybook --site expression_metric
-yarn storybook --site expression_repeat_image
-yarn storybook --site expression_reveal_image
-yarn storybook --site expression_shape
-yarn storybook --site expression_tagcloud
-yarn storybook --site fleet
-yarn storybook --site infra
-yarn storybook --site kibana_react
-yarn storybook --site lists
-yarn storybook --site observability
-yarn storybook --site presentation
-yarn storybook --site security_solution
-yarn storybook --site solution_side_nav
-yarn storybook --site shared_ux
-yarn storybook --site ui_actions_enhanced
diff --git a/test/scripts/jenkins_test_setup.sh b/test/scripts/jenkins_test_setup.sh
deleted file mode 100755
index 05b88aa2dd0a20..00000000000000
--- a/test/scripts/jenkins_test_setup.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-function post_work() {
- set +e
- if [[ -z "$REMOVE_KIBANA_INSTALL_DIR" && -z "$KIBANA_INSTALL_DIR" && -d "$KIBANA_INSTALL_DIR" ]]; then
- rm -rf "$REMOVE_KIBANA_INSTALL_DIR"
- fi
-}
-
-trap 'post_work' EXIT
-
-export TEST_BROWSER_HEADLESS=1
-
-source src/dev/ci_setup/setup_env.sh
-
-# For parallel workspaces, we should copy the .es directory from the root, because it should already have downloaded snapshots in it
-# This isn't part of jenkins_setup_parallel_workspace.sh just because not all tasks require ES
-if [[ ! -d .es && -d "$WORKSPACE/kibana/.es" ]]; then
- cp -R $WORKSPACE/kibana/.es ./
-fi
diff --git a/test/scripts/jenkins_test_setup_oss.sh b/test/scripts/jenkins_test_setup_oss.sh
deleted file mode 100755
index 29d396667c465a..00000000000000
--- a/test/scripts/jenkins_test_setup_oss.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup.sh
-
-if [[ -z "$CODE_COVERAGE" ]]; then
- destDir="$WORKSPACE/kibana-build-${TASK_QUEUE_PROCESS_ID:-$CI_PARALLEL_PROCESS_NUMBER}"
-
- if [[ ! -d $destDir ]]; then
- mkdir -p $destDir
- cp -pR "$WORKSPACE/kibana-build/." $destDir/
- fi
-
- export KIBANA_INSTALL_DIR="$destDir"
-fi
diff --git a/test/scripts/jenkins_test_setup_xpack.sh b/test/scripts/jenkins_test_setup_xpack.sh
deleted file mode 100755
index 31acc4f4865e2e..00000000000000
--- a/test/scripts/jenkins_test_setup_xpack.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup.sh
-
-if [[ -z "$CODE_COVERAGE" ]]; then
- destDir="$WORKSPACE/kibana-build-${TASK_QUEUE_PROCESS_ID:-$CI_PARALLEL_PROCESS_NUMBER}"
-
- if [[ ! -d $destDir ]]; then
- mkdir -p $destDir
- cp -pR "$WORKSPACE/kibana-build/." $destDir/
- fi
-
- export KIBANA_INSTALL_DIR="$(realpath $destDir)"
-
- cd "$XPACK_DIR"
-fi
diff --git a/test/scripts/jenkins_uptime_playwright.sh b/test/scripts/jenkins_uptime_playwright.sh
deleted file mode 100755
index 5bea30a223cd41..00000000000000
--- a/test/scripts/jenkins_uptime_playwright.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running synthetics @elastic/synthetics tests"
-cd "$XPACK_DIR"
-
-node plugins/synthetics/scripts/e2e.js
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_ux_synthetics.sh b/test/scripts/jenkins_ux_synthetics.sh
deleted file mode 100755
index acf2611e36b948..00000000000000
--- a/test/scripts/jenkins_ux_synthetics.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-echo " -> Running User Experience plugin @elastic/synthetics tests"
-cd "$XPACK_DIR"
-
-node plugins/ux/scripts/e2e.js
-
-echo ""
-echo ""
diff --git a/test/scripts/jenkins_xpack_accessibility.sh b/test/scripts/jenkins_xpack_accessibility.sh
deleted file mode 100755
index b1daa0ada1d50e..00000000000000
--- a/test/scripts/jenkins_xpack_accessibility.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/accessibility/config.ts;
diff --git a/test/scripts/jenkins_xpack_baseline.sh b/test/scripts/jenkins_xpack_baseline.sh
deleted file mode 100755
index a0a98ccd5a5e7b..00000000000000
--- a/test/scripts/jenkins_xpack_baseline.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-source "$KIBANA_DIR/src/dev/ci_setup/setup_percy.sh"
-
-echo " -> building and extracting default Kibana distributable"
-cd "$KIBANA_DIR"
-node scripts/build --debug
-
-echo " -> shipping metrics from build to ci-stats"
-node scripts/ship_ci_stats \
- --metrics target/optimizer_bundle_metrics.json \
- --metrics build/kibana/node_modules/@kbn/ui-shared-deps-src/shared_built_assets/metrics.json
-
-linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')"
-installDir="$KIBANA_DIR/install/kibana"
-mkdir -p "$installDir"
-tar -xzf "$linuxBuild" -C "$installDir" --strip=1
-
-mkdir -p "$WORKSPACE/kibana-build"
-cp -pR install/kibana/. $WORKSPACE/kibana-build/
-
-cd "$KIBANA_DIR"
-source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
diff --git a/test/scripts/jenkins_xpack_build_plugins.sh b/test/scripts/jenkins_xpack_build_plugins.sh
deleted file mode 100755
index bdf6ee24555270..00000000000000
--- a/test/scripts/jenkins_xpack_build_plugins.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-echo " -> building kibana platform plugins"
-node scripts/build_kibana_platform_plugins \
- --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
- --scan-dir "$KIBANA_DIR/test/common/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \
- --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/plugins" \
- --scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \
- --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \
- --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \
- --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \
- --scan-dir "$XPACK_DIR/test/security_functional/fixtures/common" \
- --scan-dir "$KIBANA_DIR/examples" \
- --scan-dir "$XPACK_DIR/examples" \
- --workers 12
diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh
deleted file mode 100755
index 59bcf45a2089f8..00000000000000
--- a/test/scripts/jenkins_xpack_ci_group.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-if [[ -z "$CODE_COVERAGE" ]]; then
- echo " -> Running functional and api tests"
-
- node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --include-tag "ciGroup$CI_GROUP"
-
- echo ""
- echo ""
-else
- echo " -> Running X-Pack functional tests with code coverage"
- export NODE_OPTIONS=--max_old_space_size=8192
-
- echo " -> making hard link clones"
- cd ..
- cp -RlP kibana "kibana${CI_GROUP}"
- cd "kibana${CI_GROUP}/x-pack"
-
- echo " -> running tests from the clone folder"
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
-
- echo " -> moving junit output, silently fail in case of no report"
- mkdir -p ../../kibana/target/junit
- mv ../target/junit/* ../../kibana/target/junit/ || echo "copying junit failed"
-
- echo " -> copying screenshots and html for failures"
- cp -r test/functional/screenshots/* ../../kibana/x-pack/test/functional/screenshots/ || echo "copying screenshots failed"
- cp -r test/functional/failure_debug ../../kibana/x-pack/test/functional/ || echo "copying html failed"
-fi
diff --git a/test/scripts/jenkins_xpack_firefox_smoke.sh b/test/scripts/jenkins_xpack_firefox_smoke.sh
deleted file mode 100755
index de19d3867520d5..00000000000000
--- a/test/scripts/jenkins_xpack_firefox_smoke.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --include-tag "includeFirefox" \
- --config test/functional/config.firefox.js \
- --config test/functional_embedded/config.firefox.ts;
diff --git a/test/scripts/jenkins_xpack_saved_objects_field_metrics.sh b/test/scripts/jenkins_xpack_saved_objects_field_metrics.sh
deleted file mode 100755
index fc3a7db06a43bd..00000000000000
--- a/test/scripts/jenkins_xpack_saved_objects_field_metrics.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_xpack.sh
-
-node scripts/functional_tests \
- --debug --bail \
- --kibana-install-dir "$KIBANA_INSTALL_DIR" \
- --config test/saved_objects_field_count/config.ts;
diff --git a/test/scripts/lint/eslint.sh b/test/scripts/lint/eslint.sh
deleted file mode 100755
index 8395df85c5d309..00000000000000
--- a/test/scripts/lint/eslint.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/eslint --no-cache
diff --git a/test/scripts/lint/stylelint.sh b/test/scripts/lint/stylelint.sh
deleted file mode 100755
index 2f500c7e14aaa0..00000000000000
--- a/test/scripts/lint/stylelint.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/stylelint
diff --git a/test/scripts/test/api_integration.sh b/test/scripts/test/api_integration.sh
deleted file mode 100755
index 06263c38b07283..00000000000000
--- a/test/scripts/test/api_integration.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/functional_tests \
- --config test/api_integration/config.js \
- --bail \
- --debug
diff --git a/test/scripts/test/health_gateway.sh b/test/scripts/test/health_gateway.sh
deleted file mode 100755
index 18a9b81b083de5..00000000000000
--- a/test/scripts/test/health_gateway.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --config test/health_gateway/config.ts \
- --bail \
- --debug \
- --kibana-install-dir $KIBANA_INSTALL_DIR
diff --git a/test/scripts/test/interpreter_functional.sh b/test/scripts/test/interpreter_functional.sh
deleted file mode 100755
index 2a40c81c34ad02..00000000000000
--- a/test/scripts/test/interpreter_functional.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --config test/interpreter_functional/config.ts \
- --bail \
- --debug \
- --kibana-install-dir $KIBANA_INSTALL_DIR
diff --git a/test/scripts/test/jest_integration.sh b/test/scripts/test/jest_integration.sh
deleted file mode 100755
index 3b27ba06842bec..00000000000000
--- a/test/scripts/test/jest_integration.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node --max-old-space-size=5120 scripts/jest_integration --ci
diff --git a/test/scripts/test/jest_unit.sh b/test/scripts/test/jest_unit.sh
deleted file mode 100755
index f368554e357608..00000000000000
--- a/test/scripts/test/jest_unit.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-source src/dev/ci_setup/setup_env.sh
-
-node scripts/jest --ci --maxWorkers=6
diff --git a/test/scripts/test/plugin_functional.sh b/test/scripts/test/plugin_functional.sh
deleted file mode 100755
index 115ddb81d3e45e..00000000000000
--- a/test/scripts/test/plugin_functional.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --config test/plugin_functional/config.ts \
- --bail \
- --debug
diff --git a/test/scripts/test/server_integration.sh b/test/scripts/test/server_integration.sh
deleted file mode 100755
index fa4c4c6ce2c356..00000000000000
--- a/test/scripts/test/server_integration.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-source test/scripts/jenkins_test_setup_oss.sh
-
-node scripts/functional_tests \
- --config test/server_integration/http/ssl/config.js \
- --config test/server_integration/http/ssl_redirect/config.js \
- --config test/server_integration/http/platform/config.ts \
- --config test/server_integration/http/ssl_with_p12/config.js \
- --config test/server_integration/http/ssl_with_p12_intermediate/config.js \
- --bail \
- --debug \
- --kibana-install-dir $KIBANA_INSTALL_DIR
-
-# Tests that must be run against source in order to build test plugins
-node scripts/functional_tests \
- --config test/server_integration/http/platform/config.status.ts \
- --bail \
- --debug
diff --git a/vars/agentInfo.groovy b/vars/agentInfo.groovy
deleted file mode 100644
index 166a86c1692616..00000000000000
--- a/vars/agentInfo.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-def print() {
- catchError(catchInterruptions: false, buildResult: null) {
- def startTime = sh(script: "date -d '-3 minutes' -Iseconds | sed s/+/%2B/", returnStdout: true).trim()
- def endTime = sh(script: "date -d '+1 hour 30 minutes' -Iseconds | sed s/+/%2B/", returnStdout: true).trim()
-
- def resourcesUrl =
- (
- "https://infra-stats.elastic.co/app/kibana#/visualize/edit/8bd92360-1b92-11ea-b719-aba04518cc34" +
- "?_g=(time:(from:'${startTime}',to:'${endTime}'))" +
- "&_a=(query:'host.name:${env.NODE_NAME}')"
- )
- .replaceAll("'", '%27') // Need to escape ' because of the shell echo below, but can't really replace "'" with "\'" because of groovy sandbox
- .replaceAll(/\)$/, '%29') // This is just here because the URL parsing in the Jenkins console doesn't work right
-
- def logsStartTime = sh(script: "date -d '-3 minutes' +%s", returnStdout: true).trim()
- def logsUrl =
- (
- "https://infra-stats.elastic.co/app/infra#/logs" +
- "?_g=()&flyoutOptions=(flyoutId:!n,flyoutVisibility:hidden,surroundingLogsId:!n)" +
- "&logFilter=(expression:'host.name:${env.NODE_NAME}',kind:kuery)" +
- "&logPosition=(position:(time:${logsStartTime}000),streamLive:!f)"
- )
- .replaceAll("'", '%27')
- .replaceAll('\\)', '%29')
-
- sh script: """
- set +x
- echo 'Resource Graph:'
- echo '${resourcesUrl}'
- echo ''
- echo 'Agent Logs:'
- echo '${logsUrl}'
- echo ''
- echo 'SSH Command:'
- echo "ssh -F ssh_config \$(hostname --ip-address)"
- """, label: "Worker/Agent/Node debug links"
- }
-}
-
-return this
diff --git a/vars/buildState.groovy b/vars/buildState.groovy
deleted file mode 100644
index 365705661350ce..00000000000000
--- a/vars/buildState.groovy
+++ /dev/null
@@ -1,30 +0,0 @@
-import groovy.transform.Field
-
-public static @Field JENKINS_BUILD_STATE = [:]
-
-def add(key, value) {
- if (!buildState.JENKINS_BUILD_STATE.containsKey(key)) {
- buildState.JENKINS_BUILD_STATE[key] = value
- return true
- }
-
- return false
-}
-
-def set(key, value) {
- buildState.JENKINS_BUILD_STATE[key] = value
-}
-
-def get(key) {
- return buildState.JENKINS_BUILD_STATE[key]
-}
-
-def has(key) {
- return buildState.JENKINS_BUILD_STATE.containsKey(key)
-}
-
-def get() {
- return buildState.JENKINS_BUILD_STATE
-}
-
-return this
diff --git a/vars/catchErrors.groovy b/vars/catchErrors.groovy
deleted file mode 100644
index 2a1b55d832606c..00000000000000
--- a/vars/catchErrors.groovy
+++ /dev/null
@@ -1,15 +0,0 @@
-// Basically, this is a shortcut for catchError(catchInterruptions: false) {}
-// By default, catchError will swallow aborts/timeouts, which we almost never want
-// Also, by wrapping it in an additional try/catch, we cut down on spam in Pipeline Steps
-def call(Map params = [:], Closure closure) {
- try {
- closure()
- } catch (ex) {
- params.catchInterruptions = false
- catchError(params) {
- throw ex
- }
- }
-}
-
-return this
diff --git a/vars/esSnapshots.groovy b/vars/esSnapshots.groovy
deleted file mode 100644
index 884fbcdb17aebb..00000000000000
--- a/vars/esSnapshots.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-def promote(snapshotVersion, snapshotId) {
- def snapshotDestination = "${snapshotVersion}/archives/${snapshotId}"
- def MANIFEST_URL = "https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${snapshotDestination}/manifest.json"
-
- dir('verified-manifest') {
- def verifiedSnapshotFilename = 'manifest-latest-verified.json'
-
- sh """
- curl -O '${MANIFEST_URL}'
- mv manifest.json ${verifiedSnapshotFilename}
- """
-
- googleStorageUpload(
- credentialsId: 'kibana-ci-gcs-plugin',
- bucket: "gs://kibana-ci-es-snapshots-daily/${snapshotVersion}",
- pattern: verifiedSnapshotFilename,
- sharedPublicly: false,
- showInline: false,
- )
- }
-
- // This would probably be more efficient if we could just copy using gsutil and specifying buckets for src and dest
- // But we don't currently have access to the GCS credentials in a way that can be consumed easily from here...
- dir('transfer-to-permanent') {
- googleStorageDownload(
- credentialsId: 'kibana-ci-gcs-plugin',
- bucketUri: "gs://kibana-ci-es-snapshots-daily/${snapshotDestination}/*",
- localDirectory: '.',
- pathPrefix: snapshotDestination,
- )
-
- def manifestJson = readFile file: 'manifest.json'
- writeFile(
- file: 'manifest.json',
- text: manifestJson.replace("kibana-ci-es-snapshots-daily/${snapshotDestination}", "kibana-ci-es-snapshots-permanent/${snapshotVersion}")
- )
-
- // Ideally we would have some delete logic here before uploading,
- // But we don't currently have access to the GCS credentials in a way that can be consumed easily from here...
- googleStorageUpload(
- credentialsId: 'kibana-ci-gcs-plugin',
- bucket: "gs://kibana-ci-es-snapshots-permanent/${snapshotVersion}",
- pattern: '*.*',
- sharedPublicly: false,
- showInline: false,
- )
- }
-}
-
-return this
diff --git a/vars/getCheckoutInfo.groovy b/vars/getCheckoutInfo.groovy
deleted file mode 100644
index f9d797f8127c7a..00000000000000
--- a/vars/getCheckoutInfo.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-def call(branchOverride) {
- def repoInfo = [
- branch: branchOverride ?: env.ghprbSourceBranch,
- targetBranch: env.ghprbTargetBranch,
- targetsTrackedBranch: true
- ]
-
- if (repoInfo.branch == null) {
- if (!(params.branch_specifier instanceof String)) {
- throw new Exception(
- "Unable to determine branch automatically, either pass a branch name to getCheckoutInfo() or use the branch_specifier param."
- )
- }
-
- // strip prefix from the branch specifier to make it consistent with ghprbSourceBranch
- repoInfo.branch = params.branch_specifier.replaceFirst(/^(refs\/heads\/|origin\/)/, "")
- }
-
- repoInfo.commit = sh(
- script: "git rev-parse HEAD",
- label: "determining checked out sha",
- returnStdout: true
- ).trim()
-
- if (repoInfo.targetBranch) {
- // Try to clone fetch from Github up to 8 times, waiting 15 secs between attempts
- retryWithDelay(8, 15) {
- sh(
- script: "git fetch origin ${repoInfo.targetBranch}",
- label: "fetch latest from '${repoInfo.targetBranch}' at origin"
- )
- }
-
- repoInfo.mergeBase = sh(
- script: "git merge-base HEAD FETCH_HEAD",
- label: "determining merge point with '${repoInfo.targetBranch}' at origin",
- returnStdout: true
- ).trim()
-
- def pkgJson = readFile("package.json")
- def releaseBranch = toJSON(pkgJson).branch
- repoInfo.targetsTrackedBranch = releaseBranch == repoInfo.targetBranch
- }
-
- print "repoInfo: ${repoInfo}"
-
- return repoInfo
-}
-
-return this
diff --git a/vars/githubCommitStatus.groovy b/vars/githubCommitStatus.groovy
deleted file mode 100644
index 175dbe0c90542f..00000000000000
--- a/vars/githubCommitStatus.groovy
+++ /dev/null
@@ -1,57 +0,0 @@
-def defaultCommit() {
- if (buildState.has('checkoutInfo')) {
- return buildState.get('checkoutInfo').commit
- }
-}
-
-def onStart(commit = defaultCommit(), context = 'kibana-ci') {
- catchError {
- if (githubPr.isPr() || !commit) {
- return
- }
-
- create(commit, 'pending', 'Build started.', context)
- }
-}
-
-def onFinish(commit = defaultCommit(), context = 'kibana-ci') {
- catchError {
- if (githubPr.isPr() || !commit) {
- return
- }
-
- def status = buildUtils.getBuildStatus()
-
- if (status == 'SUCCESS' || status == 'UNSTABLE') {
- create(commit, 'success', 'Build completed successfully.', context)
- } else if(status == 'ABORTED') {
- create(commit, 'error', 'Build aborted or timed out.', context)
- } else {
- create(commit, 'error', 'Build failed.', context)
- }
- }
-}
-
-def trackBuild(commit, context, Closure closure) {
- onStart(commit, context)
- catchError {
- closure()
- }
- onFinish(commit, context)
-}
-
-// state: error|failure|pending|success
-def create(sha, state, description, context, targetUrl = null) {
- targetUrl = targetUrl ?: env.BUILD_URL
-
- withGithubCredentials {
- return githubApi.post("repos/elastic/kibana/statuses/${sha}", [
- state: state,
- description: description,
- context: context,
- target_url: targetUrl.toString()
- ])
- }
-}
-
-return this
diff --git a/vars/githubPr.groovy b/vars/githubPr.groovy
deleted file mode 100644
index 594d54f2c5b5e6..00000000000000
--- a/vars/githubPr.groovy
+++ /dev/null
@@ -1,369 +0,0 @@
-/**
- Wraps the main/important part of a job, executes it, and then publishes a comment to GitHub with the status.
-
- It will check for the existence of GHPRB env variables before doing any actual PR work,
- so it can be used to wrap code that is executed in both PR and non-PR contexts.
-
- Inside the comment, it will hide a JSON blob containing build data (status, etc).
-
- Then, the next time it posts a comment, it will:
- 1. Read the previous comment and parse the json
- 2. Create a new comment, add a summary of up to 5 previous builds to it, and append this build's data to the hidden JSON
- 3. Delete the old comment
-
- So, there is only ever one build status comment on a PR at any given time, the most recent one.
-*/
-def withDefaultPrComments(closure) {
- catchErrors {
- // kibanaPipeline.notifyOnError() needs to know if comments are enabled, so lets track it with a global
- // isPr() just ensures this functionality is skipped for non-PR builds
- buildState.set('PR_COMMENTS_ENABLED', isPr())
- catchErrors {
- closure()
- }
- sendComment(true)
- }
-}
-
-def sendComment(isFinal = false) {
- if (!buildState.get('PR_COMMENTS_ENABLED')) {
- return
- }
-
- def status = buildUtils.getBuildStatus()
- if (status == "ABORTED") {
- return
- }
-
- def lastComment = getLatestBuildComment()
- def info = getLatestBuildInfo(lastComment) ?: [:]
- info.builds = (info.builds ?: []).takeRight(5) // Rotate out old builds
-
- // If two builds are running at the same time, the first one should not post a comment after the second one
- if (info.number && info.number.toInteger() > env.BUILD_NUMBER.toInteger()) {
- return
- }
-
- def shouldUpdateComment = !!info.builds.find { it.number == env.BUILD_NUMBER }
-
- def message = getNextCommentMessage(info, isFinal)
-
- if (shouldUpdateComment) {
- updateComment(lastComment.id, message)
- } else {
- createComment(message)
-
- if (lastComment && lastComment.user.login == 'kibanamachine') {
- deleteComment(lastComment.id)
- }
- }
-}
-
-// Checks whether or not this currently executing build was triggered via a PR in the elastic/kibana repo
-def isPr() {
- return !!(env.ghprbPullId && env.ghprbPullLink && env.ghprbPullLink =~ /\/elastic\/kibana\//)
-}
-
-def isTrackedBranchPr() {
- return isPr() && (env.ghprbTargetBranch == 'master' || env.ghprbTargetBranch == '6.8' || env.ghprbTargetBranch =~ /[7-8]\.[x0-9]+/)
-}
-
-def getLatestBuildComment() {
- return getComments()
- .reverse()
- .find { (it.user.login == 'elasticmachine' || it.user.login == 'kibanamachine') && it.body =~ //
- if (!matches || !matches[0]) {
- return null
- }
-
- return toJSON(matches[0][1].trim())
-}
-
-def getLatestBuildInfo() {
- return getLatestBuildInfo(getLatestBuildComment())
-}
-
-def getLatestBuildInfo(comment) {
- return comment ? getBuildInfoFromComment(comment.body) : null
-}
-
-def getHistoryText(builds) {
- if (!builds || builds.size() < 1) {
- return ""
- }
-
- def list = builds
- .reverse()
- .collect { build ->
- if (build.status == "SUCCESS") {
- return "* :green_heart: [Build #${build.number}](${build.url}) succeeded ${build.commit}"
- } else if(build.status == "UNSTABLE") {
- return "* :yellow_heart: [Build #${build.number}](${build.url}) was flaky ${build.commit}"
- } else {
- return "* :broken_heart: [Build #${build.number}](${build.url}) failed ${build.commit}"
- }
- }
- .join("\n")
-
- return "### History\n${list}"
-}
-
-def getTestFailuresMessage() {
- def failures = testUtils.getFailures()
- if (!failures) {
- return ""
- }
-
- def messages = []
- messages << "---\n\n### [Test Failures](${env.BUILD_URL}testReport)"
-
- failures.take(3).each { failure ->
- messages << """
-${failure.fullDisplayName}
-
-[Link to Jenkins](${failure.url})
-"""
-
- if (failure.stdOut) {
- messages << "\n#### Standard Out\n```\n${failure.stdOut}\n```"
- }
-
- if (failure.stdErr) {
- messages << "\n#### Standard Error\n```\n${failure.stdErr}\n```"
- }
-
- if (failure.stacktrace) {
- messages << "\n#### Stack Trace\n```\n${failure.stacktrace}\n```"
- }
-
- messages << " \n\n---"
- }
-
- if (failures.size() > 3) {
- messages << "and ${failures.size() - 3} more failures, only showing the first 3."
- }
-
- return messages.join("\n")
-}
-
-def getBuildStatusIncludingMetrics() {
- def status = buildUtils.getBuildStatus()
-
- if (status == 'SUCCESS' && shouldCheckCiMetricSuccess() && !ciStats.getMetricsSuccess()) {
- return 'FAILURE'
- }
-
- return status
-}
-
-def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) {
- def info = previousCommentInfo ?: [:]
- info.builds = previousCommentInfo.builds ?: []
-
- // When we update an in-progress comment, we need to remove the old version from the history
- info.builds = info.builds.findAll { it.number != env.BUILD_NUMBER }
-
- def messages = []
-
- def status = isFinal
- ? getBuildStatusIncludingMetrics()
- : buildUtils.getBuildStatus()
-
- def storybooksUrl = buildState.get('storybooksUrl')
- def storybooksMessage = storybooksUrl ? "* [Storybooks Preview](${storybooksUrl})" : "* Storybooks not built"
-
- if (!isFinal) {
- storybooksMessage = storybooksUrl ? storybooksMessage : "* Storybooks not built yet"
-
- def failuresPart = status != 'SUCCESS' ? ', with failures' : ''
- messages << """
- ## :hourglass_flowing_sand: Build in-progress${failuresPart}
- * [continuous-integration/kibana-ci/pull-request](${env.BUILD_URL})
- * Commit: ${getCommitHash()}
- ${storybooksMessage}
- * This comment will update when the build is complete
- """
- } else if (status == 'SUCCESS') {
- messages << """
- ## :green_heart: Build Succeeded
- * [continuous-integration/kibana-ci/pull-request](${env.BUILD_URL})
- * Commit: ${getCommitHash()}
- ${storybooksMessage}
- ${getDocsChangesLink()}
- """
- } else if(status == 'UNSTABLE') {
- def message = """
- ## :yellow_heart: Build succeeded, but was flaky
- * [continuous-integration/kibana-ci/pull-request](${env.BUILD_URL})
- * Commit: ${getCommitHash()}
- ${storybooksMessage}
- ${getDocsChangesLink()}
- """.stripIndent()
-
- def failures = retryable.getFlakyFailures()
- if (failures && failures.size() > 0) {
- def list = failures.collect { " * ${it.label}" }.join("\n")
- message += "* Flaky suites:\n${list}"
- }
-
- messages << message
- } else {
- messages << """
- ## :broken_heart: Build Failed
- * [continuous-integration/kibana-ci/pull-request](${env.BUILD_URL})
- * Commit: ${getCommitHash()}
- ${storybooksMessage}
- * [Pipeline Steps](${env.BUILD_URL}flowGraphTable) (look for red circles / failed steps)
- * [Interpreting CI Failures](https://www.elastic.co/guide/en/kibana/current/interpreting-ci-failures.html)
- ${getDocsChangesLink()}
- """
- }
-
- if (status != 'SUCCESS' && status != 'UNSTABLE') {
- try {
- def steps = getFailedSteps()
- if (steps?.size() > 0) {
- def list = steps.collect { "* [${it.displayName}](${it.logs})" }.join("\n")
- messages << "### Failed CI Steps\n${list}"
- }
- } catch (ex) {
- buildUtils.printStacktrace(ex)
- print "Error retrieving failed pipeline steps for PR comment, will skip this section"
- }
- }
-
- messages << getTestFailuresMessage()
-
- catchErrors {
- if (isFinal && isTrackedBranchPr()) {
- messages << ciStats.getMetricsReport()
- }
- }
-
- if (info.builds && info.builds.size() > 0) {
- messages << getHistoryText(info.builds)
- }
-
- messages << "To update your PR or re-run it, just comment with:\n`@elasticmachine merge upstream`"
-
- catchErrors {
- def assignees = getAssignees()
- if (assignees) {
- messages << "cc " + assignees.collect { "@${it}"}.join(" ")
- }
- }
-
- info.builds << [
- status: status,
- url: env.BUILD_URL,
- number: env.BUILD_NUMBER,
- commit: getCommitHash()
- ]
-
- messages << """
-
- """
-
- return messages
- .findAll { !!it } // No blank strings
- .collect { it.stripIndent().trim() } // This just allows us to indent various strings above, but leaves them un-indented in the comment
- .join("\n\n")
-}
-
-def createComment(message) {
- if (!isPr()) {
- error "Trying to post a GitHub PR comment on a non-PR or non-elastic PR build"
- }
-
- withGithubCredentials {
- return githubApi.post("repos/elastic/kibana/issues/${env.ghprbPullId}/comments", [ body: message ])
- }
-}
-
-def getComments() {
- withGithubCredentials {
- return githubIssues.getComments(env.ghprbPullId)
- }
-}
-
-def updateComment(commentId, message) {
- if (!isPr()) {
- error "Trying to post a GitHub PR comment on a non-PR or non-elastic PR build"
- }
-
- withGithubCredentials {
- def path = "repos/elastic/kibana/issues/comments/${commentId}"
- def json = toJSON([ body: message ]).toString()
-
- def resp = githubApi([ path: path ], [ method: "POST", data: json, headers: [ "X-HTTP-Method-Override": "PATCH" ] ])
- return toJSON(resp)
- }
-}
-
-def deleteComment(commentId) {
- withGithubCredentials {
- def path = "repos/elastic/kibana/issues/comments/${commentId}"
- return githubApi([ path: path ], [ method: "DELETE" ])
- }
-}
-
-def getCommitHash() {
- return env.ghprbActualCommit
-}
-
-def getDocsChangesLink() {
- def url = "https://kibana_${env.ghprbPullId}.docs-preview.app.elstc.co/diff"
-
- try {
- // httpRequest throws on status codes >400 and failures
- def resp = httpRequest([ method: "GET", url: url ])
-
- if (resp.contains("There aren't any differences!")) {
- return ""
- }
-
- return "* [Documentation Changes](${url})"
- } catch (ex) {
- print "Failed to reach ${url}"
- buildUtils.printStacktrace(ex)
- }
-
- return ""
-}
-
-def getFailedSteps() {
- return jenkinsApi.getFailedSteps()?.findAll { step ->
- step.displayName != 'Check out from version control'
- }
-}
-
-def shouldCheckCiMetricSuccess() {
- // disable ciMetrics success check when a PR is targetting a non-tracked branch
- if (buildState.has('checkoutInfo') && !buildState.get('checkoutInfo').targetsTrackedBranch) {
- return false
- }
-
- return true
-}
-
-def getPR() {
- withGithubCredentials {
- def path = "repos/elastic/kibana/pulls/${env.ghprbPullId}"
- return githubApi.get(path)
- }
-}
-
-def getAssignees() {
- def pr = getPR()
- if (!pr) {
- return []
- }
-
- return pr.assignees.collect { it.login }
-}
diff --git a/vars/jenkinsApi.groovy b/vars/jenkinsApi.groovy
deleted file mode 100644
index 57818593ffeb23..00000000000000
--- a/vars/jenkinsApi.groovy
+++ /dev/null
@@ -1,21 +0,0 @@
-def getSteps() {
- def url = "${env.BUILD_URL}api/json?tree=actions[nodes[iconColor,running,displayName,id,parents]]"
- def responseRaw = httpRequest([ method: "GET", url: url ])
- def response = toJSON(responseRaw)
-
- def graphAction = response?.actions?.find { it._class == "org.jenkinsci.plugins.workflow.job.views.FlowGraphAction" }
-
- return graphAction?.nodes
-}
-
-def getFailedSteps() {
- def steps = getSteps()
- def failedSteps = steps?.findAll { (it.iconColor == "red" || it.iconColor == "red_anime") && it._class == "org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode" }
- failedSteps.each { step ->
- step.logs = "${env.BUILD_URL}execution/node/${step.id}/log".toString()
- }
-
- return failedSteps
-}
-
-return this
diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy
deleted file mode 100644
index 374219d8006000..00000000000000
--- a/vars/kibanaPipeline.groovy
+++ /dev/null
@@ -1,496 +0,0 @@
-def withPostBuildReporting(Map params, Closure closure) {
- try {
- closure()
- } finally {
- def parallelWorkspaces = []
- try {
- parallelWorkspaces = getParallelWorkspaces()
- } catch(ex) {
- print ex
- }
-
- if (params.runErrorReporter) {
- catchErrors {
- runErrorReporter([pwd()] + parallelWorkspaces)
- }
- }
-
- catchErrors {
- publishJunit()
- }
-
- catchErrors {
- def parallelWorkspace = "${env.WORKSPACE}/parallel"
- if (fileExists(parallelWorkspace)) {
- dir(parallelWorkspace) {
- def workspaceTasks = [:]
-
- parallelWorkspaces.each { workspaceDir ->
- workspaceTasks[workspaceDir] = {
- dir(workspaceDir) {
- catchErrors {
- runbld.junit()
- }
- }
- }
- }
-
- if (workspaceTasks) {
- parallel(workspaceTasks)
- }
- }
- }
- }
- }
-}
-
-def getParallelWorkspaces() {
- def workspaces = []
- def parallelWorkspace = "${env.WORKSPACE}/parallel"
- if (fileExists(parallelWorkspace)) {
- dir(parallelWorkspace) {
- // findFiles only returns files if you use glob, so look for a file that should be in every valid workspace
- workspaces = findFiles(glob: '*/kibana/package.json')
- .collect {
- // get the paths to the kibana directories for the parallel workspaces
- return parallelWorkspace + '/' + it.path.tokenize('/').dropRight(1).join('/')
- }
- }
- }
-
- return workspaces
-}
-
-def notifyOnError(Closure closure) {
- try {
- closure()
- } catch (ex) {
- // If this is the first failed step, it's likely that the error hasn't propagated up far enough to mark the build as a failure
- currentBuild.result = 'FAILURE'
- catchErrors {
- githubPr.sendComment(false)
- }
- catchErrors {
- // an empty map is a valid config, but is falsey, so let's use .has()
- if (buildState.has('SLACK_NOTIFICATION_CONFIG')) {
- slackNotifications.sendFailedBuild(buildState.get('SLACK_NOTIFICATION_CONFIG'))
- }
- }
- throw ex
- }
-}
-
-def withFunctionalTestEnv(List additionalEnvs = [], Closure closure) {
- // This can go away once everything that uses the deprecated workers.parallelProcesses() is moved to task queue
- def parallelId = env.TASK_QUEUE_PROCESS_ID ?: env.CI_PARALLEL_PROCESS_NUMBER
-
- def kibanaPort = "61${parallelId}1"
- def esPort = "62${parallelId}1"
- // Ports 62x2-62x9 kept open for ES nodes
- def esTransportPort = "63${parallelId}1-63${parallelId}9"
- def fleetPackageRegistryPort = "64${parallelId}1"
- def alertingProxyPort = "64${parallelId}2"
- def corsTestServerPort = "64${parallelId}3"
- // needed for https://github.com/elastic/kibana/issues/107246
- def proxyTestServerPort = "64${parallelId}4"
- def contextPropagationOnly = githubPr.isPr() ? "true" : "false"
-
- withEnv([
- "CI_GROUP=${parallelId}",
- "REMOVE_KIBANA_INSTALL_DIR=1",
- "CI_PARALLEL_PROCESS_NUMBER=${parallelId}",
- "TEST_KIBANA_HOST=localhost",
- "TEST_KIBANA_PORT=${kibanaPort}",
- "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}",
- "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}",
- "TEST_ES_TRANSPORT_PORT=${esTransportPort}",
- "TEST_CORS_SERVER_PORT=${corsTestServerPort}",
- "TEST_PROXY_SERVER_PORT=${proxyTestServerPort}",
- "KBN_NP_PLUGINS_BUILT=true",
- "FLEET_PACKAGE_REGISTRY_PORT=${fleetPackageRegistryPort}",
- "ALERTING_PROXY_PORT=${alertingProxyPort}",
- "ELASTIC_APM_ACTIVE=true",
- "ELASTIC_APM_CONTEXT_PROPAGATION_ONLY=${contextPropagationOnly}",
- "ELASTIC_APM_TRANSACTION_SAMPLE_RATE=0.1",
- ] + additionalEnvs) {
- closure()
- }
-}
-
-def functionalTestProcess(String name, Closure closure) {
- return {
- notifyOnError {
- withFunctionalTestEnv(["JOB=${name}"], closure)
- }
- }
-}
-
-def functionalTestProcess(String name, String script) {
- return functionalTestProcess(name) {
- retryable(name) {
- runbld(script, "Execute ${name}")
- }
- }
-}
-
-def ossCiGroupProcess(ciGroup, withDelay = false) {
- return functionalTestProcess("ciGroup" + ciGroup) {
- if (withDelay && !(ciGroup instanceof String) && !(ciGroup instanceof GString)) {
- sleep((ciGroup-1)*30) // smooth out CPU spikes from ES startup
- }
-
- withEnv([
- "CI_GROUP=${ciGroup}",
- "JOB=kibana-ciGroup${ciGroup}",
- ]) {
- retryable("kibana-ciGroup${ciGroup}") {
- runbld("./test/scripts/jenkins_ci_group.sh", "Execute kibana-ciGroup${ciGroup}")
- }
- }
- }
-}
-
-def xpackCiGroupProcess(ciGroup, withDelay = false) {
- return functionalTestProcess("xpack-ciGroup" + ciGroup) {
- if (withDelay && !(ciGroup instanceof String) && !(ciGroup instanceof GString)) {
- sleep((ciGroup-1)*30) // smooth out CPU spikes from ES startup
- }
- withEnv([
- "CI_GROUP=${ciGroup}",
- "JOB=xpack-kibana-ciGroup${ciGroup}",
- ]) {
- retryable("xpack-kibana-ciGroup${ciGroup}") {
- runbld("./test/scripts/jenkins_xpack_ci_group.sh", "Execute xpack-kibana-ciGroup${ciGroup}")
- }
- }
- }
-}
-
-def uploadGcsArtifact(uploadPrefix, pattern) {
- googleStorageUpload(
- credentialsId: 'kibana-ci-gcs-plugin',
- bucket: "gs://${uploadPrefix}",
- pattern: pattern,
- sharedPublicly: true,
- showInline: true,
- )
-}
-
-def withGcsArtifactUpload(workerName, closure) {
- def uploadPrefix = "kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}"
- def ARTIFACT_PATTERNS = [
- 'target/junit/**/*',
- 'target/kibana-*',
- 'target/kibana-coverage/jest/**/*',
- 'target/kibana-security-solution/**/*.png',
- 'target/kibana-fleet/**/*.png',
- 'target/test-metrics/*',
- 'target/test-suites-ci-plan.json',
- 'test/**/screenshots/diff/*.png',
- 'test/**/screenshots/failure/*.png',
- 'test/**/screenshots/session/*.png',
- 'test/functional/failure_debug/html/*.html',
- 'x-pack/test/**/screenshots/diff/*.png',
- 'x-pack/test/**/screenshots/failure/*.png',
- 'x-pack/test/**/screenshots/session/*.png',
- 'x-pack/test/functional/failure_debug/html/*.html',
- '.es/**/*.hprof'
- ]
-
- withEnv([
- "GCS_UPLOAD_PREFIX=${uploadPrefix}"
- ], {
- try {
- closure()
- } finally {
- catchErrors {
- ARTIFACT_PATTERNS.each { pattern ->
- uploadGcsArtifact(uploadPrefix, pattern)
- }
-
- dir(env.WORKSPACE) {
- ARTIFACT_PATTERNS.each { pattern ->
- uploadGcsArtifact(uploadPrefix, "parallel/*/kibana/${pattern}")
- }
- }
- }
- }
- })
-}
-
-def publishJunit() {
- junit(testResults: 'target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true)
-
- dir(env.WORKSPACE) {
- junit(testResults: 'parallel/*/kibana/target/junit/**/*.xml', allowEmptyResults: true, keepLongStdio: true)
- }
-}
-
-def sendMail(Map params = [:]) {
- // If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success
- // The e-mail plugin for the infra e-mail depends upon this being set
- currentBuild.result = currentBuild.result ?: 'SUCCESS'
-
- def buildStatus = buildUtils.getBuildStatus()
- if (buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') {
- node('flyweight') {
- sendInfraMail()
- sendKibanaMail(params)
- }
- }
-}
-
-def sendInfraMail() {
- catchErrors {
- step([
- $class: 'Mailer',
- notifyEveryUnstableBuild: true,
- recipients: 'infra-root+build@elastic.co',
- sendToIndividuals: false
- ])
- }
-}
-
-def sendKibanaMail(Map params = [:]) {
- def config = [to: 'build-kibana@elastic.co'] + params
-
- catchErrors {
- def buildStatus = buildUtils.getBuildStatus()
- if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') {
- emailext(
- config.to,
- subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}",
- body: '${SCRIPT,template="groovy-html.template"}',
- mimeType: 'text/html',
- )
- }
- }
-}
-
-def bash(script, label) {
- sh(
- script: "#!/bin/bash\n${script}",
- label: label
- )
-}
-
-def doSetup() {
- notifyOnError {
- retryWithDelay(2, 15) {
- try {
- runbld("./test/scripts/jenkins_setup.sh", "Setup Build Environment and Dependencies")
- } catch (ex) {
- try {
- // Setup expects this directory to be missing, so we need to remove it before we do a retry
- bash("rm -rf ../elasticsearch", "Remove elasticsearch sibling directory, if it exists")
- } finally {
- throw ex
- }
- }
- }
- }
-}
-
-def getBuildArtifactBucket() {
- def dir = env.ghprbPullId ? "pr-${env.ghprbPullId}" : buildState.get('checkoutInfo').branch.replace("/", "__")
- return "gs://ci-artifacts.kibana.dev/default-build/${dir}/${buildState.get('checkoutInfo').commit}"
-}
-
-def buildKibana(maxWorkers = '') {
- notifyOnError {
- withEnv(["KBN_OPTIMIZER_MAX_WORKERS=${maxWorkers}"]) {
- runbld("./test/scripts/jenkins_build_kibana.sh", "Build Kibana")
- }
-
- withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') {
- bash("""
- cd "${env.WORKSPACE}"
- gsutil -q -m cp 'kibana-default.tar.gz' '${getBuildArtifactBucket()}/'
- gsutil -q -m cp 'kibana-default-plugins.tar.gz' '${getBuildArtifactBucket()}/'
- """, "Upload Default Build artifacts to GCS")
- }
- }
-}
-
-def downloadDefaultBuildArtifacts() {
- withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') {
- bash("""
- cd "${env.WORKSPACE}"
- gsutil -q -m cp '${getBuildArtifactBucket()}/kibana-default.tar.gz' ./
- gsutil -q -m cp '${getBuildArtifactBucket()}/kibana-default-plugins.tar.gz' ./
- """, "Download Default Build artifacts from GCS")
- }
-}
-
-def runErrorReporter() {
- return runErrorReporter([pwd()])
-}
-
-def runErrorReporter(workspaces) {
- def status = buildUtils.getBuildStatus()
- def dryRun = status != "ABORTED" ? "" : "--no-github-update"
-
- def globs = workspaces.collect { "'${it}/target/junit/**/*.xml'" }.join(" ")
-
- bash(
- """
- source src/dev/ci_setup/setup_env.sh
- node scripts/report_failed_tests --no-index-errors ${dryRun} ${globs}
- """,
- "Report failed tests, if necessary"
- )
-}
-
-def call(Map params = [:], Closure closure) {
- def config = [timeoutMinutes: 135, checkPrChanges: false, setCommitStatus: false] + params
-
- stage("Kibana Pipeline") {
- timeout(time: config.timeoutMinutes, unit: 'MINUTES') {
- timestamps {
- ansiColor('xterm') {
- if (config.setCommitStatus) {
- buildState.set('shouldSetCommitStatus', true)
- }
- if (config.checkPrChanges && githubPr.isPr()) {
- pipelineLibraryTests()
-
- print "Checking PR for changes to determine if CI needs to be run..."
-
- if (prChanges.areChangesSkippable()) {
- print "No changes requiring CI found in PR, skipping."
- return
- }
- }
- try {
- closure()
- } finally {
- if (config.setCommitStatus) {
- githubCommitStatus.onFinish()
- }
- }
- }
- }
- }
- }
-}
-
-// Creates a task queue using withTaskQueue, and copies the bootstrapped kibana repo into each process's workspace
-// Note that node_modules are mostly symlinked to save time/space. See test/scripts/jenkins_setup_parallel_workspace.sh
-def withCiTaskQueue(Map options = [:], Closure closure) {
- def setupClosure = {
- // This can't use runbld, because it expects the source to be there, which isn't yet
- bash("${env.WORKSPACE}/kibana/test/scripts/jenkins_setup_parallel_workspace.sh", "Set up duplicate workspace for parallel process")
- }
-
- def config = [parallel: 24, setup: setupClosure] + options
-
- withTaskQueue(config) {
- closure.call()
- }
-}
-
-def scriptTask(description, script) {
- return {
- withFunctionalTestEnv {
- notifyOnError {
- runbld(script, description)
- }
- }
- }
-}
-
-def scriptTaskDocker(description, script) {
- return {
- withDocker(scriptTask(description, script))
- }
-}
-
-def buildDocker() {
- sh(
- script: "./.ci/build_docker.sh",
- label: 'Build CI Docker image'
- )
-}
-
-def withDocker(Closure closure) {
- docker
- .image('kibana-ci')
- .inside(
- "-v /etc/runbld:/etc/runbld:ro -v '${env.JENKINS_HOME}:${env.JENKINS_HOME}' -v '/dev/shm/workspace:/dev/shm/workspace' --shm-size 2GB --cpus 4",
- closure
- )
-}
-
-def buildPlugins() {
- runbld('./test/scripts/jenkins_build_plugins.sh', 'Build OSS Plugins')
-}
-
-def withTasks(Map params = [:], Closure closure) {
- catchErrors {
- def config = [setupWork: {}, worker: [:], parallel: 24] + params
- def workerConfig = [name: 'ci-worker', size: 'xxl', ramDisk: true] + config.worker
-
- workers.ci(workerConfig) {
- withCiTaskQueue([parallel: config.parallel]) {
- parallel([
- docker: {
- retry(2) {
- buildDocker()
- }
- },
-
- // There are integration tests etc that require the plugins to be built first, so let's go ahead and build them before set up the parallel workspaces
- plugins: { buildPlugins() },
- ])
-
- config.setupWork()
-
- catchErrors {
- closure()
- }
- }
- }
- }
-}
-
-def allCiTasks() {
- parallel([
- general: {
- withTasks {
- tasks.check()
- tasks.lint()
- tasks.test()
- task {
- buildKibana(16)
- tasks.functionalOss()
- tasks.functionalXpack()
- }
- tasks.storybooksCi()
- }
- },
- jest: {
- workers.ci(name: 'jest', size: 'n2-standard-16', ramDisk: false) {
- catchErrors {
- scriptTask('Jest Unit Tests', 'test/scripts/test/jest_unit.sh')()
- }
-
- catchErrors {
- runbld.junit()
- }
- }
- },
- ])
-}
-
-def pipelineLibraryTests() {
- return
- whenChanged(['vars/', '.ci/pipeline-library/']) {
- workers.base(size: 'flyweight', bootstrapped: false, ramDisk: false) {
- dir('.ci/pipeline-library') {
- sh './gradlew test'
- }
- }
- }
-}
-
-return this
diff --git a/vars/prChanges.groovy b/vars/prChanges.groovy
deleted file mode 100644
index a8a81cade844c6..00000000000000
--- a/vars/prChanges.groovy
+++ /dev/null
@@ -1,82 +0,0 @@
-import groovy.transform.Field
-
-public static @Field PR_CHANGES_CACHE = []
-
-// if all the changed files in a PR match one of these regular
-// expressions then CI will be skipped for that PR
-def getSkippablePaths() {
- return [
- /^docs\//,
- /^rfcs\//,
- /^.ci\/.+\.yml$/,
- /^.ci\/es-snapshots\//,
- /^.ci\/pipeline-library\//,
- /^.ci\/Jenkinsfile_[^\/]+$/,
- /^\.github\//,
- /\.md$/,
- /^\.backportrc\.json$/,
- /^\.buildkite\//,
- ]
-}
-
-// exclusion regular expressions that will invalidate paths that
-// match one of the skippable path regular expressions
-def getNotSkippablePaths() {
- return [
- // this file is auto-generated and changes to it need to be validated with CI
- /^docs\/developer\/plugin-list.asciidoc$/,
- // don't skip CI on prs with changes to plugin readme files (?i) is for case-insensitive matching
- /(?i)\/plugins\/[^\/]+\/readme\.(md|asciidoc)$/,
- ]
-}
-
-def areChangesSkippable() {
- if (!githubPr.isPr()) {
- return false
- }
-
- try {
- def skippablePaths = getSkippablePaths()
- def notSkippablePaths = getNotSkippablePaths()
- def files = getChangedFiles()
-
- // 3000 is the max files GH API will return
- if (files.size() >= 3000) {
- return false
- }
-
- files = files.findAll { file ->
- def skippable = skippablePaths.find { regex -> file =~ regex} && !notSkippablePaths.find { regex -> file =~ regex }
- return !skippable
- }
-
- return files.size() < 1
- } catch (ex) {
- buildUtils.printStacktrace(ex)
- print "Error while checking to see if CI is skippable based on changes. Will run CI."
- return false
- }
-}
-
-def getChanges() {
- if (!PR_CHANGES_CACHE && env.ghprbPullId) {
- withGithubCredentials {
- def changes = githubPrs.getChanges(env.ghprbPullId)
- if (changes) {
- PR_CHANGES_CACHE.addAll(changes)
- }
- }
- }
-
- return PR_CHANGES_CACHE
-}
-
-def getChangedFiles() {
- def changes = getChanges()
- def changedFiles = changes.collect { it.filename }
- def renamedFiles = changes.collect { it.previousFilename }.findAll { it }
-
- return changedFiles + renamedFiles
-}
-
-return this
diff --git a/vars/retryWithDelay.groovy b/vars/retryWithDelay.groovy
deleted file mode 100644
index 83fd94c6f2b1ec..00000000000000
--- a/vars/retryWithDelay.groovy
+++ /dev/null
@@ -1,18 +0,0 @@
-def call(retryTimes, delaySecs, closure) {
- retry(retryTimes) {
- try {
- closure()
- } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException ex) {
- throw ex // Immediately re-throw build abort exceptions, don't sleep first
- } catch (Exception ex) {
- sleep delaySecs
- throw ex
- }
- }
-}
-
-def call(retryTimes, Closure closure) {
- call(retryTimes, 15, closure)
-}
-
-return this
diff --git a/vars/retryable.groovy b/vars/retryable.groovy
deleted file mode 100644
index bfd021ddd8167b..00000000000000
--- a/vars/retryable.groovy
+++ /dev/null
@@ -1,78 +0,0 @@
-import groovy.transform.Field
-
-public static @Field GLOBAL_RETRIES_ENABLED = false
-public static @Field MAX_GLOBAL_RETRIES = 1
-public static @Field CURRENT_GLOBAL_RETRIES = 0
-public static @Field FLAKY_FAILURES = []
-
-def setMax(max) {
- retryable.MAX_GLOBAL_RETRIES = max
-}
-
-def enable() {
- retryable.GLOBAL_RETRIES_ENABLED = true
-}
-
-def enable(max) {
- enable()
- setMax(max)
-}
-
-def haveReachedMaxRetries() {
- return retryable.CURRENT_GLOBAL_RETRIES >= retryable.MAX_GLOBAL_RETRIES
-}
-
-def getFlakyFailures() {
- return retryable.FLAKY_FAILURES
-}
-
-def printFlakyFailures() {
- catchErrors {
- def failures = getFlakyFailures()
-
- if (failures && failures.size() > 0) {
- print "This build had the following flaky failures:"
- failures.each {
- print "\n${it.label}"
- buildUtils.printStacktrace(it.exception)
- }
- }
- }
-}
-
-def call(label, Closure closure) {
- if (!retryable.GLOBAL_RETRIES_ENABLED) {
- closure()
- return
- }
-
- try {
- closure()
- } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException ex) {
- // If the build was aborted, don't retry the step
- throw ex
- } catch (Exception ex) {
- if (haveReachedMaxRetries()) {
- print "Couldn't retry '${label}', have already reached the max number of retries for this build."
- throw ex
- }
-
- retryable.CURRENT_GLOBAL_RETRIES++
- buildUtils.printStacktrace(ex)
- unstable "${label} failed but is retryable, trying a second time..."
-
- def JOB = env.JOB ? "${env.JOB}-retry" : ""
- withEnv([
- "JOB=${JOB}",
- ]) {
- closure()
- }
-
- retryable.FLAKY_FAILURES << [
- label: label,
- exception: ex,
- ]
-
- unstable "${label} failed on the first attempt, but succeeded on the second. Marking it as flaky."
- }
-}
diff --git a/vars/runbld.groovy b/vars/runbld.groovy
deleted file mode 100644
index 80416d4fa9a412..00000000000000
--- a/vars/runbld.groovy
+++ /dev/null
@@ -1,17 +0,0 @@
-def call(script, label, enableJunitProcessing = false) {
- // def extraConfig = enableJunitProcessing ? "" : "--config ${env.WORKSPACE}/kibana/.ci/runbld_no_junit.yml"
-
- sh(
- script: "bash ${script}",
- label: label ?: script
- )
-}
-
-def junit() {
- sh(
- script: "/usr/local/bin/runbld -d '${pwd()}' ${env.WORKSPACE}/kibana/test/scripts/jenkins_runbld_junit.sh",
- label: "Process JUnit reports with runbld"
- )
-}
-
-return this
diff --git a/vars/slackNotifications.groovy b/vars/slackNotifications.groovy
deleted file mode 100644
index 02aad14d8ba3fb..00000000000000
--- a/vars/slackNotifications.groovy
+++ /dev/null
@@ -1,228 +0,0 @@
-def getFailedBuildBlocks() {
- def messages = [
- getFailedSteps(),
- getTestFailures(),
- ]
-
- return messages
- .findAll { !!it } // No blank strings
- .collect { markdownBlock(it) }
-}
-
-def dividerBlock() {
- return [ type: "divider" ]
-}
-
-// If a message is longer than the limit, split it up by '\n' into parts, and return as many parts as will fit within the limit
-def shortenMessage(message, sizeLimit = 3000) {
- if (message.size() <= sizeLimit) {
- return message
- }
-
- def truncatedMessage = "[...truncated...]"
-
- def parts = message.split("\n")
- message = ""
-
- for(def part in parts) {
- if ((message.size() + part.size() + truncatedMessage.size() + 1) > sizeLimit) {
- break;
- }
- message += part+"\n"
- }
-
- message += truncatedMessage
-
- return message.size() <= sizeLimit ? message : truncatedMessage
-}
-
-def markdownBlock(message) {
- return [
- type: "section",
- text: [
- type: "mrkdwn",
- text: shortenMessage(message, 3000), // 3000 is max text length for `section`s only
- ],
- ]
-}
-
-def contextBlock(message) {
- return [
- type: "context",
- elements: [
- [
- type: 'mrkdwn',
- text: message, // Not sure what the size limit is here, I tried 10000s of characters and it still worked
- ]
- ]
- ]
-}
-
-def getFailedSteps() {
- try {
- def steps = jenkinsApi.getFailedSteps()?.findAll { step ->
- step.displayName != 'Check out from version control'
- }
-
- if (steps?.size() > 0) {
- def list = steps.collect { "• <${it.logs}|${it.displayName}>" }.join("\n")
- return "*Failed Steps*\n${list}"
- }
- } catch (ex) {
- buildUtils.printStacktrace(ex)
- print "Error retrieving failed pipeline steps for PR comment, will skip this section"
- }
-
- return ""
-}
-
-def getTestFailures() {
- def failures = testUtils.getFailures()
- if (!failures) {
- return ""
- }
-
- def messages = []
- messages << "*Test Failures*"
-
- def list = failures.take(10).collect {
- def name = it
- .fullDisplayName
- .split(/\./, 2)[-1]
- // Only the following three characters need to be escaped for link text, per Slack's docs
- .replaceAll('&', '&')
- .replaceAll('<', '<')
- .replaceAll('>', '>')
-
- return "• <${it.url}|${name}>"
- }.join("\n")
-
- def moreText = failures.size() > 10 ? "\n• ...and ${failures.size()-10} more" : ""
- return "*Test Failures*\n${list}${moreText}"
-}
-
-def getDefaultDisplayName() {
- return "${env.JOB_NAME} ${env.BUILD_DISPLAY_NAME}"
-}
-
-def getDefaultContext(config = [:]) {
- def progressMessage = ""
- if (config && !config.isFinal) {
- progressMessage = "In-progress"
- } else {
- def duration = currentBuild.durationString.replace(' and counting', '')
- progressMessage = "${buildUtils.getBuildStatus().toLowerCase().capitalize()} after ${duration}"
- }
-
- return contextBlock([
- progressMessage,
- "",
- ].join(' · '))
-}
-
-def getStatusIcon(config = [:]) {
- if (config && !config.isFinal) {
- return ':hourglass_flowing_sand:'
- }
-
- def status = buildUtils.getBuildStatus()
- if (status == 'UNSTABLE') {
- return ':yellow_heart:'
- }
-
- return ':broken_heart:'
-}
-
-def getBackupMessage(config) {
- return "${getStatusIcon(config)} ${config.title}\n\nFirst attempt at sending this notification failed. Please check the build."
-}
-
-def sendFailedBuild(Map params = [:]) {
- def config = [
- channel: '#kibana-operations-alerts',
- title: "*<${env.BUILD_URL}|${getDefaultDisplayName()}>*",
- message: getDefaultDisplayName(),
- color: 'danger',
- icon: ':jenkins:',
- username: 'Kibana Operations',
- isFinal: false,
- ] + params
-
- config.context = config.context ?: getDefaultContext(config)
-
- def title = "${getStatusIcon(config)} ${config.title}"
- def message = "${getStatusIcon(config)} ${config.message}"
-
- def blocks = [markdownBlock(title)]
- getFailedBuildBlocks().each { blocks << it }
- blocks << dividerBlock()
- blocks << config.context
-
- def channel = config.channel
- def timestamp = null
-
- def previousResp = buildState.get('SLACK_NOTIFICATION_RESPONSE')
- if (previousResp) {
- // When using `timestamp` to update a previous message, you have to use the channel ID from the previous response
- channel = previousResp.channelId
- timestamp = previousResp.ts
- }
-
- def resp = slackSend(
- channel: channel,
- timestamp: timestamp,
- username: config.username,
- iconEmoji: config.icon,
- color: config.color,
- message: message,
- blocks: blocks
- )
-
- if (!resp) {
- resp = slackSend(
- channel: config.channel,
- username: config.username,
- iconEmoji: config.icon,
- color: config.color,
- message: message,
- blocks: [markdownBlock(getBackupMessage(config))]
- )
- }
-
- if (resp) {
- buildState.set('SLACK_NOTIFICATION_RESPONSE', resp)
- }
-}
-
-def onFailure(Map options = [:]) {
- catchError {
- def status = buildUtils.getBuildStatus()
- if (status != "SUCCESS") {
- catchErrors {
- options.isFinal = true
- sendFailedBuild(options)
- }
- }
- }
-}
-
-def onFailure(Map options = [:], Closure closure) {
- if (options.disabled) {
- catchError {
- closure()
- }
-
- return
- }
-
- buildState.set('SLACK_NOTIFICATION_CONFIG', options)
-
- // try/finally will NOT work here, because the build status will not have been changed to ERROR when the finally{} block executes
- catchError {
- closure()
- }
-
- onFailure(options)
-}
-
-return this
diff --git a/vars/storybooks.groovy b/vars/storybooks.groovy
deleted file mode 100644
index f3c4a97a7d4364..00000000000000
--- a/vars/storybooks.groovy
+++ /dev/null
@@ -1,83 +0,0 @@
-def getStorybooksBucket() {
- return "ci-artifacts.kibana.dev/storybooks"
-}
-
-def getDestinationDir() {
- return env.ghprbPullId ? "pr-${env.ghprbPullId}" : buildState.get('checkoutInfo').branch.replace("/", "__")
-}
-
-def getUrl() {
- return "https://${getStorybooksBucket()}/${getDestinationDir()}"
-}
-
-def getUrlLatest() {
- return "${getUrl()}/latest"
-}
-
-def getUrlForCommit() {
- return "${getUrl()}/${buildState.get('checkoutInfo').commit}"
-}
-
-def upload() {
- dir("built_assets/storybook") {
- sh "mv ci_composite composite"
-
- def storybooks = sh(
- script: 'ls -1d */',
- returnStdout: true
- ).trim()
- .split('\n')
- .collect { it.replace('/', '') }
- .findAll { it != 'composite' }
-
- def listHtml = storybooks.collect { """${it}""" }.join("\n")
-
- def html = """
-
-
- Storybooks
- Composite Storybook
- All
-
-
-
- """
-
- writeFile(file: 'index.html', text: html)
-
- withGcpServiceAccount.fromVaultSecret('secret/kibana-issues/dev/ci-artifacts-key', 'value') {
- kibanaPipeline.bash("""
- gsutil -q -m cp -r -z js,css,html,json,map,txt,svg '*' 'gs://${getStorybooksBucket()}/${getDestinationDir()}/${buildState.get('checkoutInfo').commit}/'
- gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp -z html 'index.html' 'gs://${getStorybooksBucket()}/${getDestinationDir()}/latest/'
- """, "Upload Storybooks to GCS")
- }
-
- buildState.set('storybooksUrl', getUrlForCommit())
- }
-}
-
-def build() {
- withEnv(["STORYBOOK_BASE_URL=${getUrlForCommit()}"]) {
- kibanaPipeline.bash('test/scripts/jenkins_storybook.sh', 'Build Storybooks')
- }
-}
-
-def buildAndUpload() {
- def sha = buildState.get('checkoutInfo').commit
- def context = 'Build and Publish Storybooks'
-
- githubCommitStatus.create(sha, 'pending', 'Building Storybooks', context)
-
- try {
- build()
- upload()
- githubCommitStatus.create(sha, 'success', 'Storybooks built', context, getUrlForCommit())
- } catch(ex) {
- githubCommitStatus.create(sha, 'error', 'Building Storybooks failed', context)
- throw ex
- }
-}
-
-return this
diff --git a/vars/task.groovy b/vars/task.groovy
deleted file mode 100644
index 0c07b519b6fefc..00000000000000
--- a/vars/task.groovy
+++ /dev/null
@@ -1,5 +0,0 @@
-def call(Closure closure) {
- withTaskQueue.addTask(closure)
-}
-
-return this
diff --git a/vars/tasks.groovy b/vars/tasks.groovy
deleted file mode 100644
index 9a1ea053e9c496..00000000000000
--- a/vars/tasks.groovy
+++ /dev/null
@@ -1,201 +0,0 @@
-def call(List closures) {
- withTaskQueue.addTasks(closures)
-}
-
-def check() {
- tasks([
- kibanaPipeline.scriptTask('Quick Commit Checks', 'test/scripts/checks/commit/commit.sh'),
- kibanaPipeline.scriptTask('Check Telemetry Schema', 'test/scripts/checks/telemetry.sh'),
- kibanaPipeline.scriptTask('Check TypeScript Projects', 'test/scripts/checks/ts_projects.sh'),
- kibanaPipeline.scriptTask('Check Jest Configs', 'test/scripts/checks/jest_configs.sh'),
- kibanaPipeline.scriptTask('Check @kbn/pm Distributable', 'test/scripts/checks/kbn_pm_dist.sh'),
- kibanaPipeline.scriptTask('Check Plugin List Docs', 'test/scripts/checks/plugin_list_docs.sh'),
- kibanaPipeline.scriptTask('Check Types and Public API Docs', 'test/scripts/checks/type_check_plugin_public_api_docs.sh'),
- kibanaPipeline.scriptTask('Check Bundle Limits', 'test/scripts/checks/bundle_limits.sh'),
- kibanaPipeline.scriptTask('Check i18n', 'test/scripts/checks/i18n.sh'),
- kibanaPipeline.scriptTask('Check File Casing', 'test/scripts/checks/file_casing.sh'),
- kibanaPipeline.scriptTask('Check Licenses', 'test/scripts/checks/licenses.sh'),
- kibanaPipeline.scriptTask('Check Plugins With Circular Dependencies', 'test/scripts/checks/plugins_with_circular_deps.sh'),
- kibanaPipeline.scriptTask('Verify NOTICE', 'test/scripts/checks/verify_notice.sh'),
- kibanaPipeline.scriptTask('Test Projects', 'test/scripts/checks/test_projects.sh'),
- kibanaPipeline.scriptTask('Test Hardening', 'test/scripts/checks/test_hardening.sh'),
- ])
-}
-
-def lint() {
- tasks([
- kibanaPipeline.scriptTask('Lint: eslint', 'test/scripts/lint/eslint.sh'),
- kibanaPipeline.scriptTask('Lint: stylelint', 'test/scripts/lint/stylelint.sh'),
- ])
-}
-
-def test() {
- tasks([
- // This task requires isolation because of hard-coded, conflicting ports and such, so let's use Docker here
- kibanaPipeline.scriptTaskDocker('Jest Integration Tests', 'test/scripts/test/jest_integration.sh'),
- kibanaPipeline.scriptTask('API Integration Tests', 'test/scripts/test/api_integration.sh'),
- ])
-}
-
-def ossCiGroups() {
- def ciGroups = 1..11
- tasks(ciGroups.collect { kibanaPipeline.ossCiGroupProcess(it, true) })
-}
-
-def xpackCiGroups() {
- def ciGroups = 1..13
- tasks(ciGroups.collect { kibanaPipeline.xpackCiGroupProcess(it, true) })
-}
-
-def xpackCiGroupDocker() {
- task {
- workers.ci(name: 'xpack-cigroups-docker', size: 'm', ramDisk: true) {
- kibanaPipeline.downloadDefaultBuildArtifacts()
- kibanaPipeline.bash("""
- cd '${env.WORKSPACE}'
- mkdir -p kibana-build
- tar -xzf kibana-default.tar.gz -C kibana-build --strip=1
- tar -xzf kibana-default-plugins.tar.gz -C kibana
- """, "Extract Default Build artifacts")
- kibanaPipeline.xpackCiGroupProcess('Docker', true)()
- }
- }
-}
-
-def functionalOss(Map params = [:]) {
- def config = params ?: [
- serverIntegration: true,
- ciGroups: true,
- firefox: true,
- accessibility: true,
- pluginFunctional: true,
- visualRegression: false,
- ]
-
- task {
- if (config.ciGroups) {
- ossCiGroups()
- }
-
- if (config.firefox) {
- task(kibanaPipeline.functionalTestProcess('oss-firefox', './test/scripts/jenkins_firefox_smoke.sh'))
- }
-
- if (config.accessibility) {
- task(kibanaPipeline.functionalTestProcess('oss-accessibility', './test/scripts/jenkins_accessibility.sh'))
- }
-
- if (config.pluginFunctional) {
- task(kibanaPipeline.functionalTestProcess('oss-pluginFunctional', './test/scripts/jenkins_plugin_functional.sh'))
- }
-
- if (config.visualRegression) {
- task(kibanaPipeline.functionalTestProcess('oss-visualRegression', './test/scripts/jenkins_visual_regression.sh'))
- }
-
- if (config.serverIntegration) {
- task(kibanaPipeline.scriptTaskDocker('serverIntegration', './test/scripts/test/server_integration.sh'))
- }
- }
-}
-
-def functionalXpack(Map params = [:]) {
- def config = params ?: [
- ciGroups: true,
- firefox: true,
- accessibility: true,
- pluginFunctional: true,
- savedObjectsFieldMetrics:true,
- pageLoadMetrics: false,
- visualRegression: false,
- ]
-
- task {
- if (config.ciGroups) {
- xpackCiGroups()
- xpackCiGroupDocker()
- }
-
- if (config.firefox) {
- task(kibanaPipeline.functionalTestProcess('xpack-firefox', './test/scripts/jenkins_xpack_firefox_smoke.sh'))
- }
-
- if (config.accessibility) {
- task(kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'))
- }
-
- if (config.visualRegression) {
- task(kibanaPipeline.functionalTestProcess('xpack-visualRegression', './test/scripts/jenkins_xpack_visual_regression.sh'))
- }
-
- if (config.savedObjectsFieldMetrics) {
- task(kibanaPipeline.functionalTestProcess('xpack-savedObjectsFieldMetrics', './test/scripts/jenkins_xpack_saved_objects_field_metrics.sh'))
- }
-
- whenChanged([
- 'x-pack/plugins/security_solution/',
- 'x-pack/plugins/cases/',
- 'x-pack/plugins/timelines/',
- 'x-pack/plugins/lists/',
- 'x-pack/test/security_solution_cypress/',
- 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/',
- 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx',
- 'x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressChrome', './test/scripts/jenkins_security_solution_cypress_chrome.sh'))
- // Temporarily disabled to figure out test flake
- // task(kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypressFirefox', './test/scripts/jenkins_security_solution_cypress_firefox.sh'))
- }
- }
-
- whenChanged([
- 'x-pack/plugins/apm/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-APMCypress', './test/scripts/jenkins_apm_cypress.sh'))
- }
- }
-
- whenChanged([
- 'x-pack/plugins/synthetics/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-UptimePlaywright', './test/scripts/jenkins_uptime_playwright.sh'))
- }
- }
-
- whenChanged([
- 'x-pack/plugins/ux/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-uxPluginSynthetics', './test/scripts/jenkins_ux_synthetics.sh'))
- }
- }
-
- whenChanged([
- 'x-pack/plugins/fleet/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-FleetCypress', './test/scripts/jenkins_fleet_cypress.sh'))
- }
- }
-
- whenChanged([
- 'x-pack/plugins/osquery/',
- ]) {
- if (githubPr.isPr()) {
- task(kibanaPipeline.functionalTestProcess('xpack-osqueryCypress', './test/scripts/jenkins_osquery_cypress.sh'))
- }
- }
-
- }
-}
-
-def storybooksCi() {
- task {
- storybooks.buildAndUpload()
- }
-}
-
-return this
diff --git a/vars/whenChanged.groovy b/vars/whenChanged.groovy
deleted file mode 100644
index c58ec83f2b051e..00000000000000
--- a/vars/whenChanged.groovy
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- whenChanged('some/path') { yourCode() } can be used to execute pipeline code in PRs only when changes are detected on paths that you specify.
- The specified code blocks will also always be executed during the non-PR jobs for tracked branches.
-
- You have the option of passing in path prefixes, or regexes. Single or multiple.
- Path specifications are NOT globby, they are only prefixes.
- Specifying multiple will treat them as ORs.
-
- Example Usages:
- whenChanged('a/path/prefix/') { someCode() }
- whenChanged(startsWith: 'a/path/prefix/') { someCode() } // Same as above
- whenChanged(['prefix1/', 'prefix2/']) { someCode() }
- whenChanged(regex: /\.test\.js$/) { someCode() }
- whenChanged(regex: [/abc/, /xyz/]) { someCode() }
-*/
-
-def call(String startsWithString, Closure closure) {
- return whenChanged([ startsWith: startsWithString ], closure)
-}
-
-def call(List startsWithStrings, Closure closure) {
- return whenChanged([ startsWith: startsWithStrings ], closure)
-}
-
-def call(Map params, Closure closure) {
- if (!githubPr.isPr()) {
- return closure()
- }
-
- def files = prChanges.getChangedFiles()
- def hasMatch = false
-
- if (params.regex) {
- params.regex = [] + params.regex
- print "Checking PR for changes that match: ${params.regex.join(', ')}"
- hasMatch = !!files.find { file ->
- params.regex.find { regex -> file =~ regex }
- }
- }
-
- if (!hasMatch && params.startsWith) {
- params.startsWith = [] + params.startsWith
- print "Checking PR for changes that start with: ${params.startsWith.join(', ')}"
- hasMatch = !!files.find { file ->
- params.startsWith.find { str -> file.startsWith(str) }
- }
- }
-
- if (hasMatch) {
- print "Changes found, executing pipeline."
- closure()
- } else {
- print "No changes found, skipping."
- }
-}
-
-return this
diff --git a/vars/withGithubCredentials.groovy b/vars/withGithubCredentials.groovy
deleted file mode 100644
index 224e49af1bd6f2..00000000000000
--- a/vars/withGithubCredentials.groovy
+++ /dev/null
@@ -1,9 +0,0 @@
-def call(closure) {
- withCredentials([
- string(credentialsId: '2a9602aa-ab9f-4e52-baf3-b71ca88469c7', variable: 'GITHUB_TOKEN'),
- ]) {
- closure()
- }
-}
-
-return this
diff --git a/vars/withTaskQueue.groovy b/vars/withTaskQueue.groovy
deleted file mode 100644
index 8132d6264744f2..00000000000000
--- a/vars/withTaskQueue.groovy
+++ /dev/null
@@ -1,154 +0,0 @@
-import groovy.transform.Field
-
-public static @Field TASK_QUEUES = [:]
-public static @Field TASK_QUEUES_COUNTER = 0
-
-/**
- withTaskQueue creates a queue of "tasks" (just plain closures to execute), and executes them with your desired level of concurrency.
- This way, you can define, for example, 40 things that need to execute, then only allow 10 of them to execute at once.
-
- Each "process" will execute in a separate, unique, empty directory.
- If you want each process to have a bootstrapped kibana repo, check out kibanaPipeline.withCiTaskQueue
-
- Using the queue currently requires an agent/worker.
-
- Usage:
-
- withTaskQueue(parallel: 10) {
- task { print "This is a task" }
-
- // This is the same as calling task() multiple times
- tasks([ { print "Another task" }, { print "And another task" } ])
-
- // Tasks can queue up subsequent tasks
- task {
- buildThing()
- task { print "I depend on buildThing()" }
- }
- }
-
- You can also define a setup task that each process should execute one time before executing tasks:
- withTaskQueue(parallel: 10, setup: { sh "my-setup-scrupt.sh" }) {
- ...
- }
-
-*/
-def call(Map options = [:], Closure closure) {
- def config = [ parallel: 10 ] + options
- def counter = ++TASK_QUEUES_COUNTER
-
- // We're basically abusing withEnv() to create a "scope" for all steps inside of a withTaskQueue block
- // This way, we could have multiple task queue instances in the same pipeline
- withEnv(["TASK_QUEUE_ID=${counter}"]) {
- withTaskQueue.TASK_QUEUES[env.TASK_QUEUE_ID] = [
- tasks: [],
- tmpFile: sh(script: 'mktemp', returnStdout: true).trim()
- ]
-
- closure.call()
-
- def processesExecuting = 0
- def processes = [:]
- def iterationId = 0
-
- for(def i = 1; i <= config.parallel; i++) {
- def j = i
- processes["task-queue-process-${j}"] = {
- catchErrors {
- withEnv([
- "TASK_QUEUE_PROCESS_ID=${j}",
- "TASK_QUEUE_ITERATION_ID=${++iterationId}"
- ]) {
- dir("${WORKSPACE}/parallel/${j}/kibana") {
- if (config.setup) {
- config.setup.call(j)
- }
-
- def isDone = false
- while(!isDone) { // TODO some kind of timeout?
- catchErrors {
- if (!getTasks().isEmpty()) {
- processesExecuting++
- catchErrors {
- def task
- try {
- task = getTasks().pop()
- } catch (java.util.NoSuchElementException ex) {
- return
- }
-
- task.call()
- }
- processesExecuting--
- // If a task finishes, and no new tasks were queued up, and nothing else is executing
- // Then all of the processes should wake up and exit
- if (processesExecuting < 1 && getTasks().isEmpty()) {
- taskNotify()
- }
- return
- }
-
- if (processesExecuting > 0) {
- taskSleep()
- return
- }
-
- // Queue is empty, no processes are executing
- isDone = true
- }
- }
- }
- }
- }
- }
- }
- parallel(processes)
- }
-}
-
-// If we sleep in a loop using Groovy code, Pipeline Steps is flooded with Sleep steps
-// So, instead, we just watch a file and `touch` it whenever something happens that could modify the queue
-// There's a 20 minute timeout just in case something goes wrong,
-// in which case this method will get called again if the process is actually supposed to be waiting.
-def taskSleep() {
- sh(script: """#!/bin/bash
- TIMESTAMP=\$(date '+%s' -d "0 seconds ago")
- for (( i=1; i<=240; i++ ))
- do
- if [ "\$(stat -c %Y '${getTmpFile()}')" -ge "\$TIMESTAMP" ]
- then
- break
- else
- sleep 5
- if [[ \$i == 240 ]]; then
- echo "Waited for new tasks for 20 minutes, exiting in case something went wrong"
- fi
- fi
- done
- """, label: "Waiting for new tasks...")
-}
-
-// Used to let the task queue processes know that either a new task has been queued up, or work is complete
-def taskNotify() {
- sh "touch '${getTmpFile()}'"
-}
-
-def getTasks() {
- return withTaskQueue.TASK_QUEUES[env.TASK_QUEUE_ID].tasks
-}
-
-def getTmpFile() {
- return withTaskQueue.TASK_QUEUES[env.TASK_QUEUE_ID].tmpFile
-}
-
-def addTask(Closure closure) {
- getTasks() << closure
- taskNotify()
-}
-
-def addTasks(List closures) {
- closures.reverse().each {
- getTasks() << it
- }
- taskNotify()
-}
diff --git a/vars/workers.groovy b/vars/workers.groovy
deleted file mode 100644
index ea67ce415738f2..00000000000000
--- a/vars/workers.groovy
+++ /dev/null
@@ -1,206 +0,0 @@
-// "Workers" in this file will spin up an instance, do some setup etc depending on the configuration, and then execute some work that you define
-// e.g. workers.base(name: 'my-worker') { sh "echo 'ready to execute some kibana scripts'" }
-
-def label(size) {
- switch(size) {
- case 'flyweight':
- return 'flyweight'
- case 's':
- return 'docker && linux && immutable'
- case 's-highmem':
- return 'docker && tests-s'
- case 'm':
- return 'docker && linux && immutable && gobld/machineType:n2-standard-8'
- case 'm-highmem':
- return 'docker && linux && immutable && gobld/machineType:n1-highmem-8'
- case 'l':
- return 'docker && tests-l'
- case 'xl':
- return 'docker && tests-xl'
- case 'xl-highmem':
- return 'docker && tests-xl-highmem'
- case 'xxl':
- return 'docker && tests-xxl && gobld/machineType:custom-64-327680'
- case 'n2-standard-16':
- return 'docker && linux && immutable && gobld/machineType:n2-standard-16'
- }
-
- error "unknown size '${size}'"
-}
-
-/*
- The base worker that all of the others use. Will clone the scm (assumed to be kibana), and run kibana bootstrap processes by default.
-
- Parameters:
- size - size of worker label to use, e.g. 's' or 'xl'
- ramDisk - Should the workspace be mounted in memory? Default: true
- bootstrapped - If true, download kibana dependencies, run kbn bootstrap, etc. Default: true
- name - Name of the worker for display purposes, filenames, etc.
- scm - Jenkins scm configuration for checking out code. Use `null` to disable checkout. Default: inherited from job
-*/
-def base(Map params, Closure closure) {
- def config = [size: '', ramDisk: true, bootstrapped: true, name: 'unnamed-worker', scm: scm] + params
- if (!config.size) {
- error "You must specify an agent size, such as 'xl' or 's', when using workers.base()"
- }
-
- node(label(config.size)) {
- agentInfo.print()
-
- if (config.ramDisk) {
- // Move to a temporary workspace, so that we can symlink the real workspace into /dev/shm
- def originalWorkspace = env.WORKSPACE
- ws('/tmp/workspace') {
- sh(
- script: """
- mkdir -p /dev/shm/workspace
- mkdir -p '${originalWorkspace}' # create all of the directories leading up to the workspace, if they don't exist
- rm --preserve-root -rf '${originalWorkspace}' # then remove just the workspace, just in case there's stuff in it
- ln -s /dev/shm/workspace '${originalWorkspace}'
- """,
- label: "Move workspace to RAM - /dev/shm/workspace"
- )
- }
- }
-
- sh(
- script: "mkdir -p ${env.WORKSPACE}/tmp",
- label: "Create custom temp directory"
- )
-
- def checkoutInfo = [:]
-
- if (config.scm) {
- // Try to clone from Github up to 8 times, waiting 15 secs between attempts
- retryWithDelay(8, 15) {
- kibanaCheckout()
- }
-
- dir("kibana") {
- checkoutInfo = getCheckoutInfo()
-
- if (!buildState.has('checkoutInfo')) {
- buildState.set('checkoutInfo', checkoutInfo)
-
- if (buildState.get('shouldSetCommitStatus')) {
- githubCommitStatus.onStart()
- }
- }
- }
-
- ciStats.reportGitInfo(
- checkoutInfo.branch,
- checkoutInfo.commit,
- checkoutInfo.targetBranch,
- checkoutInfo.mergeBase
- )
- }
-
- withEnv([
- "CI=true",
- "HOME=${env.JENKINS_HOME}",
- "PR_NUMBER=${env.ghprbPullId ?: ''}",
- "PR_SOURCE_BRANCH=${env.ghprbSourceBranch ?: ''}",
- "PR_TARGET_BRANCH=${env.ghprbTargetBranch ?: ''}",
- "PR_MERGE_BASE=${checkoutInfo.mergeBase ?: ''}",
- "PR_AUTHOR=${env.ghprbPullAuthorLogin ?: ''}",
- "TEST_BROWSER_HEADLESS=1",
- "GIT_COMMIT=${checkoutInfo.commit}",
- "GIT_BRANCH=${checkoutInfo.branch}",
- "TMPDIR=${env.WORKSPACE}/tmp", // For Chrome and anything else that respects it
- ]) {
- withCredentials([
- string(credentialsId: 'vault-addr', variable: 'VAULT_ADDR'),
- string(credentialsId: 'vault-role-id', variable: 'VAULT_ROLE_ID'),
- string(credentialsId: 'vault-secret-id', variable: 'VAULT_SECRET_ID'),
- ]) {
- // scm is configured to check out to the ./kibana directory
- dir('kibana') {
- if (config.bootstrapped) {
- kibanaPipeline.doSetup()
- }
-
- closure()
- }
- }
- }
- }
-}
-
-// Worker for ci processes. Extends the base worker and adds GCS artifact upload, error reporting, junit processing
-def ci(Map params, Closure closure) {
- def config = [ramDisk: true, bootstrapped: true, runErrorReporter: true] + params
-
- return base(config) {
- kibanaPipeline.withGcsArtifactUpload(config.name) {
- kibanaPipeline.withPostBuildReporting(config) {
- closure()
- }
- }
- }
-}
-
-// Worker for running the current intake jobs. Just runs a single script after bootstrap.
-def intake(jobName, String script) {
- return {
- ci(name: jobName, size: 'm-highmem', ramDisk: true) {
- withEnv(["JOB=${jobName}"]) {
- kibanaPipeline.notifyOnError {
- runbld(script, "Execute ${jobName}")
- }
- }
- }
- }
-}
-
-// Worker for running functional tests. Runs a setup process (e.g. the kibana build) then executes a map of closures in parallel (e.g. one for each ciGroup)
-def functional(name, Closure setup, Map processes) {
- return {
- parallelProcesses(name: name, setup: setup, processes: processes, delayBetweenProcesses: 20, size: 'xl')
- }
-}
-
-/*
- Creates a ci worker that can run a setup process, followed by a group of processes in parallel.
-
- Parameters:
- name: Name of the worker for display purposes, filenames, etc.
- setup: Closure to execute after the agent is bootstrapped, before starting the parallel work
- processes: Map of closures that will execute in parallel after setup. Each closure is passed a unique number.
- delayBetweenProcesses: Number of seconds to wait between starting the parallel processes. Useful to spread the load of heavy init processes, e.g. Elasticsearch starting up. Default: 0
- size: size of worker label to use, e.g. 's' or 'xl'
-*/
-def parallelProcesses(Map params) {
- def config = [name: 'parallel-worker', setup: {}, processes: [:], delayBetweenProcesses: 0, size: 'xl'] + params
-
- ci(size: config.size, name: config.name) {
- config.setup()
-
- def nextProcessNumber = 1
- def process = { processName, processClosure ->
- def processNumber = nextProcessNumber
- nextProcessNumber++
-
- return {
- if (config.delayBetweenProcesses && config.delayBetweenProcesses > 0) {
- // This delay helps smooth out CPU load caused by ES/Kibana instances starting up at the same time
- def delay = (processNumber-1)*config.delayBetweenProcesses
- sleep(delay)
- }
-
- withEnv(["CI_PARALLEL_PROCESS_NUMBER=${processNumber}"]) {
- processClosure()
- }
- }
- }
-
- def processes = [:]
- config.processes.each { processName, processClosure ->
- processes[processName] = process(processName, processClosure)
- }
-
- parallel(processes)
- }
-}
-
-return this
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx
index f23470bbbe7a7c..4e9bf8d2726ea2 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx
@@ -70,12 +70,6 @@ const AlertsSettingsComponent = ({ knowledgeBase, setUpdatedKnowledgeBaseSetting
-
-
- {i18n.ASK_QUESTIONS_ABOUT}
-
-
-
- {i18n.LATEST_AND_RISKIEST_OPEN_ALERTS}
+ {i18n.LATEST_AND_RISKIEST_OPEN_ALERTS(knowledgeBase.latestAlerts)}
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx
index ebb5afe2f12a1e..9119e024adcb29 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx
@@ -42,7 +42,6 @@ const fetchConnectorArgs: FetchConnectorExecuteAction = {
http: mockHttp,
messages,
onNewReplacements: jest.fn(),
- ragOnAlerts: false,
};
describe('API tests', () => {
beforeEach(() => {
@@ -91,7 +90,6 @@ describe('API tests', () => {
alertsIndexPattern: '.alerts-security.alerts-default',
allow: ['a', 'b', 'c'],
allowReplacement: ['b', 'c'],
- ragOnAlerts: true,
replacements: { auuid: 'real.hostname' },
size: 30,
};
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx
index c2bdd4806a99ac..f186dab22f668f 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx
@@ -29,7 +29,6 @@ export interface FetchConnectorExecuteAction {
http: HttpSetup;
messages: Message[];
onNewReplacements: (newReplacements: Record) => void;
- ragOnAlerts: boolean;
replacements?: Record;
signal?: AbortSignal | undefined;
size?: number;
@@ -55,7 +54,6 @@ export const fetchConnectorExecuteAction = async ({
http,
messages,
onNewReplacements,
- ragOnAlerts,
replacements,
apiConfig,
signal,
@@ -90,7 +88,6 @@ export const fetchConnectorExecuteAction = async ({
alertsIndexPattern,
allow,
allowReplacement,
- ragOnAlerts,
replacements,
size,
});
@@ -192,7 +189,6 @@ export const fetchConnectorExecuteAction = async ({
response: hasParsableResponse({
alerts,
assistantLangChain,
- ragOnAlerts,
})
? getFormattedMessageContent(response.data)
: response.data,
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx
index ac72fc27dd891d..e866cad765456b 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx
@@ -33,8 +33,7 @@ export const AssistantOverlay = React.memo(() => {
WELCOME_CONVERSATION_TITLE
);
const [promptContextId, setPromptContextId] = useState();
- const { assistantTelemetry, setShowAssistantOverlay, localStorageLastConversationId } =
- useAssistantContext();
+ const { assistantTelemetry, setShowAssistantOverlay, getConversationId } = useAssistantContext();
// Bind `showAssistantOverlay` in SecurityAssistantContext to this modal instance
const showOverlay = useCallback(
@@ -44,16 +43,18 @@ export const AssistantOverlay = React.memo(() => {
promptContextId: pid,
conversationId: cid,
}: ShowAssistantOverlayProps) => {
+ const newConversationId = getConversationId(cid);
if (so)
assistantTelemetry?.reportAssistantInvoked({
- conversationId: cid ?? 'unknown',
+ conversationId: newConversationId,
invokedBy: 'click',
});
+
setIsModalVisible(so);
setPromptContextId(pid);
- setConversationId(cid);
+ setConversationId(newConversationId);
},
- [assistantTelemetry]
+ [assistantTelemetry, getConversationId]
);
useEffect(() => {
setShowAssistantOverlay(showOverlay);
@@ -63,15 +64,15 @@ export const AssistantOverlay = React.memo(() => {
const handleShortcutPress = useCallback(() => {
// Try to restore the last conversation on shortcut pressed
if (!isModalVisible) {
- setConversationId(localStorageLastConversationId ?? WELCOME_CONVERSATION_TITLE);
+ setConversationId(getConversationId());
assistantTelemetry?.reportAssistantInvoked({
invokedBy: 'shortcut',
- conversationId: localStorageLastConversationId ?? WELCOME_CONVERSATION_TITLE,
+ conversationId: getConversationId(),
});
}
setIsModalVisible(!isModalVisible);
- }, [assistantTelemetry, isModalVisible, localStorageLastConversationId]);
+ }, [assistantTelemetry, isModalVisible, getConversationId]);
// Register keyboard listener to show the modal when cmd + ; is pressed
const onKeyDown = useCallback(
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts
index 0c3c5a579d2748..8cd3c1479b46b5 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.test.ts
@@ -235,29 +235,12 @@ describe('getBlockBotConversation', () => {
});
describe('getOptionalRequestParams', () => {
- it('should return an empty object when ragOnAlerts is false', () => {
- const params = {
- alerts: true,
- alertsIndexPattern: 'indexPattern',
- allow: ['a', 'b', 'c'],
- allowReplacement: ['b', 'c'],
- ragOnAlerts: false, // <-- false
- replacements: { key: 'value' },
- size: 10,
- };
-
- const result = getOptionalRequestParams(params);
-
- expect(result).toEqual({});
- });
-
it('should return an empty object when alerts is false', () => {
const params = {
alerts: false, // <-- false
alertsIndexPattern: 'indexPattern',
allow: ['a', 'b', 'c'],
allowReplacement: ['b', 'c'],
- ragOnAlerts: true,
replacements: { key: 'value' },
size: 10,
};
@@ -267,13 +250,12 @@ describe('getBlockBotConversation', () => {
expect(result).toEqual({});
});
- it('should return the optional request params when ragOnAlerts is true and alerts is true', () => {
+ it('should return the optional request params when alerts is true', () => {
const params = {
alerts: true,
alertsIndexPattern: 'indexPattern',
allow: ['a', 'b', 'c'],
allowReplacement: ['b', 'c'],
- ragOnAlerts: true,
replacements: { key: 'value' },
size: 10,
};
@@ -292,7 +274,6 @@ describe('getBlockBotConversation', () => {
it('should return (only) the optional request params that are defined when some optional params are not provided', () => {
const params = {
alerts: true,
- ragOnAlerts: true,
allow: ['a', 'b', 'c'], // all the others are undefined
};
@@ -305,31 +286,37 @@ describe('getBlockBotConversation', () => {
});
describe('hasParsableResponse', () => {
- it('returns true when assistantLangChain is true', () => {
+ it('returns true when just assistantLangChain is true', () => {
const result = hasParsableResponse({
alerts: false,
assistantLangChain: true,
- ragOnAlerts: false,
});
expect(result).toBe(true);
});
- it('returns true when ragOnAlerts is true and alerts is true', () => {
+ it('returns true when just alerts is true', () => {
const result = hasParsableResponse({
alerts: true,
assistantLangChain: false,
- ragOnAlerts: true,
});
expect(result).toBe(true);
});
- it('returns false when assistantLangChain, ragOnAlerts, and alerts are all false', () => {
+ it('returns true when both assistantLangChain and alerts are true', () => {
+ const result = hasParsableResponse({
+ alerts: true,
+ assistantLangChain: true,
+ });
+
+ expect(result).toBe(true);
+ });
+
+ it('returns false when both assistantLangChain and alerts are false', () => {
const result = hasParsableResponse({
alerts: false,
assistantLangChain: false,
- ragOnAlerts: false,
});
expect(result).toBe(false);
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts
index 688416d2e738cb..9149d4c84ca42b 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/helpers.ts
@@ -101,7 +101,6 @@ export const getOptionalRequestParams = ({
alertsIndexPattern,
allow,
allowReplacement,
- ragOnAlerts,
replacements,
size,
}: {
@@ -109,7 +108,6 @@ export const getOptionalRequestParams = ({
alertsIndexPattern?: string;
allow?: string[];
allowReplacement?: string[];
- ragOnAlerts: boolean;
replacements?: Record;
size?: number;
}): OptionalRequestParams => {
@@ -119,10 +117,8 @@ export const getOptionalRequestParams = ({
const optionalReplacements = replacements ? { replacements } : undefined;
const optionalSize = size ? { size } : undefined;
- if (
- !ragOnAlerts || // the feature flag must be enabled
- !alerts // the settings toggle must also be enabled
- ) {
+ // the settings toggle must be enabled:
+ if (!alerts) {
return {}; // don't send any optional params
}
@@ -138,9 +134,7 @@ export const getOptionalRequestParams = ({
export const hasParsableResponse = ({
alerts,
assistantLangChain,
- ragOnAlerts,
}: {
alerts: boolean;
assistantLangChain: boolean;
- ragOnAlerts: boolean;
-}): boolean => assistantLangChain || (ragOnAlerts && alerts);
+}): boolean => assistantLangChain || alerts;
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx
index 86e0f3a460055d..190eee654bc67e 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.tsx
@@ -83,7 +83,7 @@ const AssistantComponent: React.FC = ({
http,
promptContexts,
setLastConversationId,
- localStorageLastConversationId,
+ getConversationId,
title,
allSystemPrompts,
} = useAssistantContext();
@@ -113,12 +113,7 @@ const AssistantComponent: React.FC = ({
);
const [selectedConversationId, setSelectedConversationId] = useState(
- isAssistantEnabled
- ? // if a conversationId has been provided, use that
- // if not, check local storage
- // last resort, go to welcome conversation
- conversationId ?? localStorageLastConversationId ?? WELCOME_CONVERSATION_TITLE
- : WELCOME_CONVERSATION_TITLE
+ isAssistantEnabled ? getConversationId(conversationId) : WELCOME_CONVERSATION_TITLE
);
useEffect(() => {
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx
index fcfbadb574bbdb..fb973d492a6517 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_send_messages/index.tsx
@@ -37,7 +37,6 @@ export const useSendMessages = (): UseSendMessages => {
assistantStreamingEnabled,
defaultAllow,
defaultAllowReplacement,
- ragOnAlerts,
knowledgeBase,
} = useAssistantContext();
const [isLoading, setIsLoading] = useState(false);
@@ -56,7 +55,6 @@ export const useSendMessages = (): UseSendMessages => {
assistantLangChain: knowledgeBase.assistantLangChain,
assistantStreamingEnabled,
http,
- ragOnAlerts, // feature flag
replacements,
messages,
size: knowledgeBase.latestAlerts,
@@ -74,7 +72,6 @@ export const useSendMessages = (): UseSendMessages => {
knowledgeBase.alerts,
knowledgeBase.assistantLangChain,
knowledgeBase.latestAlerts,
- ragOnAlerts,
]
);
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx
index 61f8352e0d3257..84a2ac40a6f248 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.test.tsx
@@ -12,7 +12,11 @@ import { AssistantProvider, useAssistantContext } from '.';
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { actionTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/action_type_registry.mock';
import { AssistantAvailability } from '../..';
+import { useLocalStorage } from 'react-use';
+jest.mock('react-use', () => ({
+ useLocalStorage: jest.fn().mockReturnValue(['456', jest.fn()]),
+}));
const actionTypeRegistry = actionTypeRegistryMock.create();
const mockGetInitialConversations = jest.fn(() => ({}));
const mockGetComments = jest.fn(() => []);
@@ -70,4 +74,23 @@ describe('AssistantContext', () => {
expect(mockHttp.fetch).toBeCalledWith(path);
});
+
+ test('getConversationId defaults to provided id', async () => {
+ const { result } = renderHook(useAssistantContext, { wrapper: ContextWrapper });
+ const id = result.current.getConversationId('123');
+ expect(id).toEqual('123');
+ });
+
+ test('getConversationId uses local storage id when no id is provided ', async () => {
+ const { result } = renderHook(useAssistantContext, { wrapper: ContextWrapper });
+ const id = result.current.getConversationId();
+ expect(id).toEqual('456');
+ });
+
+ test('getConversationId defaults to Welcome when no local storage id and no id is provided ', async () => {
+ (useLocalStorage as jest.Mock).mockReturnValue([undefined, jest.fn()]);
+ const { result } = renderHook(useAssistantContext, { wrapper: ContextWrapper });
+ const id = result.current.getConversationId();
+ expect(id).toEqual('Welcome');
+ });
});
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx
index 4d0eec97f2639e..024a06fd7b314b 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/index.tsx
@@ -13,6 +13,7 @@ import type { IToasts } from '@kbn/core-notifications-browser';
import { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public';
import { useLocalStorage } from 'react-use';
import type { DocLinksStart } from '@kbn/core-doc-links-browser';
+import { WELCOME_CONVERSATION_TITLE } from '../assistant/use_conversation/translations';
import { updatePromptContexts } from './helpers';
import type {
PromptContext,
@@ -88,7 +89,6 @@ export interface AssistantProviderProps {
getInitialConversations: () => Record;
modelEvaluatorEnabled?: boolean;
nameSpace?: string;
- ragOnAlerts?: boolean;
setConversations: React.Dispatch>>;
setDefaultAllow: React.Dispatch>;
setDefaultAllowReplacement: React.Dispatch>;
@@ -136,11 +136,10 @@ export interface UseAssistantContext {
}) => EuiCommentProps[];
http: HttpSetup;
knowledgeBase: KnowledgeBaseConfig;
- localStorageLastConversationId: string | undefined;
+ getConversationId: (id?: string) => string;
promptContexts: Record;
modelEvaluatorEnabled: boolean;
nameSpace: string;
- ragOnAlerts: boolean;
registerPromptContext: RegisterPromptContext;
selectedSettingsTab: SettingsTabs;
setAllQuickPrompts: React.Dispatch>;
@@ -182,7 +181,6 @@ export const AssistantProvider: React.FC = ({
getInitialConversations,
modelEvaluatorEnabled = false,
nameSpace = DEFAULT_ASSISTANT_NAMESPACE,
- ragOnAlerts = false,
setConversations,
setDefaultAllow,
setDefaultAllowReplacement,
@@ -292,6 +290,14 @@ export const AssistantProvider: React.FC = ({
[setConversations]
);
+ const getConversationId = useCallback(
+ // if a conversationId has been provided, use that
+ // if not, check local storage
+ // last resort, go to welcome conversation
+ (id?: string) => id ?? localStorageLastConversationId ?? WELCOME_CONVERSATION_TITLE,
+ [localStorageLastConversationId]
+ );
+
const value = useMemo(
() => ({
actionTypeRegistry,
@@ -319,7 +325,6 @@ export const AssistantProvider: React.FC = ({
modelEvaluatorEnabled,
promptContexts,
nameSpace,
- ragOnAlerts,
registerPromptContext,
selectedSettingsTab,
setAllQuickPrompts: setLocalStorageQuickPrompts,
@@ -334,7 +339,7 @@ export const AssistantProvider: React.FC = ({
title,
toasts,
unRegisterPromptContext,
- localStorageLastConversationId,
+ getConversationId,
setLastConversationId: setLocalStorageLastConversationId,
}),
[
@@ -358,14 +363,13 @@ export const AssistantProvider: React.FC = ({
getComments,
http,
localStorageKnowledgeBase,
- localStorageLastConversationId,
+ getConversationId,
localStorageQuickPrompts,
localStorageSystemPrompts,
modelEvaluatorEnabled,
nameSpace,
onConversationsUpdated,
promptContexts,
- ragOnAlerts,
registerPromptContext,
selectedSettingsTab,
setDefaultAllow,
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx
index 06c1b33bfda850..6e5d7e7b1b174b 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.test.tsx
@@ -22,7 +22,6 @@ const mockUseAssistantContext = {
prepend: jest.fn(),
},
},
- ragOnAlerts: true,
setAllSystemPrompts: jest.fn(),
setConversations: jest.fn(),
};
@@ -210,7 +209,7 @@ describe('Knowledge base settings', () => {
expect(queryByTestId('knowledgeBaseActionButton')).not.toBeInTheDocument();
});
- it('renders the alerts settings when ragOnAlerts is true', () => {
+ it('renders the alerts settings', () => {
const { getByTestId } = render(
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx
index bd41f5b888c931..5974bae6e5ab08 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx
+++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings.tsx
@@ -47,7 +47,7 @@ interface Props {
*/
export const KnowledgeBaseSettings: React.FC = React.memo(
({ knowledgeBase, setUpdatedKnowledgeBaseSettings }) => {
- const { http, ragOnAlerts } = useAssistantContext();
+ const { http } = useAssistantContext();
const {
data: kbStatus,
isLoading,
@@ -303,12 +303,10 @@ export const KnowledgeBaseSettings: React.FC = React.memo(
- {ragOnAlerts && (
-
- )}
+
>
);
}
diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts
index 03e989ab6a0558..e1b176e9dcaa76 100644
--- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts
+++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts
@@ -21,25 +21,27 @@ export const ASK_QUESTIONS_ABOUT = i18n.translate(
}
);
-export const LATEST_AND_RISKIEST_OPEN_ALERTS = i18n.translate(
- 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel',
- {
- defaultMessage: 'latest and riskiest open and acknowledged alerts in your environment.',
- }
-);
+export const LATEST_AND_RISKIEST_OPEN_ALERTS = (alertsCount: number) =>
+ i18n.translate(
+ 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel',
+ {
+ defaultMessage:
+ 'Send AI Assistant information about your {alertsCount} newest and riskiest open or acknowledged alerts.',
+ values: { alertsCount },
+ }
+ );
export const YOUR_ANONYMIZATION_SETTINGS = i18n.translate(
'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.yourAnonymizationSettingsLabel',
{
- defaultMessage: 'Your Anonymization settings will be applied to the alerts.',
+ defaultMessage: 'Your anonymization settings will apply to these alerts.',
}
);
export const SELECT_FEWER_ALERTS = i18n.translate(
'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.selectFewerAlertsLabel',
{
- defaultMessage:
- "Select fewer alerts if the model's maximum context length is frequently exceeded.",
+ defaultMessage: "Send fewer alerts if the model's context window is too small.",
}
);
diff --git a/x-pack/plugins/alerting/public/application/maintenance_windows.tsx b/x-pack/plugins/alerting/public/application/maintenance_windows.tsx
index 6bbe3a080943e7..bb2ba1847abac3 100644
--- a/x-pack/plugins/alerting/public/application/maintenance_windows.tsx
+++ b/x-pack/plugins/alerting/public/application/maintenance_windows.tsx
@@ -78,7 +78,7 @@ export const renderApp = ({
}) => {
const { element, history, theme$ } = mountParams;
const i18nCore = core.i18n;
- const isDarkMode = core.uiSettings.get('theme:darkMode');
+ const isDarkMode = core.theme.getTheme().darkMode;
const queryClient = new QueryClient();
diff --git a/x-pack/plugins/apm/public/components/routing/app_root/index.tsx b/x-pack/plugins/apm/public/components/routing/app_root/index.tsx
index 85df672363ce16..ea1440c91706c3 100644
--- a/x-pack/plugins/apm/public/components/routing/app_root/index.tsx
+++ b/x-pack/plugins/apm/public/components/routing/app_root/index.tsx
@@ -8,7 +8,7 @@
import { APP_WRAPPER_CLASS } from '@kbn/core/public';
import {
KibanaContextProvider,
- useUiSetting$,
+ useDarkMode,
} from '@kbn/kibana-react-plugin/public';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { Storage } from '@kbn/kibana-utils-plugin/public';
@@ -132,7 +132,7 @@ function MountApmHeaderActionMenu() {
}
export function ApmThemeProvider({ children }: { children: React.ReactNode }) {
- const [darkMode] = useUiSetting$('theme:darkMode');
+ const darkMode = useDarkMode(false);
return (
;
export type CspSettings = TypeOf;
+
+export interface BulkActionBenchmarkRulesResponse {
+ newCspSettings: SavedObjectsUpdateResponse;
+ disabledRulesCounter: number;
+}
diff --git a/x-pack/plugins/cloud_security_posture/common/types_old.ts b/x-pack/plugins/cloud_security_posture/common/types_old.ts
index d3706c51469f81..617f9dce122e80 100644
--- a/x-pack/plugins/cloud_security_posture/common/types_old.ts
+++ b/x-pack/plugins/cloud_security_posture/common/types_old.ts
@@ -181,7 +181,7 @@ export interface CnvmStatistics {
highCount: number | undefined;
mediumCount: number | undefined;
resourcesScanned: number | undefined;
- cloudRegions: number | undefined;
+ cloudAccounts: number | undefined;
}
export interface CnvmDashboardData {
diff --git a/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.test.ts b/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.test.ts
new file mode 100644
index 00000000000000..f2a35944f0825f
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.test.ts
@@ -0,0 +1,113 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { CspBenchmarkRuleMetadata } from '../types';
+import {
+ convertRuleTagsToKQL,
+ generateBenchmarkRuleTags,
+ getFindingsDetectionRuleSearchTags,
+} from './detection_rules';
+
+describe('Detection rules utils', () => {
+ it('should convert tags to KQL format', () => {
+ const inputTags = ['tag1', 'tag2', 'tag3'];
+
+ const result = convertRuleTagsToKQL(inputTags);
+
+ const expectedKQL = 'alert.attributes.tags:("tag1" AND "tag2" AND "tag3")';
+ expect(result).toBe(expectedKQL);
+ });
+
+ it('Should convert tags to KQL format', () => {
+ const inputTags = [] as string[];
+
+ const result = convertRuleTagsToKQL(inputTags);
+
+ const expectedKQL = 'alert.attributes.tags:()';
+ expect(result).toBe(expectedKQL);
+ });
+
+ it('Should generate search tags for a CSP benchmark rule', () => {
+ const cspBenchmarkRule = {
+ benchmark: {
+ id: 'cis_gcp',
+ rule_number: '1.1',
+ },
+ } as unknown as CspBenchmarkRuleMetadata;
+
+ const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);
+
+ const expectedTags = ['CIS', 'GCP', 'CIS GCP 1.1'];
+ expect(result).toEqual(expectedTags);
+ });
+
+ it('Should handle undefined benchmark object gracefully', () => {
+ const cspBenchmarkRule = { benchmark: {} } as any;
+ const expectedTags: string[] = [];
+ const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);
+ expect(result).toEqual(expectedTags);
+ });
+
+ it('Should handle undefined rule number gracefully', () => {
+ const cspBenchmarkRule = {
+ benchmark: {
+ id: 'cis_gcp',
+ },
+ } as unknown as CspBenchmarkRuleMetadata;
+ const result = getFindingsDetectionRuleSearchTags(cspBenchmarkRule);
+ const expectedTags = ['CIS', 'GCP', 'CIS GCP'];
+ expect(result).toEqual(expectedTags);
+ });
+
+ it('Should generate tags for a CSPM benchmark rule', () => {
+ const cspBenchmarkRule = {
+ benchmark: {
+ id: 'cis_gcp',
+ rule_number: '1.1',
+ posture_type: 'cspm',
+ },
+ } as unknown as CspBenchmarkRuleMetadata;
+
+ const result = generateBenchmarkRuleTags(cspBenchmarkRule);
+
+ const expectedTags = [
+ 'Cloud Security',
+ 'Use Case: Configuration Audit',
+ 'CIS',
+ 'GCP',
+ 'CIS GCP 1.1',
+ 'CSPM',
+ 'Data Source: CSPM',
+ 'Domain: Cloud',
+ ];
+ expect(result).toEqual(expectedTags);
+ });
+
+ it('Should generate tags for a KSPM benchmark rule', () => {
+ const cspBenchmarkRule = {
+ benchmark: {
+ id: 'cis_gcp',
+ rule_number: '1.1',
+ posture_type: 'kspm',
+ },
+ } as unknown as CspBenchmarkRuleMetadata;
+
+ const result = generateBenchmarkRuleTags(cspBenchmarkRule);
+
+ const expectedTags = [
+ 'Cloud Security',
+ 'Use Case: Configuration Audit',
+ 'CIS',
+ 'GCP',
+ 'CIS GCP 1.1',
+ 'KSPM',
+ 'Data Source: KSPM',
+ 'Domain: Container',
+ ];
+ expect(result).toEqual(expectedTags);
+ });
+});
diff --git a/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.ts b/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.ts
new file mode 100644
index 00000000000000..42ea7561286c16
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/common/utils/detection_rules.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { CspBenchmarkRuleMetadata } from '../types/latest';
+
+const CSP_RULE_TAG = 'Cloud Security';
+const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit';
+const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: ';
+
+const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE];
+
+export const convertRuleTagsToKQL = (tags: string[]): string => {
+ const TAGS_FIELD = 'alert.attributes.tags';
+ return `${TAGS_FIELD}:(${tags.map((tag) => `"${tag}"`).join(' AND ')})`;
+};
+
+/*
+ * Returns an array of CspFinding tags that can be used to search and filter a detection rule
+ */
+export const getFindingsDetectionRuleSearchTags = (
+ cspBenchmarkRule: CspBenchmarkRuleMetadata
+): string[] => {
+ if (!cspBenchmarkRule.benchmark || !cspBenchmarkRule.benchmark.id) {
+ // Return an empty array if benchmark ID is undefined
+ return [];
+ }
+
+ // ex: cis_gcp to ['CIS', 'GCP']
+ const benchmarkIdTags = cspBenchmarkRule.benchmark.id.split('_').map((tag) => tag.toUpperCase());
+
+ // ex: 'CIS GCP 1.1'
+ const benchmarkRuleNumberTag = cspBenchmarkRule.benchmark.rule_number
+ ? `${cspBenchmarkRule.benchmark.id.replace('_', ' ').toUpperCase()} ${
+ cspBenchmarkRule.benchmark.rule_number
+ }`
+ : cspBenchmarkRule.benchmark.id.replace('_', ' ').toUpperCase();
+
+ return benchmarkIdTags.concat([benchmarkRuleNumberTag]);
+};
+
+export const generateBenchmarkRuleTags = (rule: CspBenchmarkRuleMetadata) => {
+ return [STATIC_RULE_TAGS]
+ .concat(getFindingsDetectionRuleSearchTags(rule))
+ .concat(
+ rule.benchmark.posture_type
+ ? [
+ rule.benchmark.posture_type.toUpperCase(),
+ `${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${rule.benchmark.posture_type.toUpperCase()}`,
+ ]
+ : []
+ )
+ .concat(rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container'])
+ .flat();
+};
diff --git a/x-pack/plugins/cloud_security_posture/kibana.jsonc b/x-pack/plugins/cloud_security_posture/kibana.jsonc
index 9237ed70104ad2..56ea8549629acb 100644
--- a/x-pack/plugins/cloud_security_posture/kibana.jsonc
+++ b/x-pack/plugins/cloud_security_posture/kibana.jsonc
@@ -21,7 +21,8 @@
"cloud",
"licensing",
"share",
- "kibanaUtils"
+ "kibanaUtils",
+ "alerting"
],
"optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaReact", "usageCollection"]
diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_fetch_detection_rules_by_tags.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_fetch_detection_rules_by_tags.ts
index 309698f4219d98..dfd6f13e386921 100644
--- a/x-pack/plugins/cloud_security_posture/public/common/api/use_fetch_detection_rules_by_tags.ts
+++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_fetch_detection_rules_by_tags.ts
@@ -11,6 +11,7 @@ import { useQuery } from '@tanstack/react-query';
import { DETECTION_RULE_RULES_API_CURRENT_VERSION } from '../../../common/constants';
import { RuleResponse } from '../types';
import { DETECTION_ENGINE_RULES_KEY } from '../constants';
+import { convertRuleTagsToKQL } from '../../../common/utils/detection_rules';
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
@@ -26,16 +27,10 @@ export interface FetchRulesResponse {
data: RuleResponse[];
}
-export const TAGS_FIELD = 'alert.attributes.tags';
-
const DETECTION_ENGINE_URL = '/api/detection_engine' as const;
const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules` as const;
export const DETECTION_ENGINE_RULES_URL_FIND = `${DETECTION_ENGINE_RULES_URL}/_find` as const;
-export function convertRuleTagsToKQL(tags: string[]): string {
- return `${TAGS_FIELD}:(${tags.map((tag) => `"${tag}"`).join(' AND ')})`;
-}
-
export const useFetchDetectionRulesByTags = (tags: string[]) => {
const { http } = useKibana().services;
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_detection_rule_counter.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_detection_rule_counter.tsx
index f39379f8c4ddd8..24ef238cf57748 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_detection_rule_counter.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_detection_rule_counter.tsx
@@ -9,10 +9,8 @@ import type { HttpSetup } from '@kbn/core/public';
import React from 'react';
import { CspFinding } from '../../../../common/schemas/csp_finding';
import { DetectionRuleCounter } from '../../../components/detection_rule_counter';
-import {
- createDetectionRuleFromFinding,
- getFindingsDetectionRuleSearchTags,
-} from '../utils/create_detection_rule_from_finding';
+import { createDetectionRuleFromFinding } from '../utils/create_detection_rule_from_finding';
+import { getFindingsDetectionRuleSearchTags } from '../../../../common/utils/detection_rules';
export const FindingsDetectionRuleCounter = ({ finding }: { finding: CspFinding }) => {
const createMisconfigurationRuleFn = async (http: HttpSetup) =>
@@ -20,7 +18,7 @@ export const FindingsDetectionRuleCounter = ({ finding }: { finding: CspFinding
return (
);
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts
index b06246e6605e9b..ca0e03460c3406 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts
+++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/create_detection_rule_from_finding.ts
@@ -12,6 +12,7 @@ import {
LATEST_FINDINGS_RETENTION_POLICY,
} from '../../../../common/constants';
import { createDetectionRule } from '../../../common/api/create_detection_rule';
+import { generateBenchmarkRuleTags } from '../../../../common/utils/detection_rules';
const DEFAULT_RULE_RISK_SCORE = 0;
const DEFAULT_RULE_SEVERITY = 'low';
@@ -47,43 +48,6 @@ const convertReferencesLinksToArray = (input: string | undefined) => {
return matches.map((link) => link.replace(/^\d+\. /, '').replace(/\n/g, ''));
};
-const CSP_RULE_TAG = 'Cloud Security';
-const CSP_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit';
-const CSP_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: ';
-
-const STATIC_RULE_TAGS = [CSP_RULE_TAG, CSP_RULE_TAG_USE_CASE];
-
-/*
- * Returns an array of CspFinding tags that can be used to search and filter a detection rule
- */
-export const getFindingsDetectionRuleSearchTags = ({ rule }: CspFinding) => {
- // ex: cis_gcp to ['CIS', 'GCP']
- const benchmarkIdTags = rule.benchmark.id.split('_').map((tag) => tag.toUpperCase());
- // ex: 'CIS GCP 1.1'
- const benchmarkRuleNumberTag = `${rule.benchmark.id.replace('_', ' ').toUpperCase()} ${
- rule.benchmark.rule_number
- }`;
-
- return benchmarkIdTags.concat([benchmarkRuleNumberTag]);
-};
-
-const generateFindingsTags = (finding: CspFinding) => {
- return [STATIC_RULE_TAGS]
- .concat(getFindingsDetectionRuleSearchTags(finding))
- .concat(
- finding.rule.benchmark.posture_type
- ? [
- finding.rule.benchmark.posture_type.toUpperCase(),
- `${CSP_RULE_TAG_DATA_SOURCE_PREFIX}${finding.rule.benchmark.posture_type.toUpperCase()}`,
- ]
- : []
- )
- .concat(
- finding.rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container']
- )
- .flat();
-};
-
const generateFindingsRuleQuery = (finding: CspFinding) => {
const currentTimestamp = new Date().toISOString();
@@ -128,7 +92,7 @@ export const createDetectionRuleFromFinding = async (http: HttpSetup, finding: C
references: convertReferencesLinksToArray(finding.rule.references),
name: finding.rule.name,
description: finding.rule.rationale,
- tags: generateFindingsTags(finding),
+ tags: generateBenchmarkRuleTags(finding.rule),
investigation_fields: DEFAULT_INVESTIGATION_FIELDS,
note: finding.rule.remediation,
},
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts
index 0148105bbc83a5..7811f2c29892be 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts
@@ -13,7 +13,7 @@ export const mockCnvmDashboardData: CnvmDashboardData = {
highCount: 4715,
mediumCount: 10537,
resourcesScanned: 81,
- cloudRegions: 1,
+ cloudAccounts: 1,
},
vulnTrends: [
{
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_statistics.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_statistics.tsx
index 1552ed2821bb85..adfca7f2f02052 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_statistics.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_statistics.tsx
@@ -84,11 +84,11 @@ export const VulnerabilityStatistics = () => {
id: 'cloud-regions-stat',
title: (
),
description: i18n.translate('xpack.csp.cnvmDashboard.statistics.cloudRegionTitle', {
- defaultMessage: 'Cloud Regions',
+ defaultMessage: 'Cloud Accounts',
}),
},
{
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts
deleted file mode 100644
index 1bb2232d2834d1..00000000000000
--- a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts
+++ /dev/null
@@ -1,36 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import expect from 'expect';
-import { setRulesStates, buildRuleKey } from './utils';
-
-describe('CSP Rule State Management', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('should set rules states correctly', () => {
- const ruleIds = ['rule1', 'rule3'];
- const newState = true;
-
- const updatedRulesStates = setRulesStates(ruleIds, newState);
-
- expect(updatedRulesStates).toEqual({
- rule1: { muted: true },
- rule3: { muted: true },
- });
- });
-
- it('should build a rule key with the provided benchmarkId, benchmarkVersion, and ruleNumber', () => {
- const benchmarkId = 'randomId';
- const benchmarkVersion = 'v1';
- const ruleNumber = '001';
-
- const result = buildRuleKey(benchmarkId, benchmarkVersion, ruleNumber);
-
- expect(result).toBe(`${benchmarkId};${benchmarkVersion};${ruleNumber}`);
- });
-});
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts
index a87df39341741b..1b033867c03fd6 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts
@@ -16,6 +16,26 @@ import { CspRouter } from '../../../types';
import { CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH } from '../../../../common/constants';
import { bulkActionBenchmarkRulesHandler } from './v1';
+/**
+ This API allows bulk actions (mute or unmute) on CSP benchmark rules.
+ Request:
+ {
+ action: 'mute' | 'unmute'; // Specify the bulk action type (mute or unmute)
+ rules: [
+ {
+ rule_id: string; // Unique identifier for the rule
+ },
+ // ... (additional benchmark rules)
+ ];
+ }
+
+ Response:
+ {
+ updated_benchmark_rules: CspBenchmarkRulesStates; Benchmark rules object that were affected
+ detection_rules: string; // Status message indicating the number of detection rules affected
+ message: string; // Success message
+ }
+ */
export const defineBulkActionCspBenchmarkRulesRoute = (router: CspRouter) =>
router.versioned
.post({
@@ -42,16 +62,24 @@ export const defineBulkActionCspBenchmarkRulesRoute = (router: CspRouter) =>
const benchmarkRulesToUpdate = requestBody.rules;
+ const detectionRulesClient = (await context.alerting).getRulesClient();
+
const handlerResponse = await bulkActionBenchmarkRulesHandler(
+ cspContext.soClient,
cspContext.encryptedSavedObjects,
+ detectionRulesClient,
benchmarkRulesToUpdate,
- requestBody.action
+ requestBody.action,
+ cspContext.logger
);
- const updatedBenchmarkRules: CspBenchmarkRulesStates = handlerResponse;
+ const updatedBenchmarkRules: CspBenchmarkRulesStates =
+ handlerResponse.newCspSettings.attributes.rules!;
+
return response.ok({
body: {
updated_benchmark_rules: updatedBenchmarkRules,
+ detection_rules: `disabled ${handlerResponse.disabledRulesCounter} detections rules.`,
message: 'The bulk operation has been executed successfully.',
},
});
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts
index 3442b3f050b90c..2f03af30c58b5e 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts
@@ -9,13 +9,99 @@ import type {
SavedObjectsClientContract,
SavedObjectsUpdateResponse,
} from '@kbn/core-saved-objects-api-server';
-import { CspBenchmarkRulesStates, CspSettings } from '../../../../common/types/rules/v3';
+import type { FindResult, RulesClient } from '@kbn/alerting-plugin/server';
+import type { RuleParams } from '@kbn/alerting-plugin/server/application/rule/types';
+import type {
+ CspBenchmarkRule,
+ CspBenchmarkRulesStates,
+ CspSettings,
+} from '../../../../common/types/rules/v3';
+import {
+ convertRuleTagsToKQL,
+ generateBenchmarkRuleTags,
+} from '../../../../common/utils/detection_rules';
import {
+ CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE,
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID,
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE,
} from '../../../../common/constants';
+export const getRuleIdsToDisable = async (detectionRules: Array>) => {
+ const idsToDisable = detectionRules
+ .map((detectionRule) => {
+ return detectionRule.data.map((data) => data.id);
+ })
+ .flat();
+ return idsToDisable;
+};
+
+const disableDetectionRules = async (
+ detectionRulesClient: RulesClient,
+ detectionRules: Array>
+) => {
+ const idsToDisable = await getRuleIdsToDisable(detectionRules);
+ if (!idsToDisable.length) return;
+ return await detectionRulesClient.bulkDisableRules({ ids: idsToDisable });
+};
+
+export const getDetectionRules = async (
+ detectionRulesClient: RulesClient,
+ rulesTags: string[][]
+): Promise>> => {
+ const detectionRules = Promise.all(
+ rulesTags.map(async (ruleTags) => {
+ return detectionRulesClient.find({
+ excludeFromPublicApi: false,
+ options: {
+ filter: convertRuleTagsToKQL(ruleTags),
+ searchFields: ['tags'],
+ page: 1,
+ per_page: 1,
+ },
+ });
+ })
+ );
+
+ return detectionRules;
+};
+
+export const getBenchmarkRules = async (
+ soClient: SavedObjectsClientContract,
+ ruleIds: string[]
+): Promise> => {
+ const bulkGetObject = ruleIds.map((ruleId) => ({
+ id: ruleId,
+ type: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE,
+ }));
+ const cspBenchmarkRulesSo = await soClient.bulkGet(bulkGetObject);
+
+ const benchmarkRules = cspBenchmarkRulesSo.saved_objects.map(
+ (cspBenchmarkRule) => cspBenchmarkRule.attributes
+ );
+ return benchmarkRules;
+};
+
+export const muteDetectionRules = async (
+ soClient: SavedObjectsClientContract,
+ detectionRulesClient: RulesClient,
+ rulesIds: string[]
+): Promise => {
+ const benchmarkRules = await getBenchmarkRules(soClient, rulesIds);
+ if (benchmarkRules.includes(undefined)) {
+ throw new Error('At least one of the provided benchmark rule IDs does not exist');
+ }
+ const benchmarkRulesTags = benchmarkRules.map((benchmarkRule) =>
+ generateBenchmarkRuleTags(benchmarkRule!.metadata)
+ );
+
+ const detectionRules = await getDetectionRules(detectionRulesClient, benchmarkRulesTags);
+
+ const disabledDetectionRules = await disableDetectionRules(detectionRulesClient, detectionRules);
+
+ return disabledDetectionRules ? disabledDetectionRules.rules.length : 0;
+};
+
export const updateRulesStates = async (
encryptedSoClient: SavedObjectsClientContract,
newRulesStates: CspBenchmarkRulesStates
@@ -29,14 +115,26 @@ export const updateRulesStates = async (
);
};
-export const setRulesStates = (ruleIds: string[], state: boolean): CspBenchmarkRulesStates => {
+export const setRulesStates = (
+ ruleIds: string[],
+ state: boolean,
+ benchmarkRules: CspBenchmarkRule[]
+): CspBenchmarkRulesStates => {
const rulesStates: CspBenchmarkRulesStates = {};
- ruleIds.forEach((ruleId) => {
- rulesStates[ruleId] = { muted: state };
+ ruleIds.forEach((ruleId, index) => {
+ const benchmarkRule = benchmarkRules[index];
+ rulesStates[ruleId] = {
+ muted: state,
+ benchmark_id: benchmarkRule.metadata.benchmark.id,
+ benchmark_version: benchmarkRule.metadata.benchmark.version,
+ rule_number: benchmarkRule.metadata.benchmark.rule_number || '',
+ rule_id: benchmarkRule.metadata.id,
+ };
});
return rulesStates;
};
-export const buildRuleKey = (benchmarkId: string, benchmarkVersion: string, ruleNumber: string) => {
- return `${benchmarkId};${benchmarkVersion};${ruleNumber}`;
+export const buildRuleKey = (benchmarkRule: CspBenchmarkRule) => {
+ const ruleNumber = benchmarkRule.metadata.benchmark.rule_number;
+ return `${benchmarkRule.metadata.benchmark.id};${benchmarkRule.metadata.benchmark.version};${ruleNumber}`;
};
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts
index 42895b5eb694d1..907b8cea3d73ce 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts
@@ -5,8 +5,20 @@
* 2.0.
*/
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
-import { CspBenchmarkRules, CspBenchmarkRulesStates } from '../../../../common/types/rules/v3';
-import { buildRuleKey, setRulesStates, updateRulesStates } from './utils';
+import { Logger } from '@kbn/core/server';
+import type { RulesClient } from '@kbn/alerting-plugin/server';
+import {
+ buildRuleKey,
+ getBenchmarkRules,
+ muteDetectionRules,
+ setRulesStates,
+ updateRulesStates,
+} from './utils';
+import type {
+ BulkActionBenchmarkRulesResponse,
+ CspBenchmarkRule,
+ CspBenchmarkRules,
+} from '../../../../common/types/rules/v3';
const muteStatesMap = {
mute: true,
@@ -14,17 +26,29 @@ const muteStatesMap = {
};
export const bulkActionBenchmarkRulesHandler = async (
+ soClient: SavedObjectsClientContract,
encryptedSoClient: SavedObjectsClientContract,
+ detectionRulesClient: RulesClient,
rulesToUpdate: CspBenchmarkRules,
- action: 'mute' | 'unmute'
-): Promise => {
- const ruleKeys = rulesToUpdate.map((rule) =>
- buildRuleKey(rule.benchmark_id, rule.benchmark_version, rule.rule_number)
- );
+ action: 'mute' | 'unmute',
+ logger: Logger
+): Promise => {
+ const rulesIds = rulesToUpdate.map((rule) => rule.rule_id);
+
+ const benchmarkRules = await getBenchmarkRules(soClient, rulesIds);
+ if (benchmarkRules.includes(undefined))
+ throw new Error('At least one of the provided benchmark rule IDs does not exist');
- const newRulesStates = setRulesStates(ruleKeys, muteStatesMap[action]);
+ const rulesKeys = benchmarkRules.map((benchmarkRule) => buildRuleKey(benchmarkRule!));
+ const newRulesStates = setRulesStates(
+ rulesKeys,
+ muteStatesMap[action],
+ benchmarkRules as CspBenchmarkRule[]
+ );
const newCspSettings = await updateRulesStates(encryptedSoClient, newRulesStates);
+ const disabledRulesCounter =
+ action === 'mute' ? await muteDetectionRules(soClient, detectionRulesClient, rulesIds) : 0;
- return newCspSettings.attributes.rules!;
+ return { newCspSettings, disabledRulesCounter };
};
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts
index 8458d66817f82d..530090c326766c 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_statistics.ts
@@ -25,7 +25,7 @@ export interface VulnerabilitiesStatisticsQueryResult {
resources_scanned: {
value: number;
};
- cloud_regions: {
+ cloud_accounts: {
value: number;
};
}
@@ -51,9 +51,9 @@ export const getVulnerabilitiesStatisticsQuery = (): SearchRequest => ({
field: 'resource.id',
},
},
- cloud_regions: {
+ cloud_accounts: {
cardinality: {
- field: 'cloud.region',
+ field: 'cloud.account.id',
},
},
},
@@ -69,6 +69,6 @@ export const getVulnerabilitiesStatistics = async (esClient: ElasticsearchClient
highCount: queryResult.aggregations?.high.doc_count,
mediumCount: queryResult.aggregations?.medium.doc_count,
resourcesScanned: queryResult.aggregations?.resources_scanned.value,
- cloudRegions: queryResult.aggregations?.cloud_regions.value,
+ cloudAccounts: queryResult.aggregations?.cloud_accounts.value,
};
};
diff --git a/x-pack/plugins/cloud_security_posture/server/types.ts b/x-pack/plugins/cloud_security_posture/server/types.ts
index b6e83939b6ca74..15dee7c9941d0c 100644
--- a/x-pack/plugins/cloud_security_posture/server/types.ts
+++ b/x-pack/plugins/cloud_security_posture/server/types.ts
@@ -34,6 +34,8 @@ import type {
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
import type { FleetStartContract, FleetRequestHandlerContext } from '@kbn/fleet-plugin/server';
import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server';
+import type { AlertingApiRequestHandlerContext } from '@kbn/alerting-plugin/server';
+import type { AlertingPluginSetup } from '@kbn/alerting-plugin/public/plugin';
import { CspStatusCode, IndexDetails } from '../common/types_old';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -47,6 +49,7 @@ export interface CspServerPluginSetupDeps {
taskManager: TaskManagerSetupContract;
security: SecurityPluginSetup;
cloud: CloudSetup;
+ alerting: AlertingPluginSetup;
// optional
usageCollection?: UsageCollectionSetup;
}
@@ -80,6 +83,7 @@ export interface CspApiRequestHandlerContext {
export type CspRequestHandlerContext = CustomRequestHandlerContext<{
csp: CspApiRequestHandlerContext;
fleet: FleetRequestHandlerContext['fleet'];
+ alerting: AlertingApiRequestHandlerContext;
}>;
/**
diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json
index ad1a97748967f9..e6e0879f774190 100755
--- a/x-pack/plugins/cloud_security_posture/tsconfig.json
+++ b/x-pack/plugins/cloud_security_posture/tsconfig.json
@@ -61,6 +61,7 @@
"@kbn/field-formats-plugin",
"@kbn/data-view-field-editor-plugin",
"@kbn/securitysolution-grouping",
+ "@kbn/alerting-plugin"
],
"exclude": ["target/**/*"]
}
diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss
index a98eb200f022e7..8a0b9cc992c3ec 100644
--- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss
+++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.scss
@@ -16,7 +16,7 @@
font-weight: normal;
text-align: left;
line-height: 1.1;
- font-size: #{calc($euiFontSizeL / 2)}; // 10px
+ font-size: #{$euiFontSizeL / 2}; // 10px
}
.dataGridChart__legend--numeric {
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts
index 7412d861d7136a..e12366f42f3ef0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts
@@ -573,6 +573,32 @@ describe('MlInferenceLogic', () => {
});
describe('listeners', () => {
+ describe('clearModelPlaceholderFlag', () => {
+ it('sets placeholder flag false for selected model', () => {
+ MLInferenceLogic.actions.setInferencePipelineConfiguration({
+ ...MLInferenceLogic.values.addInferencePipelineModal.configuration,
+ modelID: 'unit-test-model',
+ isModelPlaceholderSelected: true,
+ });
+ MLInferenceLogic.actions.clearModelPlaceholderFlag('unit-test-model');
+
+ expect(
+ MLInferenceLogic.values.addInferencePipelineModal.configuration.isModelPlaceholderSelected
+ ).toBe(false);
+ });
+ it('leaves placeholder flag unmodified if another model was selected', () => {
+ MLInferenceLogic.actions.setInferencePipelineConfiguration({
+ ...MLInferenceLogic.values.addInferencePipelineModal.configuration,
+ modelID: 'unit-test-model',
+ isModelPlaceholderSelected: true,
+ });
+ MLInferenceLogic.actions.clearModelPlaceholderFlag('some-other-model-id');
+
+ expect(
+ MLInferenceLogic.values.addInferencePipelineModal.configuration.isModelPlaceholderSelected
+ ).toBe(true);
+ });
+ });
describe('createPipeline', () => {
const mockModelConfiguration = {
...DEFAULT_VALUES.addInferencePipelineModal,
@@ -639,7 +665,7 @@ describe('MlInferenceLogic', () => {
AddInferencePipelineSteps.Fields
);
});
- it('triggers pipeline fetch when moving from configuration step', () => {
+ it('triggers pipeline and model fetch when moving from configuration step', () => {
MLInferenceLogic.actions.setInferencePipelineConfiguration({
...MLInferenceLogic.values.addInferencePipelineModal.configuration,
pipelineName: 'unit-test-pipeline',
@@ -647,12 +673,14 @@ describe('MlInferenceLogic', () => {
existingPipeline: false,
});
jest.spyOn(MLInferenceLogic.actions, 'fetchPipelineByName');
+ jest.spyOn(MLInferenceLogic.actions, 'makeMLModelsRequest');
MLInferenceLogic.actions.onAddInferencePipelineStepChange(AddInferencePipelineSteps.Fields);
expect(MLInferenceLogic.actions.fetchPipelineByName).toHaveBeenCalledWith({
pipelineName: 'ml-inference-unit-test-pipeline',
});
+ expect(MLInferenceLogic.actions.makeMLModelsRequest).toHaveBeenCalledWith(undefined);
});
- it('does not trigger pipeline fetch existing pipeline is selected', () => {
+ it('does not trigger pipeline and model fetch existing pipeline is selected', () => {
MLInferenceLogic.actions.setInferencePipelineConfiguration({
...MLInferenceLogic.values.addInferencePipelineModal.configuration,
pipelineName: 'unit-test-pipeline',
@@ -660,8 +688,10 @@ describe('MlInferenceLogic', () => {
existingPipeline: true,
});
jest.spyOn(MLInferenceLogic.actions, 'fetchPipelineByName');
+ jest.spyOn(MLInferenceLogic.actions, 'makeMLModelsRequest');
MLInferenceLogic.actions.onAddInferencePipelineStepChange(AddInferencePipelineSteps.Fields);
expect(MLInferenceLogic.actions.fetchPipelineByName).not.toHaveBeenCalled();
+ expect(MLInferenceLogic.actions.makeMLModelsRequest).not.toHaveBeenCalled();
});
});
describe('fetchPipelineSuccess', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts
index 407f33eb1e2d0e..bdcf23d71a743d 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts
@@ -133,6 +133,7 @@ export interface MLInferenceProcessorsActions {
>['apiSuccess'];
attachPipeline: () => void;
clearFetchedPipeline: FetchPipelineApiLogicActions['apiReset'];
+ clearModelPlaceholderFlag: (modelId: string) => { modelId: string };
createApiError: Actions<
CreateMlInferencePipelineApiLogicArgs,
CreateMlInferencePipelineResponse
@@ -222,13 +223,13 @@ export const MLInferenceLogic = kea<
}),
attachPipeline: true,
clearFormErrors: true,
+ clearModelPlaceholderFlag: (modelId: string) => ({ modelId }),
createPipeline: true,
onAddInferencePipelineStepChange: (step: AddInferencePipelineSteps) => ({ step }),
removeFieldFromMapping: (fieldName: string) => ({ fieldName }),
selectExistingPipeline: (pipelineName: string) => ({ pipelineName }),
selectFields: (fieldNames: string[]) => ({ fieldNames }),
setAddInferencePipelineStep: (step: AddInferencePipelineSteps) => ({ step }),
- setFormErrors: (inputErrors: AddInferencePipelineFormErrors) => ({ inputErrors }),
setIndexName: (indexName: string) => ({ indexName }),
setInferencePipelineConfiguration: (configuration: InferencePipelineConfiguration) => ({
configuration,
@@ -299,6 +300,19 @@ export const MLInferenceLogic = kea<
pipelineName,
});
},
+ clearModelPlaceholderFlag: ({ modelId }) => {
+ const {
+ addInferencePipelineModal: { configuration },
+ } = values;
+
+ // Don't change the flag if the user clicked away from the selected model
+ if (modelId !== configuration.modelID) return;
+
+ actions.setInferencePipelineConfiguration({
+ ...configuration,
+ isModelPlaceholderSelected: false,
+ });
+ },
createPipeline: () => {
const {
addInferencePipelineModal: { configuration, indexName },
@@ -361,6 +375,9 @@ export const MLInferenceLogic = kea<
});
// Continue to the next step so we don't have to save it to state, we will change
// back to the Configuration step if we find a pipeline with the same name
+
+ // Re-fetch ML model list to include those that were deployed in this step
+ actions.makeMLModelsRequest(undefined);
}
actions.setAddInferencePipelineStep(step);
},
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.test.ts
index 1252d77bb776aa..b0c26aaf8be8c1 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.test.ts
@@ -71,6 +71,15 @@ describe('ModelSelectLogic', () => {
expect(ModelSelectLogic.actions.startPollingModels).toHaveBeenCalled();
});
+ it('sets selected model as non-placeholder', () => {
+ jest.spyOn(ModelSelectLogic.actions, 'clearModelPlaceholderFlagFromMLInferenceLogic');
+
+ ModelSelectLogic.actions.createModelSuccess(CREATE_MODEL_API_RESPONSE);
+
+ expect(
+ ModelSelectLogic.actions.clearModelPlaceholderFlagFromMLInferenceLogic
+ ).toHaveBeenCalledWith(CREATE_MODEL_API_RESPONSE.modelId);
+ });
});
describe('fetchModels', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.ts
index 6fe25cd3c8b5f8..4074ffac92f6b4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select_logic.ts
@@ -32,24 +32,22 @@ import {
} from './ml_inference_logic';
export interface ModelSelectActions {
+ clearModelPlaceholderFlagFromMLInferenceLogic: MLInferenceProcessorsActions['clearModelPlaceholderFlag'];
createModel: (modelId: string) => { modelId: string };
createModelError: CreateModelApiLogicActions['apiError'];
createModelMakeRequest: CreateModelApiLogicActions['makeRequest'];
createModelSuccess: CreateModelApiLogicActions['apiSuccess'];
-
fetchModels: () => void;
fetchModelsError: CachedFetchModlesApiLogicActions['apiError'];
fetchModelsMakeRequest: CachedFetchModlesApiLogicActions['makeRequest'];
fetchModelsSuccess: CachedFetchModlesApiLogicActions['apiSuccess'];
- startPollingModels: CachedFetchModlesApiLogicActions['startPolling'];
-
+ setInferencePipelineConfiguration: MLInferenceProcessorsActions['setInferencePipelineConfiguration'];
+ setInferencePipelineConfigurationFromMLInferenceLogic: MLInferenceProcessorsActions['setInferencePipelineConfiguration'];
startModel: (modelId: string) => { modelId: string };
startModelError: CreateModelApiLogicActions['apiError'];
startModelMakeRequest: StartModelApiLogicActions['makeRequest'];
startModelSuccess: StartModelApiLogicActions['apiSuccess'];
-
- setInferencePipelineConfiguration: MLInferenceProcessorsActions['setInferencePipelineConfiguration'];
- setInferencePipelineConfigurationFromMLInferenceLogic: MLInferenceProcessorsActions['setInferencePipelineConfiguration'];
+ startPollingModels: CachedFetchModlesApiLogicActions['startPolling'];
}
export interface ModelSelectValues {
@@ -96,6 +94,7 @@ export const ModelSelectLogic = kea {
actions.createModelMakeRequest({ modelId });
},
- createModelSuccess: () => {
+ createModelSuccess: (response) => {
actions.startPollingModels();
+ // The create action succeeded, so the model is no longer a placeholder
+ actions.clearModelPlaceholderFlagFromMLInferenceLogic(response.modelId);
},
fetchModels: () => {
actions.fetchModelsMakeRequest({});
},
- startModel: ({ modelId }) => {
- actions.startModelMakeRequest({ modelId });
- },
setInferencePipelineConfiguration: ({ configuration }) => {
actions.setInferencePipelineConfigurationFromMLInferenceLogic(configuration);
},
+ startModel: ({ modelId }) => {
+ actions.startModelMakeRequest({ modelId });
+ },
startModelSuccess: () => {
actions.startPollingModels();
},
diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts
index 2262879657ec4e..abdac9fb57e0fd 100644
--- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys.ts
@@ -54,9 +54,19 @@ export function registerApiKeysRoutes(
const { client } = (await context.core).elasticsearch;
const user = security.authc.getCurrentUser(request);
if (user) {
- const apiKeys = await client.asCurrentUser.security.getApiKey({ username: user.username });
- const validKeys = apiKeys.api_keys.filter(({ invalidated }) => !invalidated);
- return response.ok({ body: { api_keys: validKeys } });
+ try {
+ const apiKeys = await client.asCurrentUser.security.getApiKey({
+ username: user.username,
+ });
+ const validKeys = apiKeys.api_keys.filter(({ invalidated }) => !invalidated);
+ return response.ok({ body: { api_keys: validKeys } });
+ } catch {
+ // Ideally we check the error response here for unauthorized user
+ // Unfortunately the error response is not structured enough for us to filter those
+ // Always returning an empty array should also be fine, and deals with transient errors
+
+ return response.ok({ body: { api_keys: [] } });
+ }
}
return response.customError({
body: 'Could not retrieve current user, security plugin is not ready',
diff --git a/x-pack/plugins/exploratory_view/public/application/index.tsx b/x-pack/plugins/exploratory_view/public/application/index.tsx
index f3189e7cf660b4..d496af2b23fb95 100644
--- a/x-pack/plugins/exploratory_view/public/application/index.tsx
+++ b/x-pack/plugins/exploratory_view/public/application/index.tsx
@@ -52,7 +52,7 @@ export const renderApp = ({
}) => {
const { element, history, theme$ } = appMountParameters;
const i18nCore = core.i18n;
- const isDarkMode = core.uiSettings.get('theme:darkMode');
+ const isDarkMode = core.theme.getTheme().darkMode;
core.chrome.setHelpExtension({
appName: i18n.translate('xpack.exploratoryView.feedbackMenu.appName', {
diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/index.tsx
index d5cb78b84f6428..8fe530c0b5bdf3 100644
--- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/index.tsx
+++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/index.tsx
@@ -33,7 +33,7 @@ function ExploratoryViewEmbeddable(props: ExploratoryEmbeddableComponentProps) {
export function getExploratoryViewEmbeddable(
services: CoreStart & ExploratoryViewPublicPluginsStart
) {
- const { lens, dataViews: dataViewsService, uiSettings } = services;
+ const { lens, dataViews: dataViewsService, theme } = services;
const dataViewCache: Record = {};
@@ -70,7 +70,7 @@ export function getExploratoryViewEmbeddable(
const series = attributes[0];
- const isDarkMode = uiSettings?.get('theme:darkMode');
+ const isDarkMode = theme?.getTheme().darkMode ?? false;
const { data: lensHelper, loading: lensLoading } = useFetcher(async () => {
if (lenStateHelperPromise) {
diff --git a/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss b/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss
index 28e05afe0c7816..add1d0bdf8aa33 100644
--- a/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss
+++ b/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss
@@ -28,7 +28,7 @@
.gphGuidancePanel__itemIcon {
position: absolute;
left: 0;
- top: -(calc($euiSizeXS / 2));
+ top: -($euiSizeXS / 2);
width: $euiSizeL;
height: $euiSizeL;
padding: $euiSizeXS;
diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts
index 5e6f809645ac1b..68fe2503ace143 100644
--- a/x-pack/plugins/infra/server/features.ts
+++ b/x-pack/plugins/infra/server/features.ts
@@ -8,7 +8,10 @@
import { i18n } from '@kbn/i18n';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
import { logViewSavedObjectName } from '@kbn/logs-shared-plugin/server';
-import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils';
+import {
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
+ OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
+} from '@kbn/rule-data-utils';
import { ES_QUERY_ID } from '@kbn/rule-data-utils';
import { metricsDataSourceSavedObjectName } from '@kbn/metrics-data-access-plugin/server';
import { LOG_DOCUMENT_COUNT_RULE_TYPE_ID } from '../common/alerting/logs/log_threshold/types';
@@ -24,6 +27,7 @@ const metricRuleTypes = [
METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
ES_QUERY_ID,
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
];
export const METRICS_FEATURE = {
@@ -89,6 +93,7 @@ const logsRuleTypes = [
LOG_DOCUMENT_COUNT_RULE_TYPE_ID,
ES_QUERY_ID,
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
];
export const LOGS_FEATURE = {
diff --git a/x-pack/plugins/kubernetes_security/public/test/index.tsx b/x-pack/plugins/kubernetes_security/public/test/index.tsx
index 1ca31c8e0baf00..a267169e6cf18a 100644
--- a/x-pack/plugins/kubernetes_security/public/test/index.tsx
+++ b/x-pack/plugins/kubernetes_security/public/test/index.tsx
@@ -11,7 +11,6 @@ import { render as reactRender, RenderOptions, RenderResult } from '@testing-lib
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Router } from '@kbn/shared-ux-router';
import { History } from 'history';
-import useObservable from 'react-use/lib/useObservable';
import { I18nProvider } from '@kbn/i18n-react';
import { CoreStart } from '@kbn/core/public';
import { coreMock } from '@kbn/core/public/mocks';
@@ -65,8 +64,8 @@ const AppRootProvider = memo<{
history: History;
coreStart: CoreStart;
children: ReactNode | ReactNode[];
-}>(({ history, coreStart: { http, notifications, uiSettings, application }, children }) => {
- const isDarkMode = useObservable(uiSettings.get$('theme:darkMode'));
+}>(({ history, coreStart: { http, notifications, theme, application }, children }) => {
+ const isDarkMode = useMemo(() => theme.getTheme().darkMode, [theme]);
const services = useMemo(
() => ({ http, notifications, application }),
[application, http, notifications]
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss
index 35606c67382d58..cd2ee706c1e18e 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss
@@ -25,8 +25,8 @@
flex: 0 0 auto;
height: $lnsSuggestionHeight;
margin-right: $euiSizeS;
- margin-left: calc($euiSizeXS / 2);
- margin-bottom: calc($euiSizeXS / 2);
+ margin-left: $euiSizeXS / 2;
+ margin-bottom: $euiSizeXS / 2;
padding: 0 $euiSizeS;
box-shadow: none !important; // sass-lint:disable-line no-important
diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss
index 7f1c61801a4f3c..c88f343f7dbfa1 100644
--- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss
@@ -128,7 +128,7 @@
background-color: $euiColorEmptyShade;
border: $euiBorderThin;
color: $euiTextColor;
- border-radius: calc($euiBorderRadius / 2);
+ border-radius: $euiBorderRadius / 2;
height: $euiSize;
width: $euiSizeXL;
line-height: $euiSize;
diff --git a/x-pack/plugins/ml/common/constants/alerts.ts b/x-pack/plugins/ml/common/constants/alerts.ts
index 4582f06b214b6b..81081cd79306af 100644
--- a/x-pack/plugins/ml/common/constants/alerts.ts
+++ b/x-pack/plugins/ml/common/constants/alerts.ts
@@ -12,11 +12,12 @@ import {
ALERT_RULE_NAME,
ALERT_START,
ALERT_STATUS,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
} from '@kbn/rule-data-utils';
import { JobsHealthTests } from '../types/alerts';
export const ML_ALERT_TYPES = {
- ANOMALY_DETECTION: 'xpack.ml.anomaly_detection_alert',
+ ANOMALY_DETECTION: ML_ANOMALY_DETECTION_RULE_TYPE_ID,
AD_JOBS_HEALTH: 'xpack.ml.anomaly_detection_jobs_health',
} as const;
diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts
index 1aed6088b79d85..f994ae95682cc8 100644
--- a/x-pack/plugins/ml/common/index.ts
+++ b/x-pack/plugins/ml/common/index.ts
@@ -9,3 +9,4 @@ export { composeValidators, patternValidator } from './util/validators';
export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capabilities';
export { DATAFEED_STATE, JOB_STATE } from './constants/states';
export type { MlSummaryJob, SummaryJobState } from './types/anomaly_detection_jobs';
+export { ML_ALERT_TYPES } from './constants/alerts';
diff --git a/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss b/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss
index 1b091e4046c502..e33811aa9a8ccc 100644
--- a/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss
+++ b/x-pack/plugins/ml/public/application/components/influencers_list/_influencers_list.scss
@@ -28,7 +28,7 @@
}
.progress-bar {
- height: calc($euiSizeXS / 2);
+ height: $euiSizeXS / 2;
margin-top: $euiSizeM;
text-align: right;
line-height: 18px; // SASSTODO: Calc proper value
@@ -96,7 +96,7 @@
font-size: 11px;
line-height: 14px;
border-radius: $euiBorderRadius;
- padding: calc($euiSizeXS / 2);
+ padding: $euiSizeXS / 2;
margin-top: $euiSizeXS;
display: inline-block;
border: $euiBorderThin;
diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss b/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss
index 09605c4016565d..03eca2842c3002 100644
--- a/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss
+++ b/x-pack/plugins/ml/public/application/components/rule_editor/_rule_editor.scss
@@ -41,7 +41,7 @@
// SASSTODO: Dangerous EUI overwrite
.scope-field-checkbox {
- margin-right: calc($euiSizeXS / 2);
+ margin-right: $euiSizeXS / 2;
.euiCheckbox {
margin-top: $euiSizeXS;
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss
index 29967e8db9b3f0..55ebfe8ab3edb9 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/_explorer_chart.scss
@@ -15,7 +15,7 @@
rect.selected-interval {
fill: rgba(200, 200, 200, .1);
stroke: $euiColorDarkShade;
- stroke-width: calc($euiSizeXS / 2);
+ stroke-width: $euiSizeXS / 2;
stroke-opacity: .8;
}
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss
index 656f38590d3a5e..a7186597b41356 100644
--- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss
+++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss
@@ -40,10 +40,10 @@ $mlAnnotationRectDefaultFillOpacity: .05;
}
.mlAnnotationRect-isBlur {
- stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2);
+ stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity / 2;
transition: stroke-opacity $euiAnimSpeedFast;
- fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2);
+ fill-opacity: $mlAnnotationRectDefaultFillOpacity / 2;
transition: fill-opacity $euiAnimSpeedFast;
}
@@ -95,9 +95,9 @@ $mlAnnotationRectDefaultFillOpacity: .05;
}
.mlContextAnnotationRect-isBlur {
- stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2);
+ stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity / 2;
transition: stroke-opacity $euiAnimSpeedFast;
- fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2);
+ fill-opacity: $mlAnnotationRectDefaultFillOpacity / 2;
transition: fill-opacity $euiAnimSpeedFast;
}
diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx
index f34cde08370500..b9fd25f60c3675 100644
--- a/x-pack/plugins/monitoring/public/application/index.tsx
+++ b/x-pack/plugins/monitoring/public/application/index.tsx
@@ -8,11 +8,10 @@
import { AppMountParameters, CoreStart, CoreTheme, MountPoint } from '@kbn/core/public';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
-import React, { useMemo } from 'react';
+import React from 'react';
import ReactDOM from 'react-dom';
import { Redirect } from 'react-router-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router';
-import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs';
import {
CODE_PATH_APM,
@@ -100,12 +99,7 @@ const MonitoringApp: React.FC<{
theme$: Observable;
}> = ({ core, plugins, externalConfig, setHeaderActionMenu, theme$ }) => {
const history = createPreserveQueryHistory();
-
- const darkModeObservable: Observable = useMemo(
- () => core.uiSettings!.get$('theme:darkMode'),
- [core.uiSettings]
- );
- const darkMode = useObservable(darkModeObservable, core.uiSettings!.get('theme:darkMode'));
+ const darkMode = core.theme.getTheme().darkMode;
return (
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss
index 961e0350ccc85f..c46d7a048b93ba 100644
--- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss
+++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.scss
@@ -22,7 +22,7 @@
margin: $euiSizeS;
border: 1px solid $euiColorMediumShade;
border-radius: $euiSizeXS;
- padding: calc($euiSizeXS / 2) 0;
+ padding: $euiSizeXS / 2 0;
&.monChild--index {
border-left: $euiSizeXS solid $euiColorSuccess;
diff --git a/x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss b/x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss
index 50c705f80650ef..2d36e7fc90f225 100644
--- a/x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss
+++ b/x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss
@@ -1,7 +1,7 @@
.monStatusIcon {
display: inline-block;
margin-left: $euiSizeXS;
- padding: calc($euiSizeXS / 2) $euiSizeS;
+ padding: ($euiSizeXS / 2) $euiSizeS;
border-radius: $euiBorderRadius;
color: $euiColorGhost;
min-width: 1.9em;
diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.test.ts b/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.test.ts
new file mode 100644
index 00000000000000..6971ff2d33c2db
--- /dev/null
+++ b/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.test.ts
@@ -0,0 +1,157 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { DiscoverAppLocatorParams } from '@kbn/discover-plugin/common';
+import { Aggregators } from './types';
+import { LocatorPublic } from '@kbn/share-plugin/common';
+import { getViewInAppUrl, GetViewInAppUrlArgs } from './get_view_in_app_url';
+
+describe('getViewInAppUrl', () => {
+ const logExplorerLocator = {
+ getRedirectUrl: jest.fn(() => 'mockedGetRedirectUrl'),
+ } as unknown as LocatorPublic;
+ const startedAt = '2023-12-07T16:30:15.403Z';
+ const endedAt = '2023-12-07T20:30:15.403Z';
+ const returnedTimeRange = {
+ // Duration 4 hour, time range will be extended it with 30 minutes from each side
+ from: '2023-12-07T16:00:15.403Z',
+ to: '2023-12-07T21:00:15.403Z',
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('Should return empty string if logExplorerLocator is not provided', () => {
+ const args: GetViewInAppUrlArgs = {
+ metrics: [],
+ startedAt,
+ endedAt,
+ };
+
+ expect(getViewInAppUrl(args)).toBe('');
+ });
+
+ it('should call getRedirectUrl with data view, time range and filters', () => {
+ const args: GetViewInAppUrlArgs = {
+ metrics: [
+ {
+ name: 'A',
+ aggType: Aggregators.COUNT,
+ filter: 'mockedCountFilter',
+ },
+ ],
+ logExplorerLocator,
+ startedAt,
+ endedAt,
+ filter: 'mockedFilter',
+ dataViewId: 'mockedDataViewId',
+ };
+
+ expect(getViewInAppUrl(args)).toBe('mockedGetRedirectUrl');
+ expect(logExplorerLocator.getRedirectUrl).toHaveBeenCalledWith({
+ dataset: args.dataViewId,
+ timeRange: returnedTimeRange,
+ query: {
+ query: 'mockedFilter and mockedCountFilter',
+ language: 'kuery',
+ },
+ });
+ });
+
+ it('should call getRedirectUrl with only count filter', () => {
+ const args: GetViewInAppUrlArgs = {
+ metrics: [
+ {
+ name: 'A',
+ aggType: Aggregators.COUNT,
+ filter: 'mockedCountFilter',
+ },
+ ],
+ logExplorerLocator,
+ startedAt,
+ endedAt,
+ };
+
+ expect(getViewInAppUrl(args)).toBe('mockedGetRedirectUrl');
+ expect(logExplorerLocator.getRedirectUrl).toHaveBeenCalledWith({
+ dataset: undefined,
+ timeRange: returnedTimeRange,
+ query: {
+ query: 'mockedCountFilter',
+ language: 'kuery',
+ },
+ });
+ });
+
+ it('should call getRedirectUrl with only filter', () => {
+ const args: GetViewInAppUrlArgs = {
+ logExplorerLocator,
+ startedAt,
+ endedAt,
+ filter: 'mockedFilter',
+ };
+
+ expect(getViewInAppUrl(args)).toBe('mockedGetRedirectUrl');
+ expect(logExplorerLocator.getRedirectUrl).toHaveBeenCalledWith({
+ dataset: undefined,
+ timeRange: returnedTimeRange,
+ query: {
+ query: 'mockedFilter',
+ language: 'kuery',
+ },
+ });
+ });
+
+ it('should call getRedirectUrl with empty query if metrics and filter are not not provided', () => {
+ const args: GetViewInAppUrlArgs = {
+ logExplorerLocator,
+ startedAt,
+ endedAt,
+ };
+
+ expect(getViewInAppUrl(args)).toBe('mockedGetRedirectUrl');
+ expect(logExplorerLocator.getRedirectUrl).toHaveBeenCalledWith({
+ dataset: undefined,
+ timeRange: returnedTimeRange,
+ query: {
+ query: '',
+ language: 'kuery',
+ },
+ });
+ });
+
+ it('should call getRedirectUrl with empty if there are multiple metrics', () => {
+ const args: GetViewInAppUrlArgs = {
+ metrics: [
+ {
+ name: 'A',
+ aggType: Aggregators.COUNT,
+ filter: 'mockedCountFilter',
+ },
+ {
+ name: 'A',
+ aggType: Aggregators.AVERAGE,
+ field: 'mockedAvgField',
+ },
+ ],
+ logExplorerLocator,
+ startedAt,
+ endedAt,
+ };
+
+ expect(getViewInAppUrl(args)).toBe('mockedGetRedirectUrl');
+ expect(logExplorerLocator.getRedirectUrl).toHaveBeenCalledWith({
+ dataset: undefined,
+ timeRange: returnedTimeRange,
+ query: {
+ query: '',
+ language: 'kuery',
+ },
+ });
+ });
+});
diff --git a/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.ts b/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.ts
index 658d1debe0a94b..27efa730dccb77 100644
--- a/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.ts
+++ b/x-pack/plugins/observability/common/custom_threshold_rule/get_view_in_app_url.ts
@@ -11,21 +11,27 @@ import type { TimeRange } from '@kbn/es-query';
import type { LocatorPublic } from '@kbn/share-plugin/common';
import type { CustomThresholdExpressionMetric } from './types';
-export const getViewInAppUrl = (
- metrics: CustomThresholdExpressionMetric[],
- startedAt?: string,
- logExplorerLocator?: LocatorPublic,
- filter?: string,
- dataViewId?: string,
- endedAt?: string
-) => {
+export interface GetViewInAppUrlArgs {
+ dataViewId?: string;
+ endedAt?: string;
+ startedAt?: string;
+ filter?: string;
+ logExplorerLocator?: LocatorPublic;
+ metrics?: CustomThresholdExpressionMetric[];
+}
+
+export const getViewInAppUrl = ({
+ dataViewId,
+ endedAt,
+ startedAt = new Date().toISOString(),
+ filter,
+ logExplorerLocator,
+ metrics = [],
+}: GetViewInAppUrlArgs) => {
if (!logExplorerLocator) return '';
- let timeRange: TimeRange | undefined;
- if (startedAt) {
- timeRange = getPaddedAlertTimeRange(startedAt, endedAt);
- timeRange.to = endedAt ? timeRange.to : 'now';
- }
+ const timeRange: TimeRange | undefined = getPaddedAlertTimeRange(startedAt, endedAt);
+ timeRange.to = endedAt ? timeRange.to : 'now';
const query = {
query: '',
diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx
index 7b01bc1f8eeb1d..23a0952ed91db4 100644
--- a/x-pack/plugins/observability/public/application/index.tsx
+++ b/x-pack/plugins/observability/public/application/index.tsx
@@ -67,7 +67,7 @@ export const renderApp = ({
}) => {
const { element, history, theme$ } = appMountParameters;
const i18nCore = core.i18n;
- const isDarkMode = core.uiSettings.get('theme:darkMode');
+ const isDarkMode = core.theme.getTheme().darkMode;
core.chrome.setHelpExtension({
appName: i18n.translate('xpack.observability.feedbackMenu.appName', {
diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx
index 010642acdf551f..fb90056b5193e6 100644
--- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx
+++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx
@@ -67,7 +67,7 @@ export function ExpressionChart({
timeRange,
timeFieldName,
}: Props) {
- const { charts, uiSettings } = useKibana().services;
+ const { charts, uiSettings, theme } = useKibana().services;
const { isLoading, data } = useExpressionChartData(
expression,
derivedIndexPattern,
@@ -90,7 +90,7 @@ export function ExpressionChart({
return ;
}
- const isDarkMode = uiSettings?.get('theme:darkMode') || false;
+ const isDarkMode = theme?.getTheme().darkMode ?? false;
const firstSeries = first(first(data.pages)!.series);
// Creating a custom series where the ID is changed to 0
// so that we can get a proper domain
diff --git a/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts
index 24e8a0fc107fb9..99a448a36405d1 100644
--- a/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts
+++ b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts
@@ -6,13 +6,17 @@
*/
import { useMemo } from 'react';
-import { ES_QUERY_ID } from '@kbn/rule-data-utils';
+import { ES_QUERY_ID, ML_ANOMALY_DETECTION_RULE_TYPE_ID } from '@kbn/rule-data-utils';
import { usePluginContext } from './use_plugin_context';
export function useGetFilteredRuleTypes() {
const { observabilityRuleTypeRegistry } = usePluginContext();
return useMemo(() => {
- return [ES_QUERY_ID, ...observabilityRuleTypeRegistry.list()];
+ return [
+ ES_QUERY_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
+ ...observabilityRuleTypeRegistry.list(),
+ ];
}, [observabilityRuleTypeRegistry]);
}
diff --git a/x-pack/plugins/observability/public/pages/alerts/helpers/parse_alert.ts b/x-pack/plugins/observability/public/pages/alerts/helpers/parse_alert.ts
index 33cff33c663a17..2ffea657998676 100644
--- a/x-pack/plugins/observability/public/pages/alerts/helpers/parse_alert.ts
+++ b/x-pack/plugins/observability/public/pages/alerts/helpers/parse_alert.ts
@@ -38,10 +38,17 @@ export const parseAlert =
};
const formatter = observabilityRuleTypeRegistry.getFormatter(parsedFields[ALERT_RULE_TYPE_ID]!);
+ let formattedFields = {};
+ try {
+ formattedFields =
+ formatter?.({ fields: parsedFields, formatters: { asDuration, asPercent } }) ?? {};
+ } catch (error) {
+ // Ignore formatted fields if there is a formatting error
+ }
const formatted = {
link: undefined,
reason: parsedFields[ALERT_REASON] ?? parsedFields[ALERT_RULE_NAME] ?? '',
- ...(formatter?.({ fields: parsedFields, formatters: { asDuration, asPercent } }) ?? {}),
+ ...formattedFields,
};
return {
diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/palette_legends.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/palette_legends.tsx
index 1887f203200854..20f7d47e08d23c 100644
--- a/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/palette_legends.tsx
+++ b/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/palette_legends.tsx
@@ -17,7 +17,7 @@ import {
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n-react';
import { euiLightVars, euiDarkVars } from '@kbn/ui-theme';
-import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
+import { useDarkMode } from '@kbn/kibana-react-plugin/public';
import { getCoreVitalTooltipMessage, Thresholds } from './core_vital_item';
import {
LEGEND_NEEDS_IMPROVEMENT_LABEL,
@@ -50,8 +50,7 @@ interface Props {
}
export function PaletteLegends({ ranks, title, onItemHover, thresholds, isCls }: Props) {
- const [darkMode] = useUiSetting$('theme:darkMode');
-
+ const darkMode = useDarkMode(false);
const palette = euiPaletteForStatus(3);
const labels = [LEGEND_GOOD_LABEL, LEGEND_NEEDS_IMPROVEMENT_LABEL, LEGEND_POOR_LABEL];
diff --git a/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx b/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx
index a2939bb4876e9e..ae896b66d990fd 100644
--- a/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx
+++ b/x-pack/plugins/observability/public/pages/rules/rules_tab.tsx
@@ -9,6 +9,7 @@ import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';
import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public';
import { RuleStatus } from '@kbn/triggers-actions-ui-plugin/public';
+import { AlertConsumers } from '@kbn/rule-data-utils';
import { useKibana } from '../../utils/kibana_react';
import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types';
import { observabilityAlertFeatureIds } from '../../../common/constants';
@@ -97,6 +98,7 @@ export function RulesTab({ setRefresh, stateRefresh }: RulesTabProps) {
onSearchFilterChange={handleSearchFilterChange}
onStatusFilterChange={handleStatusFilterChange}
onTypeFilterChange={handleTypeFilterChange}
+ initialSelectedConsumer={AlertConsumers.LOGS}
/>
);
}
diff --git a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts
index 643d5ffd4f337d..c9079991bdf4d3 100644
--- a/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts
+++ b/x-pack/plugins/observability/public/rules/register_observability_rule_types.ts
@@ -145,13 +145,13 @@ export const registerObservabilityRuleTypes = async (
const dataViewId = getDataViewId(searchConfiguration);
return {
reason: fields[ALERT_REASON] ?? '-',
- link: getViewInAppUrl(
+ link: getViewInAppUrl({
metrics,
- fields[ALERT_START],
+ startedAt: fields[ALERT_START],
logExplorerLocator,
- (searchConfiguration?.query as { query: string }).query,
- dataViewId
- ),
+ filter: (searchConfiguration?.query as { query: string }).query,
+ dataViewId,
+ }),
hasBasePath: true,
};
},
diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts
index b769f5f32b73bb..f581236e229250 100644
--- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts
+++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts
@@ -27,10 +27,11 @@ import {
CustomMetricExpressionParams,
CustomThresholdExpressionMetric,
} from '../../../../common/custom_threshold_rule/types';
+import { getViewInAppUrl } from '../../../../common/custom_threshold_rule/get_view_in_app_url';
jest.mock('./lib/evaluate_rule', () => ({ evaluateRule: jest.fn() }));
jest.mock('../../../../common/custom_threshold_rule/get_view_in_app_url', () => ({
- getViewInAppUrl: () => 'mockedViewInApp',
+ getViewInAppUrl: jest.fn().mockReturnValue('mockedViewInApp'),
}));
interface AlertTestInstance {
@@ -67,6 +68,7 @@ const logger = {
const STARTED_AT_MOCK_DATE = new Date();
+const mockQuery = 'mockQuery';
const mockOptions = {
executionId: '',
startedAt: STARTED_AT_MOCK_DATE,
@@ -74,7 +76,7 @@ const mockOptions = {
params: {
searchConfiguration: {
query: {
- query: '',
+ query: mockQuery,
language: 'kuery',
},
},
@@ -1058,6 +1060,7 @@ describe('The custom threshold alert type', () => {
);
});
});
+
describe('querying with the count aggregator', () => {
afterAll(() => clearInstances());
const instanceID = '*';
@@ -1196,6 +1199,63 @@ describe('The custom threshold alert type', () => {
});
});
});
+
+ describe('querying recovered alert with a count aggregator', () => {
+ afterAll(() => clearInstances());
+ const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') =>
+ executor({
+ ...mockOptions,
+ services,
+ params: {
+ ...mockOptions.params,
+ sourceId,
+ criteria: [
+ {
+ ...customThresholdCountCriterion,
+ comparator,
+ threshold,
+ },
+ ],
+ },
+ });
+ test('alerts based on the doc_count value instead of the aggregatedValue', async () => {
+ setEvaluationResults([
+ {
+ '*': {
+ ...customThresholdCountCriterion,
+ comparator: Comparator.GT,
+ threshold: [0.9],
+ currentValue: 1,
+ timestamp: new Date().toISOString(),
+ shouldFire: true,
+ isNoData: false,
+ bucketKey: { groupBy0: 'a' },
+ },
+ },
+ ]);
+ const mockedSetContext = jest.fn();
+ services.alertFactory.done.mockImplementation(() => {
+ return {
+ getRecoveredAlerts: jest.fn().mockReturnValue([
+ {
+ setContext: mockedSetContext,
+ getId: jest.fn().mockReturnValue('mockedId'),
+ },
+ ]),
+ };
+ });
+ await execute(Comparator.GT, [0.9]);
+ const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
+ expect(getViewInAppUrl).toBeCalledWith({
+ dataViewId: 'c34a7c79-a88b-4b4a-ad19-72f6d24104e4',
+ filter: mockQuery,
+ logExplorerLocator: undefined,
+ metrics: customThresholdCountCriterion.metrics,
+ startedAt: expect.stringMatching(ISO_DATE_REGEX),
+ });
+ });
+ });
+
describe("querying a metric that hasn't reported data", () => {
afterAll(() => clearInstances());
const instanceID = '*';
@@ -1901,12 +1961,14 @@ const customThresholdNonCountCriterion: CustomMetricExpressionParams = {
threshold: [0],
};
+const mockedCountFilter = 'mockedCountFilter';
const customThresholdCountCriterion: CustomMetricExpressionParams = {
comparator: Comparator.GT,
metrics: [
{
aggType: Aggregators.COUNT,
name: 'A',
+ filter: mockedCountFilter,
},
],
timeSize: 1,
diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts
index 2aa9ee09a073db..1be464f0fbbc39 100644
--- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts
+++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts
@@ -295,13 +295,13 @@ export const createCustomThresholdExecutor = ({
}
return formatAlertResult(evaluation).currentValue;
}),
- viewInAppUrl: getViewInAppUrl(
- alertResults.length === 1 ? alertResults[0][group].metrics : [],
- indexedStartedAt,
+ viewInAppUrl: getViewInAppUrl({
+ dataViewId: params.searchConfiguration?.index?.title ?? dataViewId,
+ filter: params.searchConfiguration.query.query,
logExplorerLocator,
- params.searchConfiguration.query.query,
- params.searchConfiguration?.index?.title ?? dataViewId
- ),
+ metrics: alertResults.length === 1 ? alertResults[0][group].metrics : [],
+ startedAt: indexedStartedAt,
+ }),
...additionalContext,
});
}
@@ -321,6 +321,7 @@ export const createCustomThresholdExecutor = ({
const alertUuid = getAlertUuid(recoveredAlertId);
const timestamp = startedAt.toISOString();
const indexedStartedAt = getAlertStartedDate(recoveredAlertId) ?? timestamp;
+ const group = groupByKeysObjectForRecovered[recoveredAlertId];
const alertHits = alertUuid ? await getAlertByAlertUuid(alertUuid) : undefined;
const additionalContext = getContextForRecoveredAlerts(alertHits);
@@ -333,8 +334,15 @@ export const createCustomThresholdExecutor = ({
alertsLocator,
basePath.publicBaseUrl
),
- group: groupByKeysObjectForRecovered[recoveredAlertId],
+ group,
timestamp: startedAt.toISOString(),
+ viewInAppUrl: getViewInAppUrl({
+ dataViewId: params.searchConfiguration?.index?.title ?? dataViewId,
+ filter: params.searchConfiguration.query.query,
+ logExplorerLocator,
+ metrics: params.criteria[0]?.metrics,
+ startedAt: indexedStartedAt,
+ }),
...additionalContext,
});
}
diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts
index 791ddc8108312f..b5be8fb9f622ec 100644
--- a/x-pack/plugins/observability/server/plugin.ts
+++ b/x-pack/plugins/observability/server/plugin.ts
@@ -28,6 +28,7 @@ import { i18n } from '@kbn/i18n';
import {
ApmRuleType,
ES_QUERY_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
} from '@kbn/rule-data-utils';
@@ -89,6 +90,7 @@ const o11yRuleTypes = [
SLO_BURN_RATE_RULE_TYPE_ID,
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
ES_QUERY_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
...Object.values(ApmRuleType),
];
diff --git a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts
index 8c7d433f48a4da..3c1e123b494526 100644
--- a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts
+++ b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts
@@ -13,6 +13,7 @@ describe('[Observability onboarding] Landing page', () => {
describe('Entry point', () => {
it('when clicking on the logs card the user is navigated to the observability onboarding page', () => {
cy.getByTestSubj('guideButtonRedirect').click();
+ cy.getByTestSubj('guide-filter-observability').click();
cy.getByTestSubj('onboarding--observability--logs').click();
cy.url().should('include', '/app/observabilityOnboarding');
diff --git a/x-pack/plugins/observability_onboarding/public/application/app.tsx b/x-pack/plugins/observability_onboarding/public/application/app.tsx
index 6142f7acd45cb3..7a6143c9954cb1 100644
--- a/x-pack/plugins/observability_onboarding/public/application/app.tsx
+++ b/x-pack/plugins/observability_onboarding/public/application/app.tsx
@@ -16,7 +16,7 @@ import { i18n } from '@kbn/i18n';
import {
KibanaContextProvider,
KibanaThemeProvider,
- useUiSetting$,
+ useDarkMode,
} from '@kbn/kibana-react-plugin/public';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { HeaderMenuPortal } from '@kbn/observability-shared-plugin/public';
@@ -112,8 +112,7 @@ function App() {
}
function ObservabilityOnboardingApp() {
- const [darkMode] = useUiSetting$('theme:darkMode');
-
+ const darkMode = useDarkMode(false);
return (
({
diff --git a/x-pack/plugins/security/server/routes/api_keys/has_active.test.ts b/x-pack/plugins/security/server/routes/api_keys/has_active.test.ts
new file mode 100644
index 00000000000000..d2c38651f4397a
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/api_keys/has_active.test.ts
@@ -0,0 +1,113 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { kibanaResponseFactory } from '@kbn/core/server';
+import type { RequestHandler } from '@kbn/core/server';
+import type { CustomRequestHandlerMock, ScopedClusterClientMock } from '@kbn/core/server/mocks';
+import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
+import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
+import type { DeeplyMockedKeys } from '@kbn/utility-types-jest';
+
+import { defineHasApiKeysRoutes } from './has_active';
+import type { InternalAuthenticationServiceStart } from '../../authentication';
+import { authenticationServiceMock } from '../../authentication/authentication_service.mock';
+import { routeDefinitionParamsMock } from '../index.mock';
+
+describe('Has API Keys route', () => {
+ let routeHandler: RequestHandler;
+ let authc: DeeplyMockedKeys;
+ let esClientMock: ScopedClusterClientMock;
+ let mockContext: CustomRequestHandlerMock;
+
+ beforeEach(async () => {
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ authc = authenticationServiceMock.createStart();
+ mockRouteDefinitionParams.getAuthenticationService.mockReturnValue(authc);
+ defineHasApiKeysRoutes(mockRouteDefinitionParams);
+ [[, routeHandler]] = mockRouteDefinitionParams.router.get.mock.calls;
+ mockContext = coreMock.createCustomRequestHandlerContext({
+ core: coreMock.createRequestHandlerContext(),
+ licensing: licensingMock.createRequestHandlerContext(),
+ });
+
+ esClientMock = (await mockContext.core).elasticsearch.client;
+
+ authc.apiKeys.areAPIKeysEnabled.mockResolvedValue(true);
+ authc.apiKeys.areCrossClusterAPIKeysEnabled.mockResolvedValue(true);
+
+ esClientMock.asCurrentUser.security.getApiKey.mockResponse({
+ api_keys: [
+ { id: '123', invalidated: false },
+ { id: '456', invalidated: true },
+ ],
+ } as any);
+ });
+
+ it('should calculate when user has API keys', async () => {
+ const response = await routeHandler(
+ mockContext,
+ httpServerMock.createKibanaRequest(),
+ kibanaResponseFactory
+ );
+
+ expect(response.payload).toEqual(
+ expect.objectContaining({
+ hasApiKeys: true,
+ })
+ );
+ expect(esClientMock.asCurrentUser.security.getApiKey).toHaveBeenCalledTimes(1);
+ expect(esClientMock.asCurrentUser.security.getApiKey).toHaveBeenCalledWith({
+ owner: true,
+ active_only: true,
+ });
+ });
+
+ it('should calculate when user does not have API keys', async () => {
+ esClientMock.asCurrentUser.security.getApiKey.mockResponse({
+ api_keys: [],
+ });
+
+ const response = await routeHandler(
+ mockContext,
+ httpServerMock.createKibanaRequest(),
+ kibanaResponseFactory
+ );
+
+ expect(response.payload).toEqual(
+ expect.objectContaining({
+ hasApiKeys: false,
+ })
+ );
+ });
+
+ it('should filter out invalidated API keys', async () => {
+ const response = await routeHandler(
+ mockContext,
+ httpServerMock.createKibanaRequest(),
+ kibanaResponseFactory
+ );
+
+ expect(response.status).toBe(200);
+ expect(response.payload?.hasApiKeys).toBe(true);
+ });
+
+ it('should return `404` if API keys are disabled', async () => {
+ authc.apiKeys.areAPIKeysEnabled.mockResolvedValue(false);
+
+ const response = await routeHandler(
+ mockContext,
+ httpServerMock.createKibanaRequest(),
+ kibanaResponseFactory
+ );
+
+ expect(response.status).toBe(404);
+ expect(response.payload).toEqual({
+ message:
+ "API keys are disabled in Elasticsearch. To use API keys enable 'xpack.security.authc.api_key.enabled' setting.",
+ });
+ });
+});
diff --git a/x-pack/plugins/security/server/routes/api_keys/has_active.ts b/x-pack/plugins/security/server/routes/api_keys/has_active.ts
new file mode 100644
index 00000000000000..eea5ac71e53a07
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/api_keys/has_active.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { RouteDefinitionParams } from '..';
+import { createLicensedRouteHandler } from '../licensed_route_handler';
+
+/**
+ * Response of Kibana Has API keys endpoint.
+ */
+export interface HasAPIKeysResult {
+ hasApiKeys: boolean;
+}
+
+export function defineHasApiKeysRoutes({
+ router,
+ getAuthenticationService,
+}: RouteDefinitionParams) {
+ router.get(
+ {
+ path: '/internal/security/api_key/_has_active',
+ validate: false,
+ options: {
+ access: 'internal',
+ },
+ },
+ createLicensedRouteHandler(async (context, _request, response) => {
+ const esClient = (await context.core).elasticsearch.client;
+ const authenticationService = getAuthenticationService();
+
+ const areApiKeysEnabled = await authenticationService.apiKeys.areAPIKeysEnabled();
+
+ if (!areApiKeysEnabled) {
+ return response.notFound({
+ body: {
+ message:
+ "API keys are disabled in Elasticsearch. To use API keys enable 'xpack.security.authc.api_key.enabled' setting.",
+ },
+ });
+ }
+
+ const { api_keys: apiKeys } = await esClient.asCurrentUser.security.getApiKey({
+ owner: true,
+ // @ts-expect-error @elastic/elasticsearch SecurityGetApiKeyRequest.active_only: boolean | undefined
+ active_only: true,
+ });
+
+ // simply return true if the result array is non-empty
+ return response.ok({
+ body: {
+ hasApiKeys: apiKeys.length > 0,
+ },
+ });
+ })
+ );
+}
diff --git a/x-pack/plugins/security/server/routes/api_keys/index.ts b/x-pack/plugins/security/server/routes/api_keys/index.ts
index 9855d94923c33b..3b71eb7e1104d7 100644
--- a/x-pack/plugins/security/server/routes/api_keys/index.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/index.ts
@@ -8,6 +8,7 @@
import { defineCreateApiKeyRoutes } from './create';
import { defineEnabledApiKeysRoutes } from './enabled';
import { defineGetApiKeysRoutes } from './get';
+import { defineHasApiKeysRoutes } from './has_active';
import { defineInvalidateApiKeysRoutes } from './invalidate';
import { defineUpdateApiKeyRoutes } from './update';
import type { RouteDefinitionParams } from '..';
@@ -24,6 +25,7 @@ export type { GetAPIKeysResult } from './get';
export function defineApiKeysRoutes(params: RouteDefinitionParams) {
defineEnabledApiKeysRoutes(params);
defineGetApiKeysRoutes(params);
+ defineHasApiKeysRoutes(params);
defineCreateApiKeyRoutes(params);
defineUpdateApiKeyRoutes(params);
defineInvalidateApiKeysRoutes(params);
diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts
new file mode 100644
index 00000000000000..594dc685097db6
--- /dev/null
+++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { AlertWithCommonFields800 } from '@kbn/rule-registry-plugin/common/schemas/8.0.0';
+import type {
+ ALERT_HOST_CRITICALITY,
+ ALERT_USER_CRITICALITY,
+} from '../../../../../field_maps/field_names';
+import type {
+ Ancestor8120,
+ BaseFields8120,
+ EqlBuildingBlockFields8120,
+ EqlShellFields8120,
+ NewTermsFields8120,
+} from '../8.12.0';
+
+/* DO NOT MODIFY THIS SCHEMA TO ADD NEW FIELDS. These types represent the alerts that shipped in 8.13.0.
+Any changes to these types should be bug fixes so the types more accurately represent the alerts from 8.13.0.
+If you are adding new fields for a new release of Kibana, create a new sibling folder to this one
+for the version to be released and add the field(s) to the schema in that folder.
+Then, update `../index.ts` to import from the new folder that has the latest schemas, add the
+new schemas to the union of all alert schemas, and re-export the new schemas as the `*Latest` schemas.
+*/
+
+export type { Ancestor8120 as Ancestor8130 };
+
+export interface BaseFields8130 extends BaseFields8120 {
+ [ALERT_HOST_CRITICALITY]: string | undefined;
+ [ALERT_USER_CRITICALITY]: string | undefined;
+}
+
+export interface WrappedFields8130 {
+ _id: string;
+ _index: string;
+ _source: T;
+}
+
+export type GenericAlert8130 = AlertWithCommonFields800;
+
+export type EqlShellFields8130 = EqlShellFields8120 & BaseFields8130;
+
+export type EqlBuildingBlockFields8130 = EqlBuildingBlockFields8120 & BaseFields8130;
+
+export type NewTermsFields8130 = NewTermsFields8120 & BaseFields8130;
+
+export type NewTermsAlert8130 = NewTermsFields8120 & BaseFields8130;
+
+export type EqlBuildingBlockAlert8130 = AlertWithCommonFields800;
+
+export type EqlShellAlert8130 = AlertWithCommonFields800;
+
+export type DetectionAlert8130 =
+ | GenericAlert8130
+ | EqlShellAlert8130
+ | EqlBuildingBlockAlert8130
+ | NewTermsAlert8130;
diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts
index 742e5fd4ecfc1c..6bf7b1d5dfd7e0 100644
--- a/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts
+++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts
@@ -12,15 +12,16 @@ import type { DetectionAlert860 } from './8.6.0';
import type { DetectionAlert870 } from './8.7.0';
import type { DetectionAlert880 } from './8.8.0';
import type { DetectionAlert890 } from './8.9.0';
+import type { DetectionAlert8120 } from './8.12.0';
import type {
- Ancestor8120,
- BaseFields8120,
- DetectionAlert8120,
- EqlBuildingBlockFields8120,
- EqlShellFields8120,
- NewTermsFields8120,
- WrappedFields8120,
-} from './8.12.0';
+ Ancestor8130,
+ BaseFields8130,
+ DetectionAlert8130,
+ EqlBuildingBlockFields8130,
+ EqlShellFields8130,
+ NewTermsFields8130,
+ WrappedFields8130,
+} from './8.13.0';
// When new Alert schemas are created for new Kibana versions, add the DetectionAlert type from the new version
// here, e.g. `export type DetectionAlert = DetectionAlert800 | DetectionAlert820` if a new schema is created in 8.2.0
@@ -31,14 +32,15 @@ export type DetectionAlert =
| DetectionAlert870
| DetectionAlert880
| DetectionAlert890
- | DetectionAlert8120;
+ | DetectionAlert8120
+ | DetectionAlert8130;
export type {
- Ancestor8120 as AncestorLatest,
- BaseFields8120 as BaseFieldsLatest,
- DetectionAlert8120 as DetectionAlertLatest,
- WrappedFields8120 as WrappedFieldsLatest,
- EqlBuildingBlockFields8120 as EqlBuildingBlockFieldsLatest,
- EqlShellFields8120 as EqlShellFieldsLatest,
- NewTermsFields8120 as NewTermsFieldsLatest,
+ Ancestor8130 as AncestorLatest,
+ BaseFields8130 as BaseFieldsLatest,
+ DetectionAlert8130 as DetectionAlertLatest,
+ WrappedFields8130 as WrappedFieldsLatest,
+ EqlBuildingBlockFields8130 as EqlBuildingBlockFieldsLatest,
+ EqlShellFields8130 as EqlShellFieldsLatest,
+ NewTermsFields8130 as NewTermsFieldsLatest,
};
diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts
index 9adc5f7289dc5a..755361902eaa1d 100644
--- a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts
+++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/base.ts
@@ -7,6 +7,7 @@
import type { TypeOf } from '@kbn/config-schema';
import { schema } from '@kbn/config-schema';
+import { RESPONSE_ACTION_AGENT_TYPE } from '../../../../endpoint/service/response_actions/constants';
export const BaseActionRequestSchema = {
/** A list of endpoint IDs whose hosts will be isolated (Fleet Agent IDs will be retrieved for these) */
@@ -42,6 +43,13 @@ export const BaseActionRequestSchema = {
),
comment: schema.maybe(schema.string()),
parameters: schema.maybe(schema.object({})),
+ agent_type: schema.maybe(
+ schema.oneOf(
+ // @ts-expect-error TS2769: No overload matches this call
+ RESPONSE_ACTION_AGENT_TYPE.map((agentType) => schema.literal(agentType)),
+ { defaultValue: 'endpoint' }
+ )
+ ),
};
export const NoParametersRequestSchema = {
diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts
index 558d0a2fa6a503..d122daf52627b4 100644
--- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_endpoint_policy.ts
@@ -22,17 +22,23 @@ import {
API_VERSIONS,
} from '@kbn/fleet-plugin/common';
import { memoize } from 'lodash';
+import type { ToolingLog } from '@kbn/tooling-log';
+import { catchAxiosErrorFormatAndThrow } from '../format_axios_error';
import { usageTracker } from './usage_tracker';
import { getEndpointPackageInfo } from '../utils/package';
import type { PolicyData } from '../types';
import { policyFactory as policyConfigFactory } from '../models/policy_config';
-import { wrapErrorAndRejectPromise } from './utils';
+import { RETRYABLE_TRANSIENT_ERRORS, retryOnError, wrapErrorAndRejectPromise } from './utils';
export interface IndexedFleetEndpointPolicyResponse {
integrationPolicies: PolicyData[];
agentPolicies: AgentPolicy[];
}
+enum TimeoutsInMS {
+ TEN_SECONDS = 10 * 1000,
+ FIVE_MINUTES = 5 * 60 * 1000,
+}
/**
* Create an endpoint Integration Policy (and associated Agent Policy) via Fleet
* (NOTE: ensure that fleet is setup first before calling this loading function)
@@ -43,7 +49,8 @@ export const indexFleetEndpointPolicy = usageTracker.track(
kbnClient: KbnClient,
policyName: string,
endpointPackageVersion?: string,
- agentPolicyName?: string
+ agentPolicyName?: string,
+ log?: ToolingLog
): Promise => {
const response: IndexedFleetEndpointPolicyResponse = {
integrationPolicies: [],
@@ -84,6 +91,7 @@ export const indexFleetEndpointPolicy = usageTracker.track(
// Create integration (package) policy
const newPackagePolicyData: CreatePackagePolicyRequest['body'] = {
name: policyName,
+ // skip_ensure_installed: true,
description: 'Protect the worlds data',
policy_id: agentPolicy.data.item.id,
enabled: true,
@@ -106,18 +114,48 @@ export const indexFleetEndpointPolicy = usageTracker.track(
version: packageVersion,
},
};
- const packagePolicy = (await kbnClient
- .request({
- path: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN,
- method: 'POST',
- body: newPackagePolicyData,
- headers: {
- 'elastic-api-version': API_VERSIONS.public.v1,
- },
- })
- .catch(wrapErrorAndRejectPromise)) as AxiosResponse;
- response.integrationPolicies.push(packagePolicy.data.item as PolicyData);
+ const createPackagePolicy = async (): Promise =>
+ kbnClient
+ .request({
+ path: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN,
+ method: 'POST',
+ body: newPackagePolicyData,
+ headers: {
+ 'elastic-api-version': API_VERSIONS.public.v1,
+ },
+ })
+ .catch(catchAxiosErrorFormatAndThrow)
+ .then((res) => res.data);
+
+ const started = new Date();
+ const hasTimedOut = (): boolean => {
+ const elapsedTime = Date.now() - started.getTime();
+ return elapsedTime > TimeoutsInMS.FIVE_MINUTES;
+ };
+
+ let packagePolicy: CreatePackagePolicyResponse | undefined;
+ log?.debug(`Creating integration policy with name: ${policyName}`);
+
+ while (!packagePolicy && !hasTimedOut()) {
+ packagePolicy = await retryOnError(
+ async () => createPackagePolicy(),
+ [...RETRYABLE_TRANSIENT_ERRORS, 'resource_not_found_exception'],
+ log
+ );
+
+ if (!packagePolicy) {
+ await new Promise((resolve) => setTimeout(resolve, TimeoutsInMS.TEN_SECONDS));
+ }
+ }
+
+ if (!packagePolicy) {
+ throw new Error(`Create package policy failed`);
+ }
+
+ log?.verbose(`Integration policy created:`, JSON.stringify(packagePolicy, null, 2));
+
+ response.integrationPolicies.push(packagePolicy.item as PolicyData);
return response;
}
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/format_axios_error.ts b/x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts
similarity index 100%
rename from x-pack/plugins/security_solution/scripts/endpoint/common/format_axios_error.ts
rename to x-pack/plugins/security_solution/common/endpoint/format_axios_error.ts
diff --git a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts
index 14d1272add26ba..67a29b7a185a21 100644
--- a/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/service/response_actions/constants.ts
@@ -11,6 +11,10 @@ export type ResponseActionStatus = typeof RESPONSE_ACTION_STATUS[number];
export const RESPONSE_ACTION_TYPE = ['automated', 'manual'] as const;
export type ResponseActionType = typeof RESPONSE_ACTION_TYPE[number];
+
+export const RESPONSE_ACTION_AGENT_TYPE = ['endpoint', 'sentinel_one'] as const;
+export type ResponseActionAgentType = typeof RESPONSE_ACTION_AGENT_TYPE[number];
+
/**
* The Command names that are used in the API payload for the `{ command: '' }` attribute
*/
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts
index 98fb9b0f16b0c1..f2393c02c3b41d 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts
@@ -17,6 +17,7 @@ import type {
import type {
ResponseActionStatus,
ResponseActionsApiCommandNames,
+ ResponseActionAgentType,
} from '../service/response_actions/constants';
export type ISOLATION_ACTIONS = 'isolate' | 'unisolate';
@@ -104,7 +105,7 @@ interface EndpointActionFields<
interface ActionRequestFields {
expiration: string;
type: 'INPUT_ACTION';
- input_type: 'endpoint';
+ input_type: ResponseActionAgentType;
}
interface ActionResponseFields {
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/utility_types.ts b/x-pack/plugins/security_solution/common/endpoint/types/utility_types.ts
index 92880d93221914..a70c8e124eb842 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/utility_types.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/utility_types.ts
@@ -10,3 +10,10 @@
export type PromiseResolvedValue> = T extends Promise
? Value
: never;
+
+/**
+ * Deeply convert a immutable type (those with `readonly` properties) to a mutable type
+ */
+export type DeepMutable = T extends Record
+ ? { -readonly [K in keyof T]: DeepMutable }
+ : T;
diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts
index 8c16307ab5b8c5..805192aed8a9f0 100644
--- a/x-pack/plugins/security_solution/common/experimental_features.ts
+++ b/x-pack/plugins/security_solution/common/experimental_features.ts
@@ -71,6 +71,11 @@ export const allowedExperimentalValues = Object.freeze({
*/
responseActionUploadEnabled: true,
+ /**
+ * Enables the ability to send Response actions to SentinelOne
+ */
+ responseActionsSentinelOneV1Enabled: false,
+
/**
* Enables top charts on Alerts Page
*/
@@ -94,11 +99,6 @@ export const allowedExperimentalValues = Object.freeze({
*/
assistantModelEvaluation: false,
- /**
- * Enables Retrieval Augmented Generation (RAG) on Alerts in the assistant
- */
- assistantRagOnAlerts: false,
-
/*
* Enables the new user details flyout displayed on the Alerts page and timeline.
*
@@ -125,11 +125,6 @@ export const allowedExperimentalValues = Object.freeze({
*/
protectionUpdatesEnabled: true,
- /**
- * Enables alerts suppression for threshold rules
- */
- alertSuppressionForThresholdRuleEnabled: false,
-
/**
* Disables the timeline save tour.
* This flag is used to disable the tour in cypress tests.
diff --git a/x-pack/plugins/security_solution/common/field_maps/8.13.0/alerts.ts b/x-pack/plugins/security_solution/common/field_maps/8.13.0/alerts.ts
new file mode 100644
index 00000000000000..86c84092891b80
--- /dev/null
+++ b/x-pack/plugins/security_solution/common/field_maps/8.13.0/alerts.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { alertsFieldMap840 } from '../8.4.0';
+import { ALERT_HOST_CRITICALITY, ALERT_USER_CRITICALITY } from '../field_names';
+
+export const alertsFieldMap8130 = {
+ ...alertsFieldMap840,
+ /**
+ * Stores the criticality level for the host, as determined by analysts, in relation to the alert.
+ * The Criticality level is copied from the asset criticality index.
+ */
+ [ALERT_HOST_CRITICALITY]: {
+ type: 'keyword',
+ array: false,
+ required: false,
+ },
+ /**
+ * Stores the criticality level for the user, as determined by analysts, in relation to the alert.
+ * The Criticality level is copied from the asset criticality index.
+ */
+ [ALERT_USER_CRITICALITY]: {
+ type: 'keyword',
+ array: false,
+ required: false,
+ },
+} as const;
+
+export type AlertsFieldMap8130 = typeof alertsFieldMap8130;
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/translations.ts b/x-pack/plugins/security_solution/common/field_maps/8.13.0/index.ts
similarity index 54%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/translations.ts
rename to x-pack/plugins/security_solution/common/field_maps/8.13.0/index.ts
index 83b402220f3d45..291ca7f8dff822 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/translations.ts
+++ b/x-pack/plugins/security_solution/common/field_maps/8.13.0/index.ts
@@ -5,11 +5,7 @@
* 2.0.
*/
-import { i18n } from '@kbn/i18n';
-
-export const FAIL_RISK_SCORE = i18n.translate(
- 'xpack.securitySolution.riskScore.kpi.failSearchDescription',
- {
- defaultMessage: `Failed to run search on risk score`,
- }
-);
+import type { AlertsFieldMap8130 } from './alerts';
+import { alertsFieldMap8130 } from './alerts';
+export type { AlertsFieldMap8130 };
+export { alertsFieldMap8130 };
diff --git a/x-pack/plugins/security_solution/common/field_maps/field_names.ts b/x-pack/plugins/security_solution/common/field_maps/field_names.ts
index 53ebfc5c188d10..6124cc08ebd2bb 100644
--- a/x-pack/plugins/security_solution/common/field_maps/field_names.ts
+++ b/x-pack/plugins/security_solution/common/field_maps/field_names.ts
@@ -17,6 +17,8 @@ export const ALERT_THRESHOLD_RESULT = `${ALERT_NAMESPACE}.threshold_result` as c
export const ALERT_THRESHOLD_RESULT_COUNT = `${ALERT_THRESHOLD_RESULT}.count` as const;
export const ALERT_NEW_TERMS = `${ALERT_NAMESPACE}.new_terms` as const;
export const ALERT_NEW_TERMS_FIELDS = `${ALERT_RULE_PARAMETERS}.new_terms_fields` as const;
+export const ALERT_HOST_CRITICALITY = `${ALERT_NAMESPACE}.host.criticality_level` as const;
+export const ALERT_USER_CRITICALITY = `${ALERT_NAMESPACE}.user.criticality_level` as const;
export const ALERT_ORIGINAL_EVENT = `${ALERT_NAMESPACE}.original_event` as const;
export const ALERT_ORIGINAL_EVENT_ACTION = `${ALERT_ORIGINAL_EVENT}.action` as const;
diff --git a/x-pack/plugins/security_solution/common/field_maps/index.ts b/x-pack/plugins/security_solution/common/field_maps/index.ts
index c6780a33fc64ff..fe903776d1dd43 100644
--- a/x-pack/plugins/security_solution/common/field_maps/index.ts
+++ b/x-pack/plugins/security_solution/common/field_maps/index.ts
@@ -5,9 +5,9 @@
* 2.0.
*/
-import type { AlertsFieldMap840 } from './8.4.0';
-import { alertsFieldMap840 } from './8.4.0';
+import type { AlertsFieldMap8130 } from './8.13.0';
+import { alertsFieldMap8130 } from './8.13.0';
import type { RulesFieldMap } from './8.0.0/rules';
import { rulesFieldMap } from './8.0.0/rules';
-export type { AlertsFieldMap840 as AlertsFieldMap, RulesFieldMap };
-export { alertsFieldMap840 as alertsFieldMap, rulesFieldMap };
+export type { AlertsFieldMap8130 as AlertsFieldMap, RulesFieldMap };
+export { alertsFieldMap8130 as alertsFieldMap, rulesFieldMap };
diff --git a/x-pack/plugins/security_solution/public/assistant/provider.tsx b/x-pack/plugins/security_solution/public/assistant/provider.tsx
index 7a17a98bc0d6e3..a9f9e14a8d3e0b 100644
--- a/x-pack/plugins/security_solution/public/assistant/provider.tsx
+++ b/x-pack/plugins/security_solution/public/assistant/provider.tsx
@@ -57,7 +57,6 @@ export const AssistantProvider: React.FC = ({ children }) => {
const { signalIndexName } = useSignalIndex();
const alertsIndexPattern = signalIndexName ?? undefined;
- const ragOnAlerts = useIsExperimentalFeatureEnabled('assistantRagOnAlerts');
const toasts = useAppToasts() as unknown as IToasts; // useAppToasts is the current, non-deprecated method of getting the toasts service in the Security Solution, but it doesn't return the IToasts interface (defined by core)
return (
@@ -82,7 +81,6 @@ export const AssistantProvider: React.FC = ({ children }) => {
assistantStreamingEnabled={assistantStreamingEnabled}
modelEvaluatorEnabled={isModelEvaluationEnabled}
nameSpace={nameSpace}
- ragOnAlerts={ragOnAlerts}
setConversations={setConversations}
setDefaultAllow={setDefaultAllow}
setDefaultAllowReplacement={setDefaultAllowReplacement}
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx
index 4f5e1f847ad83d..ce22e6e09b4336 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.tsx
@@ -8,6 +8,7 @@
import styled from 'styled-components';
import React from 'react';
import { EuiTitle, EuiHorizontalRule, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import type { HostRisk, UserRisk } from '../../../../entity_analytics/api/types';
import * as i18n from './translations';
import type { CtiEnrichment } from '../../../../../common/search_strategy/security_solution/cti';
@@ -16,9 +17,8 @@ import type {
TimelineEventsDetailsItem,
RiskSeverity,
} from '../../../../../common/search_strategy';
-import { RiskSummary } from './risk_summary';
+import { RiskSummary } from '../../../../entity_analytics/components/risk_summary';
import { EnrichmentSummary } from './enrichment_summary';
-import type { HostRisk, UserRisk } from '../../../../explore/containers/risk_score';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
import { useHasSecurityCapability } from '../../../../helper_hooks';
import { RiskScoreInfoTooltip } from '../../../../overview/components/common';
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/translations.ts
index 0ba5a464dc9d5a..973b438c866c34 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/translations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/translations.ts
@@ -6,9 +6,9 @@
*/
import { i18n } from '@kbn/i18n';
-import { getRiskEntityTranslation } from '../../../../explore/components/risk_score/translations';
+import { getRiskEntityTranslation } from '../../../../entity_analytics/components/risk_score/translations';
import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-export * from '../../../../explore/components/risk_score/translations';
+export * from '../../../../entity_analytics/components/risk_score/translations';
export const FEED_NAME_PREPOSITION = i18n.translate(
'xpack.securitySolution.eventDetails.ctiSummary.feedNamePreposition',
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
index 2273389e537331..6c1dd9bce910a0 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx
@@ -55,7 +55,7 @@ import { EnrichmentRangePicker } from './cti_details/enrichment_range_picker';
import { InvestigationGuideView } from './investigation_guide_view';
import { Overview } from './overview';
import { Insights } from './insights/insights';
-import { useRiskScoreData } from './use_risk_score_data';
+import { useRiskScoreData } from '../../../entity_analytics/api/hooks/use_risk_score_data';
import { getRowRenderer } from '../../../timelines/components/timeline/body/renderers/get_row_renderer';
import { DETAILS_CLASS_NAME } from '../../../timelines/components/timeline/body/renderers/helpers';
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
index d21e7a8087cbc2..88b03516e5aa82 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml/anomaly/use_anomalies_search.ts
@@ -8,12 +8,12 @@
import { useState, useEffect, useMemo } from 'react';
import { has, sortBy } from 'lodash/fp';
+import { getAggregatedAnomaliesQuery } from '../../../../entity_analytics/components/entity_analytics_anomalies/query';
import { DEFAULT_ANOMALY_SCORE } from '../../../../../common/constants';
import * as i18n from './translations';
import { useUiSetting$ } from '../../../lib/kibana';
import { useAppToasts } from '../../../hooks/use_app_toasts';
import { anomaliesSearch } from '../api/anomalies_search';
-import { getAggregatedAnomaliesQuery } from '../../../../overview/components/entity_analytics/anomalies/query';
import type { inputsModel } from '../../../store';
import { useSecurityJobs } from '../../ml_popover/hooks/use_security_jobs';
import type { SecurityJob } from '../../ml_popover/types';
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/visualization_embeddable.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/visualization_embeddable.test.tsx
index ee77776e60a91b..8d5ef1a3b6e84c 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/visualization_embeddable.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/visualization_embeddable.test.tsx
@@ -22,8 +22,7 @@ import {
import { createStore } from '../../store';
import type { State } from '../../store';
import { useRefetchByRestartingSession } from '../page/use_refetch_by_session';
-import { getRiskScoreDonutAttributes } from './lens_attributes/common/risk_scores/risk_score_donut';
-import { TOTAL_LABEL } from '../../../overview/components/entity_analytics/common/translations';
+import { getRiskScoreDonutAttributes } from '../../../entity_analytics/lens_attributes/risk_score_donut';
jest.mock('./lens_embeddable');
jest.mock('../page/use_refetch_by_session', () => ({
@@ -183,7 +182,7 @@ describe('VisualizationEmbeddable', () => {
getLensAttributes={getRiskScoreDonutAttributes}
id="testId"
isDonut={true}
- label={TOTAL_LABEL}
+ label={'Total'}
timerange={{ from: '2022-10-27T23:00:00.000Z', to: '2022-11-04T10:46:16.204Z' }}
/>
diff --git a/x-pack/plugins/security_solution/public/common/images/cloud_security_posture_dashboard_page.png b/x-pack/plugins/security_solution/public/common/images/cloud_security_posture_dashboard_page.png
index fe27c2ca951206..cc2b52693edffd 100644
Binary files a/x-pack/plugins/security_solution/public/common/images/cloud_security_posture_dashboard_page.png and b/x-pack/plugins/security_solution/public/common/images/cloud_security_posture_dashboard_page.png differ
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
index c2c85fca93f031..cb37caa661fdac 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
@@ -54,7 +54,6 @@ import { TechnicalPreviewBadge } from '../../../../detections/components/rules/t
import { BadgeList } from './badge_list';
import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants';
import * as i18n from './translations';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import type { ExperimentalFeatures } from '../../../../../common/experimental_features';
interface SavedQueryNameProps {
@@ -427,7 +426,7 @@ const prepareDefinitionSectionListItems = (
rule: Partial,
isInteractive: boolean,
savedQuery: SavedQuery | undefined,
- { alertSuppressionForThresholdRuleEnabled }: Partial
+ experimentalFeatures?: Partial
): EuiDescriptionListProps['listItems'] => {
const definitionSectionListItems: EuiDescriptionListProps['listItems'] = [];
@@ -669,16 +668,14 @@ const prepareDefinitionSectionListItems = (
});
}
- if (rule.type !== 'threshold' || alertSuppressionForThresholdRuleEnabled) {
- definitionSectionListItems.push({
- title: (
-
-
-
- ),
- description: ,
- });
- }
+ definitionSectionListItems.push({
+ title: (
+
+
+
+ ),
+ description: ,
+ });
if ('missing_fields_strategy' in rule.alert_suppression) {
definitionSectionListItems.push({
@@ -741,15 +738,10 @@ export const RuleDefinitionSection = ({
ruleType: rule.type,
});
- const alertSuppressionForThresholdRuleEnabled = useIsExperimentalFeatureEnabled(
- 'alertSuppressionForThresholdRuleEnabled'
- );
-
const definitionSectionListItems = prepareDefinitionSectionListItems(
rule,
isInteractive,
- savedQuery,
- { alertSuppressionForThresholdRuleEnabled }
+ savedQuery
);
return (
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx
index 2d6234f225b3ac..809fb97eb260c3 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx
@@ -83,7 +83,6 @@ import { useLicense } from '../../../../common/hooks/use_license';
import { AlertSuppressionMissingFieldsStrategyEnum } from '../../../../../common/api/detection_engine/model/rule_schema';
import { DurationInput } from '../duration_input';
import { MINIMUM_LICENSE_FOR_SUPPRESSION } from '../../../../../common/detection_engine/constants';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useUpsellingMessage } from '../../../../common/hooks/use_upselling';
const CommonUseField = getUseField({ component: Field });
@@ -182,9 +181,6 @@ const StepDefineRuleComponent: FC = ({
const esqlQueryRef = useRef(undefined);
- const isAlertSuppressionForThresholdRuleFeatureEnabled = useIsExperimentalFeatureEnabled(
- 'alertSuppressionForThresholdRuleEnabled'
- );
const isAlertSuppressionLicenseValid = license.isAtLeast(MINIMUM_LICENSE_FOR_SUPPRESSION);
const isThresholdRule = getIsThresholdRule(ruleType);
@@ -808,8 +804,7 @@ const StepDefineRuleComponent: FC = ({
[isUpdateView, mlCapabilities]
);
- const isAlertSuppressionEnabled =
- isQueryRule(ruleType) || (isThresholdRule && isAlertSuppressionForThresholdRuleFeatureEnabled);
+ const isAlertSuppressionEnabled = isQueryRule(ruleType) || isThresholdRule;
return (
<>
diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts
index bfce8420964486..384c6bf955e51f 100644
--- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts
+++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts
@@ -6,6 +6,10 @@
*/
import type { EuiDataGridColumn } from '@elastic/eui';
+import {
+ ALERT_HOST_CRITICALITY,
+ ALERT_USER_CRITICALITY,
+} from '../../../../common/field_maps/field_names';
import type { LicenseService } from '../../../../common/license';
import type { ColumnHeaderOptions } from '../../../../common/types';
@@ -72,6 +76,18 @@ const getBaseColumns = (
id: 'user.risk.calculated_level',
}
: null,
+ isPlatinumPlus
+ ? {
+ columnHeaderType: defaultColumnHeaderType,
+ id: ALERT_HOST_CRITICALITY,
+ }
+ : null,
+ isPlatinumPlus
+ ? {
+ columnHeaderType: defaultColumnHeaderType,
+ id: ALERT_USER_CRITICALITY,
+ }
+ : null,
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.name',
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts
index 73cb1cbd57ff09..9f2b7bb319f7d4 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import type { RiskScoreEntity } from '../../../common/search_strategy';
import {
RISK_ENGINE_STATUS_URL,
RISK_SCORE_PREVIEW_URL,
@@ -13,6 +14,7 @@ import {
RISK_ENGINE_INIT_URL,
RISK_ENGINE_PRIVILEGES_URL,
ASSET_CRITICALITY_PRIVILEGES_URL,
+ RISK_SCORE_INDEX_STATUS_API_URL,
} from '../../../common/constants';
import type {
@@ -101,6 +103,27 @@ export const useEntityAnalyticsRoutes = () => {
method: 'GET',
});
+ const getRiskScoreIndexStatus = ({
+ query,
+ signal,
+ }: {
+ query: {
+ indexName: string;
+ entity: RiskScoreEntity;
+ };
+ signal?: AbortSignal;
+ }): Promise<{
+ isDeprecated: boolean;
+ isEnabled: boolean;
+ }> =>
+ http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(RISK_SCORE_INDEX_STATUS_API_URL, {
+ version: '1',
+ method: 'GET',
+ query,
+ asSystemRequest: true,
+ signal,
+ });
+
return {
fetchRiskScorePreview,
fetchRiskEngineStatus,
@@ -109,5 +132,6 @@ export const useEntityAnalyticsRoutes = () => {
disableRiskEngine,
fetchRiskEnginePrivileges,
fetchAssetCriticalityPrivileges,
+ getRiskScoreIndexStatus,
};
};
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.test.tsx
similarity index 88%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.test.tsx
index dfbba68e25c97b..8f61796f0861b7 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.test.tsx
@@ -5,25 +5,25 @@
* 2.0.
*/
import { renderHook } from '@testing-library/react-hooks';
-import { useRiskScore } from '.';
-import { TestProviders } from '../../../../common/mock';
+import { useRiskScore } from './use_risk_score';
+import { TestProviders } from '../../../common/mock';
-import { useSearchStrategy } from '../../../../common/containers/use_search_strategy';
-import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
-import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock';
-import { useRiskScoreFeatureStatus } from '../feature_status';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
+import { useSearchStrategy } from '../../../common/containers/use_search_strategy';
+import { useAppToasts } from '../../../common/hooks/use_app_toasts';
+import { useAppToastsMock } from '../../../common/hooks/use_app_toasts.mock';
+import { useRiskScoreFeatureStatus } from './use_risk_score_feature_status';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
-jest.mock('../../../../common/containers/use_search_strategy', () => ({
+jest.mock('../../../common/containers/use_search_strategy', () => ({
useSearchStrategy: jest.fn(),
}));
-jest.mock('../../../../common/hooks/use_space_id', () => ({
+jest.mock('../../../common/hooks/use_space_id', () => ({
useSpaceId: jest.fn().mockReturnValue('default'),
}));
-jest.mock('../../../../common/hooks/use_app_toasts');
-jest.mock('../feature_status');
+jest.mock('../../../common/hooks/use_app_toasts');
+jest.mock('./use_risk_score_feature_status');
const mockUseRiskScoreFeatureStatus = useRiskScoreFeatureStatus as jest.Mock;
const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.tsx
similarity index 81%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.tsx
index b2be80f74ab713..3038e33d4bfab3 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score.tsx
@@ -7,28 +7,25 @@
import { useCallback, useEffect, useMemo } from 'react';
-import { useRiskScoreFeatureStatus } from '../feature_status';
-import { createFilter } from '../../../../common/containers/helpers';
-import type {
- RiskScoreSortField,
- StrategyResponseType,
-} from '../../../../../common/search_strategy';
+import { i18n } from '@kbn/i18n';
+import { useRiskScoreFeatureStatus } from './use_risk_score_feature_status';
+import { createFilter } from '../../../common/containers/helpers';
+import type { RiskScoreSortField, StrategyResponseType } from '../../../../common/search_strategy';
import {
RiskQueries,
getUserRiskIndex,
RiskScoreEntity,
getHostRiskIndex,
-} from '../../../../../common/search_strategy';
-import type { ESQuery } from '../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import type { InspectResponse } from '../../../../types';
-import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
-import { isIndexNotFoundError } from '../../../../common/utils/exceptions';
-import type { inputsModel } from '../../../../common/store';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { useSearchStrategy } from '../../../../common/containers/use_search_strategy';
-import { useIsNewRiskScoreModuleInstalled } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
+} from '../../../../common/search_strategy';
+import type { ESQuery } from '../../../../common/typed_json';
+
+import type { InspectResponse } from '../../../types';
+import { useAppToasts } from '../../../common/hooks/use_app_toasts';
+import { isIndexNotFoundError } from '../../../common/utils/exceptions';
+import type { inputsModel } from '../../../common/store';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { useSearchStrategy } from '../../../common/containers/use_search_strategy';
+import { useIsNewRiskScoreModuleInstalled } from './use_risk_engine_status';
export interface RiskScoreState {
data:
@@ -181,7 +178,11 @@ export const useRiskScore = {
if (error) {
if (!isIndexNotFoundError(error)) {
- addError(error, { title: i18n.FAIL_RISK_SCORE });
+ addError(error, {
+ title: i18n.translate('xpack.securitySolution.riskScore.failSearchDescription', {
+ defaultMessage: `Failed to run search on risk score`,
+ }),
+ });
}
}
}, [addError, error]);
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.test.ts
similarity index 94%
rename from x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.test.ts
index 656c13b22d8560..123d4662768c19 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.test.ts
@@ -6,13 +6,13 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { TestProviders } from '../../mock';
+import { TestProviders } from '../../../common/mock';
import { ONLY_FIRST_ITEM_PAGINATION, useRiskScoreData } from './use_risk_score_data';
-import { useRiskScore } from '../../../explore/containers/risk_score';
import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useRiskScore } from './use_risk_score';
-jest.mock('../../../explore/containers/risk_score');
+jest.mock('./use_risk_score');
jest.mock('../../../timelines/components/side_panel/event_details/helpers');
const mockUseRiskScore = useRiskScore as jest.Mock;
const mockUseBasicDataFromDetailsData = useBasicDataFromDetailsData as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.ts
similarity index 93%
rename from x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.ts
index 8ac02b1ce47e43..545d12d0851f7d 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/use_risk_score_data.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_data.ts
@@ -12,8 +12,8 @@ import {
buildUserNamesFilter,
RiskScoreEntity,
} from '../../../../common/search_strategy';
-import type { HostRisk, UserRisk } from '../../../explore/containers/risk_score';
-import { useRiskScore } from '../../../explore/containers/risk_score';
+import { useRiskScore } from './use_risk_score';
+import type { HostRisk, UserRisk } from '../types';
export const ONLY_FIRST_ITEM_PAGINATION = {
cursorStart: 0,
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.test.ts
similarity index 85%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.test.ts
index 9464ecb9d8380b..30719d1559f54d 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.test.ts
@@ -5,17 +5,17 @@
* 2.0.
*/
import { act, renderHook } from '@testing-library/react-hooks';
-import { TestProviders } from '../../../../common/mock';
+import { TestProviders } from '../../../common/mock';
-import { useRiskScoreFeatureStatus } from '.';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useFetch } from '../../../../common/hooks/use_fetch';
-import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { useHasSecurityCapability } from '../../../../helper_hooks';
+import { useRiskScoreFeatureStatus } from './use_risk_score_feature_status';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useFetch } from '../../../common/hooks/use_fetch';
+import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
+import { useHasSecurityCapability } from '../../../helper_hooks';
-jest.mock('../../../../common/hooks/use_fetch');
-jest.mock('../../../../common/components/ml/hooks/use_ml_capabilities');
-jest.mock('../../../../helper_hooks');
+jest.mock('../../../common/hooks/use_fetch');
+jest.mock('../../../common/components/ml/hooks/use_ml_capabilities');
+jest.mock('../../../helper_hooks');
const mockFetch = jest.fn();
const mockUseMlCapabilities = useMlCapabilities as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.ts
similarity index 83%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.ts
index 97994f58295c16..03fb48f56559a8 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/index.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_feature_status.ts
@@ -6,11 +6,11 @@
*/
import { useCallback, useEffect, useMemo } from 'react';
-import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { REQUEST_NAMES, useFetch } from '../../../../common/hooks/use_fetch';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { getRiskScoreIndexStatus } from './api';
-import { useHasSecurityCapability } from '../../../../helper_hooks';
+import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
+import { REQUEST_NAMES, useFetch } from '../../../common/hooks/use_fetch';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useHasSecurityCapability } from '../../../helper_hooks';
+import { useEntityAnalyticsRoutes } from '../api';
interface RiskScoresFeatureStatus {
error: unknown;
@@ -31,6 +31,7 @@ export const useRiskScoreFeatureStatus = (
const { isPlatinumOrTrialLicense, capabilitiesFetched } = useMlCapabilities();
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
const isAuthorized = isPlatinumOrTrialLicense && hasEntityAnalyticsCapability;
+ const { getRiskScoreIndexStatus } = useEntityAnalyticsRoutes();
const { fetch, data, isLoading, error } = useFetch(
REQUEST_NAMES.GET_RISK_SCORE_DEPRECATED,
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx
similarity index 78%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx
index 2197d5553c4319..4105dfc28d83d6 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/kpi/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx
@@ -7,6 +7,7 @@
import { useCallback, useEffect, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
import {
getHostRiskIndex,
getUserRiskIndex,
@@ -14,18 +15,17 @@ import {
RiskSeverity,
RiskScoreEntity,
EMPTY_SEVERITY_COUNT,
-} from '../../../../../common/search_strategy';
-import * as i18n from './translations';
-import { isIndexNotFoundError } from '../../../../common/utils/exceptions';
-import type { ESQuery } from '../../../../../common/typed_json';
-import type { SeverityCount } from '../../../components/risk_score/severity/types';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { useSearchStrategy } from '../../../../common/containers/use_search_strategy';
-import type { InspectResponse } from '../../../../types';
-import type { inputsModel } from '../../../../common/store';
-import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
-import { useIsNewRiskScoreModuleInstalled } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
-import { useRiskScoreFeatureStatus } from '../feature_status';
+} from '../../../../common/search_strategy';
+import { isIndexNotFoundError } from '../../../common/utils/exceptions';
+import type { ESQuery } from '../../../../common/typed_json';
+import type { SeverityCount } from '../../components/severity/types';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { useSearchStrategy } from '../../../common/containers/use_search_strategy';
+import type { InspectResponse } from '../../../types';
+import type { inputsModel } from '../../../common/store';
+import { useAppToasts } from '../../../common/hooks/use_app_toasts';
+import { useIsNewRiskScoreModuleInstalled } from './use_risk_engine_status';
+import { useRiskScoreFeatureStatus } from './use_risk_score_feature_status';
interface RiskScoreKpi {
error: unknown;
@@ -123,7 +123,11 @@ export const useRiskScoreKpi = ({
useEffect(() => {
if (error) {
if (!isIndexNotFoundError(error)) {
- addError(error, { title: i18n.FAIL_RISK_SCORE });
+ addError(error, {
+ title: i18n.translate('xpack.securitySolution.riskScore.kpi.failSearchDescription', {
+ defaultMessage: `Failed to run search on risk score`,
+ }),
+ });
}
}
}, [addError, error]);
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/types.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/types.ts
new file mode 100644
index 00000000000000..419ebc60c4923d
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/entity_analytics/api/types.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { HostRiskScore, UserRiskScore } from '../../../common/search_strategy';
+
+export interface HostRisk {
+ loading: boolean;
+ isModuleEnabled: boolean;
+ result?: HostRiskScore[];
+}
+
+export interface UserRisk {
+ loading: boolean;
+ isModuleEnabled: boolean;
+ result?: UserRiskScore[];
+}
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts
index 361d6d133a93d5..ee343526ea2b09 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts
@@ -32,3 +32,15 @@ export const RISK_SCORE_RANGES = {
[RiskSeverity.high]: { start: 70, stop: 90 },
[RiskSeverity.critical]: { start: 90, stop: 100 },
};
+
+export enum UserRiskScoreQueryId {
+ USERS_BY_RISK = 'UsersByRisk',
+ USER_DETAILS_RISK_SCORE = 'UserDetailsRiskScore',
+}
+
+export enum HostRiskScoreQueryId {
+ DEFAULT = 'HostRiskScore',
+ HOST_DETAILS_RISK_SCORE = 'HostDetailsRiskScore',
+ OVERVIEW_RISKY_HOSTS = 'OverviewRiskyHosts',
+ HOSTS_BY_RISK = 'HostsByRisk',
+}
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx
similarity index 86%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx
index ac064feb35f047..994158104df5b0 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/index.tsx
@@ -6,11 +6,11 @@
*/
import { EuiEmptyPrompt, EuiPanel, EuiToolTip } from '@elastic/eui';
import React from 'react';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useCheckSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_check_signal_index';
-import type { inputsModel } from '../../../../common/store';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useCheckSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_check_signal_index';
+import type { inputsModel } from '../../../common/store';
import { RiskScoreHeaderTitle } from '../risk_score_onboarding/risk_score_header_title';
-import { HeaderSection } from '../../../../common/components/header_section';
+import { HeaderSection } from '../../../common/components/header_section';
import { RiskScoreDocLink } from '../risk_score_onboarding/risk_score_doc_link';
import { RiskScoreEnableButton } from '../risk_score_onboarding/risk_score_enable_button';
import * as i18n from './translations';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/translations.ts
similarity index 92%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/translations.ts
index 4c4d7073ed1dbd..c109c403a091ee 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/enable_risk_score/translations.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/enable_risk_score/translations.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { getRiskEntityTranslation } from '../translations';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { getRiskEntityTranslation } from '../risk_score/translations';
export const ENABLE_RISK_SCORE_POPOVER = i18n.translate(
'xpack.securitySolution.enableRiskScore.enableRiskScorePopoverTitle',
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx
similarity index 73%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx
index 4ce4b6810ec981..8400578b85c4f8 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx
@@ -6,14 +6,14 @@
*/
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
-import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
-import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock';
-import { TestProviders } from '../../../../common/mock';
+import { AnomalyEntity } from '../../../common/components/ml/anomaly/use_anomalies_search';
+import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock';
+import { TestProviders } from '../../../common/mock';
import { AnomaliesCountLink } from './anomalies_count_link';
const mockedTelemetry = createTelemetryServiceMock();
-jest.mock('../../../../common/lib/kibana', () => {
- const original = jest.requireActual('../../../../common/lib/kibana');
+jest.mock('../../../common/lib/kibana', () => {
+ const original = jest.requireActual('../../../common/lib/kibana');
return {
...original,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx
similarity index 75%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx
index 529f197c62e440..bb32564acb1b94 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/anomalies_count_link.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx
@@ -6,15 +6,15 @@
*/
import { useDispatch } from 'react-redux';
import React, { useCallback } from 'react';
-import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
-import { SecuritySolutionLinkAnchor } from '../../../../common/components/links';
-import { SecurityPageName } from '../../../../app/types';
-import { usersActions } from '../../../../explore/users/store';
-import { hostsActions } from '../../../../explore/hosts/store';
-import { HostsType } from '../../../../explore/hosts/store/model';
-import { UsersType } from '../../../../explore/users/store/model';
+import { AnomalyEntity } from '../../../common/components/ml/anomaly/use_anomalies_search';
+import { SecuritySolutionLinkAnchor } from '../../../common/components/links';
+import { SecurityPageName } from '../../../app/types';
+import { usersActions } from '../../../explore/users/store';
+import { hostsActions } from '../../../explore/hosts/store';
+import { HostsType } from '../../../explore/hosts/store/model';
+import { UsersType } from '../../../explore/users/store/model';
-import { useKibana } from '../../../../common/lib/kibana';
+import { useKibana } from '../../../common/lib/kibana';
export const AnomaliesCountLink = ({
count,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.test.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.test.tsx
index f4349df20dc51a..6083c18dfb38d7 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.test.tsx
@@ -6,8 +6,8 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
-import type { SecurityJob } from '../../../../common/components/ml_popover/types';
+import { AnomalyEntity } from '../../../common/components/ml/anomaly/use_anomalies_search';
+import type { SecurityJob } from '../../../common/components/ml_popover/types';
import { useAnomaliesColumns } from './columns';
describe('useAnomaliesColumns', () => {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.tsx
index 9faabf8d43f1c8..6574f6a566b155 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/columns.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/columns.tsx
@@ -8,11 +8,11 @@ import React, { useMemo } from 'react';
import styled from 'styled-components';
import type { EuiTableFieldDataColumnType } from '@elastic/eui';
import * as i18n from './translations';
-import type { SecurityJob } from '../../../../common/components/ml_popover/types';
-import { isJobStarted } from '../../../../../common/machine_learning/helpers';
+import type { SecurityJob } from '../../../common/components/ml_popover/types';
+import { isJobStarted } from '../../../../common/machine_learning/helpers';
import { TotalAnomalies } from './components/total_anomalies';
-import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import type { AnomaliesCount } from '../../../common/components/ml/anomaly/use_anomalies_search';
type AnomaliesColumns = Array>;
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/anomalies_tab_link.tsx
similarity index 75%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/anomalies_tab_link.tsx
index 80f78ee50331ef..9f515d8439a668 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/anomalies_tab_link.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/anomalies_tab_link.tsx
@@ -7,13 +7,13 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
-import { SecuritySolutionLinkAnchor } from '../../../../../common/components/links';
-import { SecurityPageName } from '../../../../../app/types';
-import { usersActions } from '../../../../../explore/users/store';
-import { hostsActions } from '../../../../../explore/hosts/store';
-import { HostsType } from '../../../../../explore/hosts/store/model';
-import { UsersType } from '../../../../../explore/users/store/model';
-import { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search';
+import { SecuritySolutionLinkAnchor } from '../../../../common/components/links';
+import { SecurityPageName } from '../../../../app/types';
+import { usersActions } from '../../../../explore/users/store';
+import { hostsActions } from '../../../../explore/hosts/store';
+import { HostsType } from '../../../../explore/hosts/store/model';
+import { UsersType } from '../../../../explore/users/store/model';
+import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
export const AnomaliesTabLink = ({
count,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.test.tsx
similarity index 88%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.test.tsx
index c5c199b79df095..1fe70e558cbdcd 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.test.tsx
@@ -7,11 +7,11 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
-import { useEnableDataFeed } from '../../../../../common/components/ml_popover/hooks/use_enable_data_feed';
-import type { SecurityJob } from '../../../../../common/components/ml_popover/types';
+import { useEnableDataFeed } from '../../../../common/components/ml_popover/hooks/use_enable_data_feed';
+import type { SecurityJob } from '../../../../common/components/ml_popover/types';
import { EnableJob } from './enable_job';
-jest.mock('../../../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({
+jest.mock('../../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({
useEnableDataFeed: jest.fn(() => ({ enableDatafeed: jest.fn(), isLoading: false })),
}));
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.tsx
similarity index 80%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.tsx
index 533a0eddcbc1a8..b98933a34e09cc 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/enable_job.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/enable_job.tsx
@@ -7,10 +7,10 @@
import React, { useCallback } from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
-import type { SecurityJob } from '../../../../../common/components/ml_popover/types';
-import { LinkAnchor } from '../../../../../common/components/links';
+import type { SecurityJob } from '../../../../common/components/ml_popover/types';
+import { LinkAnchor } from '../../../../common/components/links';
import * as i18n from '../translations';
-import { useEnableDataFeed } from '../../../../../common/components/ml_popover/hooks/use_enable_data_feed';
+import { useEnableDataFeed } from '../../../../common/components/ml_popover/hooks/use_enable_data_feed';
export const EnableJob = ({
job,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.test.tsx
similarity index 81%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.test.tsx
index 3cd8e25869763c..4d064682f5c8d9 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.test.tsx
@@ -6,11 +6,11 @@
*/
import React from 'react';
-import { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search';
-import type { SecurityJob } from '../../../../../common/components/ml_popover/types';
+import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import type { SecurityJob } from '../../../../common/components/ml_popover/types';
import { render } from '@testing-library/react';
import { TotalAnomalies } from './total_anomalies';
-import { TestProviders } from '../../../../../common/mock';
+import { TestProviders } from '../../../../common/mock';
const defaultProps = {
count: 0,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.tsx
index 8311b28177a08a..b72f1ef8879bb5 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/components/total_anomalies.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/components/total_anomalies.tsx
@@ -11,9 +11,9 @@ import {
isJobFailed,
isJobLoading,
isJobStarted,
-} from '../../../../../../common/machine_learning/helpers';
-import type { AnomalyEntity } from '../../../../../common/components/ml/anomaly/use_anomalies_search';
-import type { SecurityJob } from '../../../../../common/components/ml_popover/types';
+} from '../../../../../common/machine_learning/helpers';
+import type { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import type { SecurityJob } from '../../../../common/components/ml_popover/types';
import * as i18n from '../translations';
import { AnomaliesTabLink } from './anomalies_tab_link';
import { EnableJob } from './enable_job';
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.test.tsx
similarity index 92%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.test.tsx
index 3e6b85d7beca9f..ee3c4cfd022d89 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.test.tsx
@@ -8,13 +8,13 @@
import { act, fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { EntityAnalyticsAnomalies } from '.';
-import type { AnomaliesCount } from '../../../../common/components/ml/anomaly/use_anomalies_search';
-import { AnomalyEntity } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import type { AnomaliesCount } from '../../../common/components/ml/anomaly/use_anomalies_search';
+import { AnomalyEntity } from '../../../common/components/ml/anomaly/use_anomalies_search';
-import { TestProviders } from '../../../../common/mock';
-import type { SecurityJob } from '../../../../common/components/ml_popover/types';
+import { TestProviders } from '../../../common/mock';
+import type { SecurityJob } from '../../../common/components/ml_popover/types';
-jest.mock('../../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({
+jest.mock('../../../common/components/ml_popover/hooks/use_enable_data_feed', () => ({
useEnableDataFeed: () => ({
loading: false,
enableDatafeed: jest.fn().mockResolvedValue({ enabled: true }),
@@ -22,7 +22,7 @@ jest.mock('../../../../common/components/ml_popover/hooks/use_enable_data_feed',
}));
// Query toggle only works if pageName.lenght > 0
-jest.mock('../../../../common/utils/route/use_route_spy', () => ({
+jest.mock('../../../common/utils/route/use_route_spy', () => ({
useRouteSpy: jest.fn().mockReturnValue([
{
pageName: 'not_empty',
@@ -45,10 +45,8 @@ jest.mock(
}
);
-jest.mock('../../../../common/components/ml/anomaly/use_anomalies_search', () => {
- const original = jest.requireActual(
- '../../../../common/components/ml/anomaly/use_anomalies_search'
- );
+jest.mock('../../../common/components/ml/anomaly/use_anomalies_search', () => {
+ const original = jest.requireActual('../../../common/components/ml/anomaly/use_anomalies_search');
return {
...original,
useAggregatedAnomaliesByJob: () => mockUseAggregatedAnomaliesByJob(),
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.tsx
index 3e6f2e570cc22c..d841e59aeb67a8 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/index.tsx
@@ -16,26 +16,26 @@ import {
import { MLJobsAwaitingNodeWarning, ML_PAGES, useMlHref } from '@kbn/ml-plugin/public';
import { FormattedMessage } from '@kbn/i18n-react';
-import { HeaderSection } from '../../../../common/components/header_section';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
-import { LastUpdatedAt } from '../../../../common/components/last_updated_at';
+import { HeaderSection } from '../../../common/components/header_section';
+import { useQueryToggle } from '../../../common/containers/query_toggle';
+import { LastUpdatedAt } from '../../../common/components/last_updated_at';
import * as i18n from './translations';
-import { useAggregatedAnomaliesByJob } from '../../../../common/components/ml/anomaly/use_anomalies_search';
+import { useAggregatedAnomaliesByJob } from '../../../common/components/ml/anomaly/use_anomalies_search';
import { useAnomaliesColumns } from './columns';
-import { useQueryInspector } from '../../../../common/components/page/manage_query';
-import { useGlobalTime } from '../../../../common/containers/use_global_time';
+import { useQueryInspector } from '../../../common/components/page/manage_query';
+import { useGlobalTime } from '../../../common/containers/use_global_time';
import {
LinkAnchor,
LinkButton,
useGetSecuritySolutionLinkProps,
-} from '../../../../common/components/links';
-import { HostsTableType } from '../../../../explore/hosts/store/model';
-import { getTabsOnHostsUrl } from '../../../../common/components/link_to/redirect_to_hosts';
-import { SecurityPageName } from '../../../../app/types';
-import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users';
-import { UsersTableType } from '../../../../explore/users/store/model';
-import { useKibana } from '../../../../common/lib/kibana';
-import type { SecurityJob } from '../../../../common/components/ml_popover/types';
+} from '../../../common/components/links';
+import { HostsTableType } from '../../../explore/hosts/store/model';
+import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
+import { SecurityPageName } from '../../../app/types';
+import { getTabsOnUsersUrl } from '../../../common/components/link_to/redirect_to_users';
+import { UsersTableType } from '../../../explore/users/store/model';
+import { useKibana } from '../../../common/lib/kibana';
+import type { SecurityJob } from '../../../common/components/ml_popover/types';
const TABLE_QUERY_ID = 'entityAnalyticsDashboardAnomaliesTable';
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/query/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/query/index.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/query/index.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/query/index.ts
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/anomalies/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/translations.ts
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx
similarity index 84%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx
index 62820ee2c0de96..392656402c63eb 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx
@@ -8,13 +8,14 @@
import { act, fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { EntityAnalyticsHeader } from '.';
-import { Direction, RiskScoreFields, RiskSeverity } from '../../../../../common/search_strategy';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
-import { TestProviders } from '../../../../common/mock';
-import { hostsActions } from '../../../../explore/hosts/store';
-import { HostsType } from '../../../../explore/hosts/store/model';
-import { usersActions } from '../../../../explore/users/store';
-import { UsersTableType } from '../../../../explore/users/store/model';
+import { Direction, RiskScoreFields, RiskSeverity } from '../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import { hostsActions } from '../../../explore/hosts/store';
+import { HostsType } from '../../../explore/hosts/store/model';
+import { usersActions } from '../../../explore/users/store';
+import { UsersTableType } from '../../../explore/users/store/model';
+
+import type { SeverityCount } from '../severity/types';
const mockSeverityCount: SeverityCount = {
[RiskSeverity.low]: 1,
@@ -24,11 +25,11 @@ const mockSeverityCount: SeverityCount = {
[RiskSeverity.critical]: 99,
};
-jest.mock('../../../../common/components/ml/hooks/use_ml_capabilities', () => ({
+jest.mock('../../../common/components/ml/hooks/use_ml_capabilities', () => ({
useMlCapabilities: () => ({ isPlatinumOrTrialLicense: true, capabilities: {} }),
}));
-jest.mock('../../../../explore/containers/risk_score', () => {
+jest.mock('../../api/hooks/use_risk_score_kpi', () => {
return {
useRiskScoreKpi: () => ({ severityCount: mockSeverityCount }),
};
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx
similarity index 82%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx
index 3be1dd65f48eb4..8a814a1ee5a2e7 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx
@@ -9,34 +9,34 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle, EuiLink } from '@elastic
import styled from 'styled-components';
import { useDispatch } from 'react-redux';
import { sumBy } from 'lodash/fp';
-import { useRiskScoreKpi } from '../../../../explore/containers/risk_score';
-import { LinkAnchor, useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
+import { SEVERITY_COLOR } from '../../../overview/components/detection_response/utils';
+import { LinkAnchor, useGetSecuritySolutionLinkProps } from '../../../common/components/links';
import {
Direction,
RiskScoreEntity,
RiskScoreFields,
RiskSeverity,
-} from '../../../../../common/search_strategy';
+} from '../../../../common/search_strategy';
import * as i18n from './translations';
-import { getTabsOnHostsUrl } from '../../../../common/components/link_to/redirect_to_hosts';
-import { SecurityPageName } from '../../../../app/types';
-import { HostsTableType, HostsType } from '../../../../explore/hosts/store/model';
-import { hostsActions } from '../../../../explore/hosts/store';
-import { usersActions } from '../../../../explore/users/store';
-import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users';
-import { UsersTableType } from '../../../../explore/users/store/model';
-import { useAggregatedAnomaliesByJob } from '../../../../common/components/ml/anomaly/use_anomalies_search';
-import { useGlobalTime } from '../../../../common/containers/use_global_time';
-import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { useQueryInspector } from '../../../../common/components/page/manage_query';
-import { ENTITY_ANALYTICS_ANOMALIES_PANEL } from '../anomalies';
-import { isJobStarted } from '../../../../../common/machine_learning/helpers';
-import { FormattedCount } from '../../../../common/components/formatted_number';
-import { SEVERITY_COLOR } from '../../detection_response/utils';
-import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query';
+import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
+import { SecurityPageName } from '../../../app/types';
+import { HostsTableType, HostsType } from '../../../explore/hosts/store/model';
+import { hostsActions } from '../../../explore/hosts/store';
+import { usersActions } from '../../../explore/users/store';
+import { getTabsOnUsersUrl } from '../../../common/components/link_to/redirect_to_users';
+import { UsersTableType } from '../../../explore/users/store/model';
+import { useAggregatedAnomaliesByJob } from '../../../common/components/ml/anomaly/use_anomalies_search';
+import { useGlobalTime } from '../../../common/containers/use_global_time';
+import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
+import { useQueryInspector } from '../../../common/components/page/manage_query';
+import { ENTITY_ANALYTICS_ANOMALIES_PANEL } from '../entity_analytics_anomalies';
+import { isJobStarted } from '../../../../common/machine_learning/helpers';
+import { FormattedCount } from '../../../common/components/formatted_number';
+import { useGlobalFilterQuery } from '../../../common/hooks/use_global_filter_query';
+import { useRiskScoreKpi } from '../../api/hooks/use_risk_score_kpi';
const StyledEuiTitle = styled(EuiTitle)`
- color: ${({ theme: { eui } }) => SEVERITY_COLOR.critical};
+ color: ${SEVERITY_COLOR.critical};
`;
const HOST_RISK_QUERY_ID = 'hostRiskScoreKpiQuery';
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/header/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/translations.ts
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/__mocks__/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts
similarity index 82%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/__mocks__/index.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts
index bf863d0011ad38..19f3a03e41c037 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/__mocks__/index.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { RiskSeverity } from '../../../../../../common/search_strategy/security_solution';
+import { RiskSeverity } from '../../../../../common/search_strategy/security_solution';
export const mockSeverityCount = {
[RiskSeverity.unknown]: 1,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx
similarity index 79%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx
index 69816567f90cab..e346e999f8bd18 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx
@@ -7,20 +7,20 @@
import { render } from '@testing-library/react';
import React from 'react';
-import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
-import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { TestProviders } from '../../../../common/mock';
-import { generateSeverityFilter } from '../../../../explore/hosts/store/helpers';
+import { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
+import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
+import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { TestProviders } from '../../../common/mock';
+import { generateSeverityFilter } from '../../../explore/hosts/store/helpers';
import { ChartContent } from './chart_content';
import { mockSeverityCount } from './__mocks__';
-jest.mock('../../../../common/components/visualization_actions/visualization_embeddable');
-jest.mock('../../../../common/hooks/use_experimental_features', () => ({
+jest.mock('../../../common/components/visualization_actions/visualization_embeddable');
+jest.mock('../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn(),
}));
-jest.mock('../../../../common/hooks/use_space_id', () => ({
+jest.mock('../../../common/hooks/use_space_id', () => ({
useSpaceId: jest.fn(),
}));
describe('ChartContent', () => {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.tsx
similarity index 66%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.tsx
index 8f03cb08594aad..78828b1c769724 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/chart_content.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.tsx
@@ -6,16 +6,16 @@
*/
import React, { useMemo } from 'react';
-import type { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
-import { EMPTY_SEVERITY_COUNT } from '../../../../../common/search_strategy';
-import { getRiskScoreDonutAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut';
-import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
-import { generateSeverityFilter } from '../../../../explore/hosts/store/helpers';
-import { RiskScoreDonutChart } from '../common/risk_score_donut_chart';
-import { TOTAL_LABEL } from '../common/translations';
+import { i18n } from '@kbn/i18n';
+import type { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
+import { EMPTY_SEVERITY_COUNT } from '../../../../common/search_strategy';
+import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
+import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import type { SeverityCount } from '../severity/types';
+import { generateSeverityFilter } from '../../../explore/hosts/store/helpers';
+import { RiskScoreDonutChart } from '../risk_score_donut_chart';
+import { getRiskScoreDonutAttributes } from '../../lens_attributes/risk_score_donut';
const CHART_HEIGHT = 180;
const ChartContentComponent = ({
@@ -56,7 +56,12 @@ const ChartContentComponent = ({
height={CHART_HEIGHT}
id={`${kpiQueryId}-donut`}
isDonut={true}
- label={TOTAL_LABEL}
+ label={i18n.translate(
+ 'xpack.securitySolution.entityAnalytics.riskScore.chart.totalLabel',
+ {
+ defaultMessage: 'Total',
+ }
+ )}
stackByField={riskEntity}
timerange={timerange}
width="270px"
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/columns.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/columns.tsx
index 120a4cf3d4c7f5..e5926c2df7b4f3 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/columns.tsx
@@ -9,28 +9,28 @@ import React from 'react';
import type { EuiBasicTableColumn } from '@elastic/eui';
import { EuiLink } from '@elastic/eui';
import styled from 'styled-components';
-import { UsersTableType } from '../../../../explore/users/store/model';
-import { getEmptyTagValue } from '../../../../common/components/empty_value';
-import { HostDetailsLink, UserDetailsLink } from '../../../../common/components/links';
-import { HostsTableType } from '../../../../explore/hosts/store/model';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
-import { CELL_ACTIONS_TELEMETRY } from '../../../../explore/components/risk_score/constants';
+import { UsersTableType } from '../../../explore/users/store/model';
+import { getEmptyTagValue } from '../../../common/components/empty_value';
+import { HostDetailsLink, UserDetailsLink } from '../../../common/components/links';
+import { HostsTableType } from '../../../explore/hosts/store/model';
+import { RiskScoreLevel } from '../severity/common';
+import { CELL_ACTIONS_TELEMETRY } from '../risk_score/constants';
import type {
HostRiskScore,
Maybe,
RiskSeverity,
UserRiskScore,
-} from '../../../../../common/search_strategy';
-import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_strategy';
+} from '../../../../common/search_strategy';
+import { RiskScoreEntity, RiskScoreFields } from '../../../../common/search_strategy';
import * as i18n from './translations';
-import { FormattedCount } from '../../../../common/components/formatted_number';
+import { FormattedCount } from '../../../common/components/formatted_number';
import {
SecurityCellActions,
CellActionsMode,
SecurityCellActionsTrigger,
SecurityCellActionType,
-} from '../../../../common/components/cell_actions';
-import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';
+} from '../../../common/components/cell_actions';
+import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date';
type HostRiskScoreColumns = Array>;
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.test.tsx
similarity index 86%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.test.tsx
index d6e7c39b4fada4..b4f24e0d27d6f9 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.test.tsx
@@ -7,14 +7,14 @@
import type { RenderResult } from '@testing-library/react';
import { render } from '@testing-library/react';
import React from 'react';
-import { SecurityPageName } from '../../../../../common/constants';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
+import { SecurityPageName } from '../../../../common/constants';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useGetSecuritySolutionLinkProps } from '../../../common/components/links';
import { RiskScoreHeaderContent } from './header_content';
import { mockSeverityCount } from './__mocks__';
-jest.mock('../../../../common/components/links', () => {
- const actual = jest.requireActual('../../../../common/components/links');
+jest.mock('../../../common/components/links', () => {
+ const actual = jest.requireActual('../../../common/components/links');
return {
...actual,
useGetSecuritySolutionLinkProps: jest.fn(),
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.tsx
similarity index 80%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.tsx
index e9b1f56c6e0ec0..10b94b95a886ff 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/header_content.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/header_content.tsx
@@ -6,14 +6,14 @@
*/
import React, { useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import type { RiskSeverity, RiskScoreEntity } from '../../../../../common/search_strategy';
-import { SeverityFilterGroup } from '../../../../explore/components/risk_score/severity/severity_filter_group';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
-import { EMPTY_SEVERITY_COUNT } from '../../../../../common/search_strategy';
-import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../../common/components/links';
-import type { SecurityPageName } from '../../../../../common/constants';
+import type { RiskSeverity, RiskScoreEntity } from '../../../../common/search_strategy';
+import { SeverityFilterGroup } from '../severity/severity_filter_group';
+import type { SeverityCount } from '../severity/types';
+import { EMPTY_SEVERITY_COUNT } from '../../../../common/search_strategy';
+import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../common/components/links';
+import type { SecurityPageName } from '../../../../common/constants';
import * as i18n from './translations';
-import { RiskInformationButtonEmpty } from '../../../../explore/components/risk_score/risk_information';
+import { RiskInformationButtonEmpty } from '../risk_information';
const RiskScoreHeaderContentComponent = ({
entityLinkProps,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx
similarity index 87%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx
index 8af9349d9bceac..d9a265e662a7ee 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx
@@ -7,18 +7,19 @@
import { render, fireEvent, waitFor } from '@testing-library/react';
import React from 'react';
-import { TestProviders } from '../../../../common/mock';
+import { TestProviders } from '../../../common/mock';
import { EntityAnalyticsRiskScores } from '.';
-import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
-import { useRiskScore, useRiskScoreKpi } from '../../../../explore/containers/risk_score';
-import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__';
-import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock';
+import { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
+import type { SeverityCount } from '../severity/types';
+import { useKibana as mockUseKibana } from '../../../common/lib/kibana/__mocks__';
+import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock';
+import { useRiskScore } from '../../api/hooks/use_risk_score';
+import { useRiskScoreKpi } from '../../api/hooks/use_risk_score_kpi';
const mockedTelemetry = createTelemetryServiceMock();
const mockedUseKibana = mockUseKibana();
-jest.mock('../../../../common/lib/kibana', () => {
- const original = jest.requireActual('../../../../common/lib/kibana');
+jest.mock('../../../common/lib/kibana', () => {
+ const original = jest.requireActual('../../../common/lib/kibana');
return {
...original,
@@ -43,7 +44,7 @@ const mockSeverityCount: SeverityCount = {
const mockUseQueryToggle = jest
.fn()
.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
-jest.mock('../../../../common/containers/query_toggle', () => {
+jest.mock('../../../common/containers/query_toggle', () => {
return {
useQueryToggle: () => mockUseQueryToggle(),
};
@@ -58,16 +59,17 @@ const defaultProps = {
};
const mockUseRiskScore = useRiskScore as jest.Mock;
const mockUseRiskScoreKpi = useRiskScoreKpi as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../api/hooks/use_risk_score');
+jest.mock('../../api/hooks/use_risk_score_kpi');
const mockOpenAlertsPageWithFilters = jest.fn();
-jest.mock('../../../../common/hooks/use_navigate_to_alerts_page_with_filters', () => {
+jest.mock('../../../common/hooks/use_navigate_to_alerts_page_with_filters', () => {
return {
useNavigateToAlertsPageWithFilters: () => mockOpenAlertsPageWithFilters,
};
});
-jest.mock('../../../../common/components/hover_actions', () => ({ HoverActions: () => null }));
+jest.mock('../../../common/components/hover_actions', () => ({ HoverActions: () => null }));
describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
'EntityAnalyticsRiskScores entityType: %s',
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx
similarity index 77%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx
index 97cf4363f690da..ce695e75f58f16 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx
@@ -7,32 +7,32 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { EnableRiskScore } from '../../../../explore/components/risk_score/enable_risk_score';
+import { EnableRiskScore } from '../enable_risk_score';
import { getRiskScoreColumns } from './columns';
-import { LastUpdatedAt } from '../../../../common/components/last_updated_at';
-import { HeaderSection } from '../../../../common/components/header_section';
-import { useRiskScore, useRiskScoreKpi } from '../../../../explore/containers/risk_score';
-
-import type { RiskSeverity } from '../../../../../common/search_strategy';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { generateSeverityFilter } from '../../../../explore/hosts/store/helpers';
-import { useQueryInspector } from '../../../../common/components/page/manage_query';
-import { useGlobalTime } from '../../../../common/containers/use_global_time';
-import { InspectButtonContainer } from '../../../../common/components/inspect';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
-import { StyledBasicTable } from '../common/styled_basic_table';
-import { RiskScoreHeaderTitle } from '../../../../explore/components/risk_score/risk_score_onboarding/risk_score_header_title';
-import { RiskScoresNoDataDetected } from '../../../../explore/components/risk_score/risk_score_onboarding/risk_score_no_data_detected';
-import { useRefetchQueries } from '../../../../common/hooks/use_refetch_queries';
-import { Loader } from '../../../../common/components/loader';
-import { Panel } from '../../../../common/components/panel';
+import { LastUpdatedAt } from '../../../common/components/last_updated_at';
+import { HeaderSection } from '../../../common/components/header_section';
+import type { RiskSeverity } from '../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { generateSeverityFilter } from '../../../explore/hosts/store/helpers';
+import { useQueryInspector } from '../../../common/components/page/manage_query';
+import { useGlobalTime } from '../../../common/containers/use_global_time';
+import { InspectButtonContainer } from '../../../common/components/inspect';
+import { useQueryToggle } from '../../../common/containers/query_toggle';
+import { StyledBasicTable } from '../styled_basic_table';
+import { RiskScoreHeaderTitle } from '../risk_score_onboarding/risk_score_header_title';
+import { RiskScoresNoDataDetected } from '../risk_score_onboarding/risk_score_no_data_detected';
+import { useRefetchQueries } from '../../../common/hooks/use_refetch_queries';
+import { Loader } from '../../../common/components/loader';
+import { Panel } from '../../../common/components/panel';
import { useEntityInfo } from './use_entity';
import { RiskScoreHeaderContent } from './header_content';
import { ChartContent } from './chart_content';
-import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters';
+import { useNavigateToAlertsPageWithFilters } from '../../../common/hooks/use_navigate_to_alerts_page_with_filters';
import { getRiskEntityTranslation } from './translations';
-import { useKibana } from '../../../../common/lib/kibana';
-import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query';
+import { useKibana } from '../../../common/lib/kibana';
+import { useGlobalFilterQuery } from '../../../common/hooks/use_global_filter_query';
+import { useRiskScoreKpi } from '../../api/hooks/use_risk_score_kpi';
+import { useRiskScore } from '../../api/hooks/use_risk_score';
const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskScoreEntity }) => {
const { deleteQuery, setQuery, from, to } = useGlobalTime();
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts
similarity index 82%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts
index 22fe8a3d528236..5816accea220aa 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/translations.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/translations.ts
@@ -6,9 +6,9 @@
*/
import { i18n } from '@kbn/i18n';
-import { getRiskEntityTranslation } from '../../../../explore/components/risk_score/translations';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-export * from '../../../../explore/components/risk_score/translations';
+import { getRiskEntityTranslation } from '../risk_score/translations';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+export * from '../risk_score/translations';
export const ENTITY_NAME = (riskEntity: RiskScoreEntity) =>
i18n.translate('xpack.securitySolution.entityAnalytics.riskDashboard.nameTitle', {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.test.ts
similarity index 93%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.test.ts
index 3e0f475d47fa98..25d772c26762ce 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.test.ts
@@ -6,7 +6,7 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { RiskScoreEntity } from '../../../../../common/search_strategy/security_solution/risk_score';
+import { RiskScoreEntity } from '../../../../common/search_strategy/security_solution/risk_score';
import { useEntityInfo } from './use_entity';
jest.mock('react-redux', () => {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.ts
similarity index 71%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.ts
index dc5d947ad44954..3b3640a7cecae6 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/use_entity.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/use_entity.ts
@@ -5,16 +5,16 @@
* 2.0.
*/
import { useDispatch } from 'react-redux';
-import { getTabsOnUsersUrl } from '../../../../common/components/link_to/redirect_to_users';
-import { UsersTableType } from '../../../../explore/users/store/model';
+import { getTabsOnUsersUrl } from '../../../common/components/link_to/redirect_to_users';
+import { UsersTableType } from '../../../explore/users/store/model';
-import { getTabsOnHostsUrl } from '../../../../common/components/link_to/redirect_to_hosts';
-import { HostsTableType, HostsType } from '../../../../explore/hosts/store/model';
+import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
+import { HostsTableType, HostsType } from '../../../explore/hosts/store/model';
-import { RiskScoreEntity } from '../../../../../common/search_strategy/security_solution/risk_score';
-import { usersActions } from '../../../../explore/users/store';
-import { hostsActions } from '../../../../explore/hosts/store';
-import { SecurityPageName } from '../../../../app/types';
+import { RiskScoreEntity } from '../../../../common/search_strategy/security_solution/risk_score';
+import { usersActions } from '../../../explore/users/store';
+import { hostsActions } from '../../../explore/hosts/store';
+import { SecurityPageName } from '../../../app/types';
const HOST_RISK_TABLE_QUERY_ID = 'hostRiskDashboardTable';
const HOST_RISK_KPI_QUERY_ID = 'headerHostRiskScoreKpiQuery';
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/action_column.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/action_column.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
index 9df74c2d608460..d5ff5425ccaa6b 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/action_column.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
@@ -8,8 +8,8 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { TestProviders } from '../../../../common/mock';
-import { ActionColumn } from './action_column';
import { alertDataMock } from '../mocks';
+import { ActionColumn } from './action_column';
describe('ActionColumn', () => {
it('renders', () => {
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/action_column.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/action_column.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/utility_bar.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.test.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/utility_bar.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.test.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/utility_bar.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/components/utility_bar.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions_panels.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions_panels.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions_panels.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/hooks/use_risk_input_actions_panels.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx
new file mode 100644
index 00000000000000..428f98530d55f8
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/index.tsx
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { PREFIX } from '../../../flyout/shared/test_ids';
+import { UserDetailsLeftPanelTab } from '../../../flyout/entity_details/user_details_left/tabs';
+import { RiskInputsTab } from './tabs/risk_inputs';
+
+export const RISK_INPUTS_TAB_TEST_ID = `${PREFIX}RiskInputsTab` as const;
+
+export const getRiskInputTab = (alertIds: string[]) => ({
+ id: UserDetailsLeftPanelTab.RISK_INPUTS,
+ 'data-test-subj': RISK_INPUTS_TAB_TEST_ID,
+ name: (
+
+ ),
+ content: ,
+});
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/mocks/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/mocks/index.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/risk_inputs.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs.test.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/risk_inputs.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs.test.tsx
index efd47e50009c97..038d6f7d622d41 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/risk_inputs.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs.test.tsx
@@ -9,8 +9,8 @@ import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { TestProviders } from '../../../../common/mock';
import { times } from 'lodash/fp';
-import { alertDataMock } from '../mocks';
import { RiskInputsTab } from './risk_inputs';
+import { alertDataMock } from '../mocks';
const mockUseAlertsByIds = jest.fn().mockReturnValue({ loading: false, data: [] });
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/risk_inputs.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/risk_inputs.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs.tsx
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.test.tsx
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.test.tsx
index 98cfa4895fbd40..d439c627365a09 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.test.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { getHostRiskScoreColumns } from './columns';
-import { TestProviders } from '../../../../common/mock';
+import { TestProviders } from '../../../common/mock';
import type { HostRiskScoreColumns } from '.';
describe('getHostRiskScoreColumns', () => {
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.tsx
similarity index 78%
rename from x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.tsx
index af6c0d502351e4..e1509a03a9a905 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/columns.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/columns.tsx
@@ -11,18 +11,18 @@ import {
SecurityCellActions,
CellActionsMode,
SecurityCellActionsTrigger,
-} from '../../../../common/components/cell_actions';
-import { getEmptyTagValue } from '../../../../common/components/empty_value';
-import { HostDetailsLink } from '../../../../common/components/links';
+} from '../../../common/components/cell_actions';
+import { getEmptyTagValue } from '../../../common/components/empty_value';
+import { HostDetailsLink } from '../../../common/components/links';
import type { HostRiskScoreColumns } from '.';
import * as i18n from './translations';
-import { HostsTableType } from '../../store/model';
-import type { Maybe, RiskSeverity } from '../../../../../common/search_strategy';
-import { RiskScoreFields, RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskScoreLevel } from '../../../components/risk_score/severity/common';
-import { ENTITY_RISK_LEVEL } from '../../../components/risk_score/translations';
-import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants';
-import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';
+import { HostsTableType } from '../../../explore/hosts/store/model';
+import type { Maybe, RiskSeverity } from '../../../../common/search_strategy';
+import { RiskScoreFields, RiskScoreEntity } from '../../../../common/search_strategy';
+import { RiskScoreLevel } from '../severity/common';
+import { ENTITY_RISK_LEVEL } from '../risk_score/translations';
+import { CELL_ACTIONS_TELEMETRY } from '../risk_score/constants';
+import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date';
export const getHostRiskScoreColumns = ({
dispatchSeverityUpdate,
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/index.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/index.tsx
index 9c486c96fdece8..5dca054f013cd9 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/index.tsx
@@ -9,10 +9,10 @@ import React, { useMemo, useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import type { Columns, Criteria, ItemsPerRow } from '../../../components/paginated_table';
-import { PaginatedTable } from '../../../components/paginated_table';
-import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
-import { hostsActions, hostsModel, hostsSelectors } from '../../store';
+import type { Columns, Criteria, ItemsPerRow } from '../../../explore/components/paginated_table';
+import { PaginatedTable } from '../../../explore/components/paginated_table';
+import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
+import { hostsActions, hostsModel, hostsSelectors } from '../../../explore/hosts/store';
import { getHostRiskScoreColumns } from './columns';
import type {
HostRiskScore,
@@ -20,18 +20,18 @@ import type {
RiskScoreSortField,
RiskSeverity,
RiskScoreFields,
-} from '../../../../../common/search_strategy';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import type { State } from '../../../../common/store';
-import * as i18n from '../hosts_table/translations';
+} from '../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import type { State } from '../../../common/store';
+import * as i18n from '../../../explore/hosts/components/hosts_table/translations';
import * as i18nHosts from './translations';
-import { SeverityBadges } from '../../../components/risk_score/severity/severity_badges';
-import { SeverityBar } from '../../../components/risk_score/severity/severity_bar';
-import { SeverityFilterGroup } from '../../../components/risk_score/severity/severity_filter_group';
+import { SeverityBadges } from '../severity/severity_badges';
+import { SeverityBar } from '../severity/severity_bar';
+import { SeverityFilterGroup } from '../severity/severity_filter_group';
-import type { SeverityCount } from '../../../components/risk_score/severity/types';
-import { RiskInformationButtonEmpty } from '../../../components/risk_score/risk_information';
+import type { SeverityCount } from '../severity/types';
+import { RiskInformationButtonEmpty } from '../risk_information';
export const rowItems: ItemsPerRow[] = [
{
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/hosts/components/host_risk_score_table/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/host_risk_score_table/translations.ts
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.test.tsx
similarity index 84%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.test.tsx
index d446645a739daa..c521542f1f7b1a 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.test.tsx
@@ -7,18 +7,17 @@
import React from 'react';
import { render } from '@testing-library/react';
-import { TestProviders } from '../../../../common/mock';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
-
-import { useRiskScore } from '../../../containers/risk_score';
+import { TestProviders } from '../../../common/mock';
+import { useQueryToggle } from '../../../common/containers/query_toggle';
import { RiskDetailsTabBody } from '.';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { HostsType } from '../../../hosts/store/model';
-import { UsersType } from '../../../users/store/model';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { HostsType } from '../../../explore/hosts/store/model';
+import { UsersType } from '../../../explore/users/store/model';
+import { useRiskScore } from '../../api/hooks/use_risk_score';
-jest.mock('../../../containers/risk_score');
-jest.mock('../../../../common/containers/query_toggle');
-jest.mock('../../../../common/lib/kibana');
+jest.mock('../../api/hooks/use_risk_score');
+jest.mock('../../../common/containers/query_toggle');
+jest.mock('../../../common/lib/kibana');
describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
'Risk Tab Body entityType: %s',
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.tsx
index 37b61a86fbea3c..ed132ade204dd5 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/index.tsx
@@ -9,33 +9,30 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
-import { RISKY_HOSTS_DASHBOARD_TITLE, RISKY_USERS_DASHBOARD_TITLE } from '../constants';
+import { RISKY_HOSTS_DASHBOARD_TITLE, RISKY_USERS_DASHBOARD_TITLE } from '../risk_score/constants';
import { EnableRiskScore } from '../enable_risk_score';
-import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
-import type { State } from '../../../../common/store';
-import { hostsModel, hostsSelectors } from '../../../hosts/store';
-import { usersSelectors } from '../../../users/store';
+import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
+import type { State } from '../../../common/store';
+import { hostsModel, hostsSelectors } from '../../../explore/hosts/store';
+import { usersSelectors } from '../../../explore/users/store';
import { RiskInformationButtonEmpty } from '../risk_information';
import * as i18n from './translations';
-import { useQueryInspector } from '../../../../common/components/page/manage_query';
+import { useQueryInspector } from '../../../common/components/page/manage_query';
import { RiskScoreOverTime } from '../risk_score_over_time';
import { TopRiskScoreContributors } from '../top_risk_score_contributors';
import { TopRiskScoreContributorsAlerts } from '../top_risk_score_contributors_alerts';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
-import {
- HostRiskScoreQueryId,
- UserRiskScoreQueryId,
- useRiskScore,
-} from '../../../containers/risk_score';
-import type { HostRiskScore, UserRiskScore } from '../../../../../common/search_strategy';
-import { buildEntityNameFilter, RiskScoreEntity } from '../../../../../common/search_strategy';
-import type { UsersComponentsQueryProps } from '../../../users/pages/navigation/types';
-import type { HostsComponentsQueryProps } from '../../../hosts/pages/navigation/types';
-import { useDashboardHref } from '../../../../common/hooks/use_dashboard_href';
+import { useQueryToggle } from '../../../common/containers/query_toggle';
+import type { HostRiskScore, UserRiskScore } from '../../../../common/search_strategy';
+import { buildEntityNameFilter, RiskScoreEntity } from '../../../../common/search_strategy';
+import type { UsersComponentsQueryProps } from '../../../explore/users/pages/navigation/types';
+import type { HostsComponentsQueryProps } from '../../../explore/hosts/pages/navigation/types';
+import { useDashboardHref } from '../../../common/hooks/use_dashboard_href';
import { RiskScoresNoDataDetected } from '../risk_score_onboarding/risk_score_no_data_detected';
-import { useRiskEngineStatus } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
-import { RiskScoreUpdatePanel } from '../../../../entity_analytics/components/risk_score_update_panel';
+import { useRiskEngineStatus } from '../../api/hooks/use_risk_engine_status';
+import { RiskScoreUpdatePanel } from '../risk_score_update_panel';
+import { HostRiskScoreQueryId, UserRiskScoreQueryId } from '../../common/utils';
+import { useRiskScore } from '../../api/hooks/use_risk_score';
const StyledEuiFlexGroup = styled(EuiFlexGroup)`
margin-top: ${({ theme }) => theme.eui.euiSizeL};
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/translations.ts
similarity index 83%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/translations.ts
index 4a98a8fc749d4e..f24f0716e4eb82 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_details_tab_body/translations.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_details_tab_body/translations.ts
@@ -6,8 +6,8 @@
*/
import { i18n } from '@kbn/i18n';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { getRiskEntityTranslation } from '../translations';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { getRiskEntityTranslation } from '../risk_score/translations';
export const RISK_SCORE_OVER_TIME = (riskEntity: RiskScoreEntity) =>
i18n.translate('xpack.securitySolution.riskTabBody.scoreOverTimeTitle', {
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.test.tsx
similarity index 92%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.test.tsx
index 19e21a57e5559e..44aee022f2f537 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.test.tsx
@@ -8,8 +8,8 @@
import { render, fireEvent } from '@testing-library/react';
import React from 'react';
import { RiskInformationButtonEmpty, RiskInformationButtonIcon } from '.';
-import { TestProviders } from '../../../../common/mock';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
'Risk Information entityType: %s',
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx
similarity index 98%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx
index bd099341419f0b..888854cebeb51b 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx
@@ -30,9 +30,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import * as i18n from './translations';
-import { useOnOpenCloseHandler } from '../../../../helper_hooks';
+import { useOnOpenCloseHandler } from '../../../helper_hooks';
import { RiskScoreLevel } from '../severity/common';
-import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
+import { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
import { RiskScoreDocLink } from '../risk_score_onboarding/risk_score_doc_link';
import { BETA } from '../risk_score_onboarding/translations';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/translations.ts
similarity index 91%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/translations.ts
index 20fa22bbc26f51..114da512b99744 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_information/translations.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/translations.ts
@@ -6,8 +6,8 @@
*/
import { i18n } from '@kbn/i18n';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { getRiskEntityTranslation } from '../translations';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { getRiskEntityTranslation } from '../risk_score/translations';
export const INFORMATION_LEVEL_HEADER = i18n.translate(
'xpack.securitySolution.riskInformation.levelHeader',
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/constants.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/constants.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/constants.ts
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score/translations.ts
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.test.tsx
similarity index 78%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.test.tsx
index 1b93d6545d8383..30a05bcfef1264 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.test.tsx
@@ -5,12 +5,12 @@
* 2.0.
*/
-import { RiskSeverity } from '../../../../../common/search_strategy';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
+import { RiskSeverity } from '../../../../common/search_strategy';
+import type { SeverityCount } from '../severity/types';
import { render } from '@testing-library/react';
import React from 'react';
-import { RiskScoreDonutChart } from './risk_score_donut_chart';
-import { TestProviders } from '../../../../common/mock';
+import { RiskScoreDonutChart } from '.';
+import { TestProviders } from '../../../common/mock';
const severityCount: SeverityCount = {
[RiskSeverity.low]: 1,
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.tsx
similarity index 67%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.tsx
index 2bb850e0c312c7..92ae592368ba58 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/risk_score_donut_chart.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/index.tsx
@@ -8,16 +8,16 @@
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
-import { RISK_SEVERITY_COLOUR } from '../../../../entity_analytics/common/utils';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
+import { i18n } from '@kbn/i18n';
+import { ChartLabel } from '../../../overview/components/detection_response/alerts_by_status/chart_label';
+import { RISK_SEVERITY_COLOUR } from '../../common/utils';
+import type { SeverityCount } from '../severity/types';
import { useRiskDonutChartData } from './use_risk_donut_chart_data';
-import type { FillColor } from '../../../../common/components/charts/donutchart';
-import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty';
-import { DonutChart } from '../../../../common/components/charts/donutchart';
-import { Legend } from '../../../../common/components/charts/legend';
-import { ChartLabel } from '../../detection_response/alerts_by_status/chart_label';
-import * as i18n from './translations';
-import type { RiskSeverity } from '../../../../../common/search_strategy';
+import type { FillColor } from '../../../common/components/charts/donutchart';
+import { emptyDonutColor } from '../../../common/components/charts/donutchart_empty';
+import { DonutChart } from '../../../common/components/charts/donutchart';
+import { Legend } from '../../../common/components/charts/legend';
+import type { RiskSeverity } from '../../../../common/search_strategy';
const DONUT_HEIGHT = 120;
@@ -53,7 +53,10 @@ export const RiskScoreDonutChart = ({ severityCount }: RiskScoreDonutChartProps)
data={donutChartData ?? null}
fillColor={fillColor}
height={DONUT_HEIGHT}
- label={i18n.TOTAL_LABEL}
+ label={i18n.translate(
+ 'xpack.securitySolution.entityAnalytics.riskScore.donut_chart.totalLabel',
+ { defaultMessage: 'Total' }
+ )}
title={}
totalCount={total}
/>
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts
similarity index 88%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts
index 2a7af3c2a28aae..933a9f98b5bea5 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts
@@ -6,9 +6,9 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { RiskSeverity } from '../../../../../common/search_strategy';
+import { RiskSeverity } from '../../../../common/search_strategy';
import { useRiskDonutChartData } from './use_risk_donut_chart_data';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
+import type { SeverityCount } from '../severity/types';
describe('useRiskDonutChartData', () => {
it('returns the total', () => {
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.ts
similarity index 70%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.ts
index 4b31142aef3c92..8505f229d893f7 100644
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/use_risk_donut_chart_data.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.ts
@@ -6,11 +6,11 @@
*/
import { sum } from 'lodash/fp';
import { useMemo } from 'react';
-import { RISK_SEVERITY_COLOUR } from '../../../../entity_analytics/common/utils';
-import type { LegendItem } from '../../../../common/components/charts/legend_item';
-import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types';
-import type { DonutChartProps } from '../../../../common/components/charts/donutchart';
-import type { RiskSeverity } from '../../../../../common/search_strategy';
+import { RISK_SEVERITY_COLOUR } from '../../common/utils';
+import type { LegendItem } from '../../../common/components/charts/legend_item';
+import type { SeverityCount } from '../severity/types';
+import type { DonutChartProps } from '../../../common/components/charts/donutchart';
+import type { RiskSeverity } from '../../../../common/search_strategy';
const legendField = 'kibana.alert.severity';
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx
index fa61a6c15b9e83..8e49e90c4cadd6 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx
@@ -37,7 +37,7 @@ import { useEnableRiskEngineMutation } from '../api/hooks/use_enable_risk_engine
import { useDisableRiskEngineMutation } from '../api/hooks/use_disable_risk_engine_mutation';
import { RiskEngineStatus, MAX_SPACES_COUNT } from '../../../common/entity_analytics/risk_engine';
-import { RiskInformationFlyout } from '../../explore/components/risk_score/risk_information';
+import { RiskInformationFlyout } from './risk_information';
import { useOnOpenCloseHandler } from '../../helper_hooks';
import type { RiskEngineMissingPrivilegesResponse } from '../hooks/use_missing_risk_engine_privileges';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx
index a54bb9e6276265..ca7b67134b29dc 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_doc_link.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_doc_link.tsx
@@ -7,9 +7,9 @@
import { EuiLink } from '@elastic/eui';
import React, { useMemo } from 'react';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { LEARN_MORE } from '../../../../overview/components/entity_analytics/risk_score/translations';
-import { useKibana } from '../../../../common/lib/kibana';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useKibana } from '../../../common/lib/kibana';
+import { LEARN_MORE } from '../entity_analytics_risk_score/translations';
const useLearnMoreLinkForEntity = (riskScoreEntity?: RiskScoreEntity) => {
const { docLinks } = useKibana().services;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.test.tsx
similarity index 89%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.test.tsx
index a92496316c3ce3..92e022b55aeeb2 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.test.tsx
@@ -6,8 +6,8 @@
*/
import { render, screen } from '@testing-library/react';
import React from 'react';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { TestProviders } from '../../../../common/mock';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
import { RiskScoreEnableButton } from './risk_score_enable_button';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx
similarity index 81%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx
index 6e84308911eddb..e6242e75677c13 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_enable_button.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_enable_button.tsx
@@ -9,16 +9,16 @@ import { EuiButton } from '@elastic/eui';
import React, { useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { useKibana } from '../../../../common/lib/kibana';
-import type { inputsModel } from '../../../../common/store';
-import { REQUEST_NAMES, useFetch } from '../../../../common/hooks/use_fetch';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { useKibana } from '../../../common/lib/kibana';
+import type { inputsModel } from '../../../common/store';
+import { REQUEST_NAMES, useFetch } from '../../../common/hooks/use_fetch';
import { useRiskScoreToastContent } from './use_risk_score_toast_content';
import { installRiskScoreModule } from './utils';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
-import { SecuritySolutionLinkButton } from '../../../../common/components/links';
-import { SecurityPageName } from '../../../../../common/constants';
+import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
+import { SecuritySolutionLinkButton } from '../../../common/components/links';
+import { SecurityPageName } from '../../../../common/constants';
const RiskScoreEnableButtonComponent = ({
refetch,
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_header_title.tsx
similarity index 53%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_header_title.tsx
index 741469833f92d0..1ec4edd65fe3b4 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_header_title.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_header_title.tsx
@@ -4,10 +4,11 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+
import React from 'react';
+import { FormattedMessage } from '@kbn/i18n-react';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import * as i18n from '../../../../overview/components/entity_analytics/common/translations';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
const RiskScoreHeaderTitleComponent = ({
riskScoreEntity,
@@ -18,7 +19,17 @@ const RiskScoreHeaderTitleComponent = ({
}) => (
<>
{title ??
- (riskScoreEntity === RiskScoreEntity.user ? i18n.USER_RISK_TITLE : i18n.HOST_RISK_TITLE)}
+ (riskScoreEntity === RiskScoreEntity.user ? (
+
+ ) : (
+
+ ))}
>
);
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_no_data_detected.tsx
similarity index 84%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_no_data_detected.tsx
index 533a7d00729cd3..c7bd233c7030a3 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_no_data_detected.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_no_data_detected.tsx
@@ -7,14 +7,14 @@
import { EuiEmptyPrompt, EuiPanel, EuiToolTip } from '@elastic/eui';
import React, { useMemo } from 'react';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
-import { HeaderSection } from '../../../../common/components/header_section';
+import { HeaderSection } from '../../../common/components/header_section';
import * as i18n from './translations';
import { RiskScoreHeaderTitle } from './risk_score_header_title';
import { RiskScoreRestartButton } from './risk_score_restart_button';
-import type { inputsModel } from '../../../../common/store';
-import { useIsNewRiskScoreModuleInstalled } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
+import type { inputsModel } from '../../../common/store';
+import { useIsNewRiskScoreModuleInstalled } from '../../api/hooks/use_risk_engine_status';
const RiskScoresNoDataDetectedComponent = ({
entityType,
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.test.tsx
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.test.tsx
index d6785847cd833c..dbccdd9a0c0852 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.test.tsx
@@ -7,8 +7,8 @@
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { TestProviders } from '../../../../common/mock';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
import { RiskScoreRestartButton } from './risk_score_restart_button';
import { restartRiskScoreTransforms } from './utils';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx
similarity index 83%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx
index be1f5b5d16621d..7f4316b77ee973 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/risk_score_restart_button.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/risk_score_restart_button.tsx
@@ -9,11 +9,11 @@ import { EuiButton } from '@elastic/eui';
import React, { useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { useKibana } from '../../../../common/lib/kibana';
-import type { inputsModel } from '../../../../common/store';
-import { REQUEST_NAMES, useFetch } from '../../../../common/hooks/use_fetch';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { useKibana } from '../../../common/lib/kibana';
+import type { inputsModel } from '../../../common/store';
+import { REQUEST_NAMES, useFetch } from '../../../common/hooks/use_fetch';
import { useRiskScoreToastContent } from './use_risk_score_toast_content';
import { restartRiskScoreTransforms } from './utils';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/translations.ts
similarity index 93%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/translations.ts
index 105036bd1be338..f8bcbf29725f9b 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/translations.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/translations.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
-import type { RiskScoreEntity } from '../../../../../common/entity_analytics/risk_engine';
-import { getRiskEntityTranslation } from '../translations';
+import type { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine';
+import { getRiskEntityTranslation } from '../risk_score/translations';
export const BETA = i18n.translate('xpack.securitySolution.riskScore.technicalPreviewLabel', {
defaultMessage: 'Beta',
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/use_risk_score_toast_content.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/use_risk_score_toast_content.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx
index 60c00ab9d8cb63..7df01d9f8bd86e 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/use_risk_score_toast_content.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/use_risk_score_toast_content.tsx
@@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n-react';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
+import type { RiskScoreEntity } from '../../../../common/search_strategy';
import { RiskScoreDocLink } from './risk_score_doc_link';
const StyledButton = styled(EuiButton)`
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.test.ts
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.test.ts
index 770c5b8caf72d2..816ba871249d2c 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.test.ts
@@ -5,26 +5,26 @@
* 2.0.
*/
import type { HttpSetup } from '@kbn/core/public';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
import {
getIngestPipelineName,
getLegacyIngestPipelineName,
getRiskScoreLatestTransformId,
getRiskScorePivotTransformId,
-} from '../../../../../common/utils/risk_score_modules';
+} from '../../../../common/utils/risk_score_modules';
import {
bulkDeletePrebuiltSavedObjects,
bulkCreatePrebuiltSavedObjects,
-} from '../../../containers/risk_score/onboarding/api';
+} from '../../deprecated_risk_engine/api';
-import * as api from '../../../containers/risk_score/onboarding/api';
+import * as api from '../../deprecated_risk_engine/api';
import {
installRiskScoreModule,
restartRiskScoreTransforms,
uninstallRiskScoreModule,
} from './utils';
-jest.mock('../../../containers/risk_score/onboarding/api');
+jest.mock('../../deprecated_risk_engine/api');
const mockHttp = {
post: jest.fn(),
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.ts
similarity index 96%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.ts
index 65677900f979e2..f1e582e1ed847c 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_onboarding/utils.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_onboarding/utils.ts
@@ -6,9 +6,9 @@
*/
import type { HttpSetup, NotificationsStart, ThemeServiceStart } from '@kbn/core/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import * as utils from '../../../../../common/utils/risk_score_modules';
-import type { inputsModel } from '../../../../common/store';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import * as utils from '../../../../common/utils/risk_score_modules';
+import type { inputsModel } from '../../../common/store';
import {
deleteStoredScripts,
@@ -19,12 +19,12 @@ import {
bulkCreatePrebuiltSavedObjects,
stopTransforms,
startTransforms,
-} from '../../../containers/risk_score/onboarding/api';
+} from '../../deprecated_risk_engine/api';
import {
INGEST_PIPELINE_DELETION_ERROR_MESSAGE,
TRANSFORM_DELETION_ERROR_MESSAGE,
UNINSTALLATION_ERROR,
-} from '../../../containers/risk_score/onboarding/api/translations';
+} from '../../deprecated_risk_engine/api/translations';
interface InstallRiskScoreModule {
dashboard?: DashboardStart;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
similarity index 85%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
index 109a51c1a829c1..22458a6d00fd11 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
@@ -8,10 +8,10 @@
import { render } from '@testing-library/react';
import React from 'react';
import { RiskScoreOverTime, scoreFormatter } from '.';
-import { TestProviders } from '../../../../common/mock';
+import { TestProviders } from '../../../common/mock';
import { LineSeries } from '@elastic/charts';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
const mockLineSeries = LineSeries as jest.Mock;
const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock;
@@ -23,11 +23,11 @@ jest.mock('@elastic/charts', () => {
};
});
-jest.mock('../../../../common/hooks/use_experimental_features', () => ({
+jest.mock('../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn(),
}));
-jest.mock('../../../../common/components/visualization_actions/visualization_embeddable');
-jest.mock('../../../../common/hooks/use_space_id', () => ({
+jest.mock('../../../common/components/visualization_actions/visualization_embeddable');
+jest.mock('../../../common/hooks/use_space_id', () => ({
useSpaceId: jest.fn().mockReturnValue('default'),
}));
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
similarity index 87%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
index 96381b3681c8b6..210f578b36ad72 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
@@ -22,23 +22,23 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingChart, EuiText, EuiPanel } from '@
import styled from 'styled-components';
import { euiThemeVars } from '@kbn/ui-theme';
import { i18n } from '@kbn/i18n';
-import { chartDefaultSettings, useThemes } from '../../../../common/components/charts/common';
-import { useTimeZone } from '../../../../common/lib/kibana';
-import { histogramDateTimeFormatter } from '../../../../common/components/utils';
-import { HeaderSection } from '../../../../common/components/header_section';
-import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
+import { chartDefaultSettings, useThemes } from '../../../common/components/charts/common';
+import { useTimeZone } from '../../../common/lib/kibana';
+import { histogramDateTimeFormatter } from '../../../common/components/utils';
+import { HeaderSection } from '../../../common/components/header_section';
+import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect';
import * as translations from './translations';
-import { PreferenceFormattedDate } from '../../../../common/components/formatted_date';
+import { PreferenceFormattedDate } from '../../../common/components/formatted_date';
import type {
HostRiskScore,
RiskScoreEntity,
UserRiskScore,
-} from '../../../../../common/search_strategy';
-import { isUserRiskScore } from '../../../../../common/search_strategy';
-import { useSpaceId } from '../../../../common/hooks/use_space_id';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
-import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
-import { getRiskScoreOverTimeAreaAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area';
+} from '../../../../common/search_strategy';
+import { isUserRiskScore } from '../../../../common/search_strategy';
+import { useSpaceId } from '../../../common/hooks/use_space_id';
+import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
+import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
+import { getRiskScoreOverTimeAreaAttributes } from '../../lens_attributes/risk_score_over_time_area';
export interface RiskScoreOverTimeProps {
from: string;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/risk_score_over_time/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/translations.ts
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx
index 05fa3c18809a6f..9b3a362e634953 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx
@@ -9,7 +9,7 @@ import React from 'react';
import { EuiInMemoryTable } from '@elastic/eui';
import type { EuiBasicTableColumn } from '@elastic/eui';
import type { RiskSeverity } from '../../../common/search_strategy';
-import { RiskScoreLevel } from '../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from './severity/common';
import { HostDetailsLink, UserDetailsLink } from '../../common/components/links';
import {
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx
similarity index 89%
rename from x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx
index a02c7729f4e0ae..9bb495118385f0 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx
@@ -8,12 +8,12 @@
import React from 'react';
import { render } from '@testing-library/react';
-import { TestProviders } from '../../../mock';
+import { TestProviders } from '../../common/mock';
import type { RiskEntity } from './risk_summary';
-import * as i18n from './translations';
+import * as i18n from '../../common/components/event_details/cti_details/translations';
import { RiskSummary } from './risk_summary';
-import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
-import { getEmptyValue } from '../../empty_value';
+import { RiskScoreEntity, RiskSeverity } from '../../../common/search_strategy';
+import { getEmptyValue } from '../../common/components/empty_value';
describe.each([RiskScoreEntity.host, RiskScoreEntity.user])(
'RiskSummary entityType: %s',
diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx
similarity index 80%
rename from x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx
index e0bc4637ea54b8..df3933ae50e5b7 100644
--- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/risk_summary.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.tsx
@@ -8,15 +8,18 @@
import React from 'react';
import { EuiLoadingSpinner, EuiPanel } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
-import * as i18n from './translations';
-import { EnrichedDataRow, ThreatSummaryPanelHeader } from './threat_summary_view';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
-import type { RiskSeverity } from '../../../../../common/search_strategy';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import type { HostRisk, UserRisk } from '../../../../explore/containers/risk_score';
-import { getEmptyValue } from '../../empty_value';
-import { RiskScoreDocLink } from '../../../../explore/components/risk_score/risk_score_onboarding/risk_score_doc_link';
-import { RiskScoreHeaderTitle } from '../../../../explore/components/risk_score/risk_score_onboarding/risk_score_header_title';
+import * as i18n from '../../common/components/event_details/cti_details/translations';
+import {
+ EnrichedDataRow,
+ ThreatSummaryPanelHeader,
+} from '../../common/components/event_details/cti_details/threat_summary_view';
+import { RiskScoreLevel } from './severity/common';
+import type { RiskSeverity } from '../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../common/search_strategy';
+import { getEmptyValue } from '../../common/components/empty_value';
+import { RiskScoreDocLink } from './risk_score_onboarding/risk_score_doc_link';
+import { RiskScoreHeaderTitle } from './risk_score_onboarding/risk_score_header_title';
+import type { HostRisk, UserRisk } from '../api/types';
interface HostRiskEntity {
originalRisk?: RiskSeverity | undefined;
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx
similarity index 86%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx
index 06828be073f32f..0f00959845ff3e 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.stories.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.stories.tsx
@@ -9,8 +9,8 @@ import React from 'react';
import type { Story } from '@storybook/react';
import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context';
import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
-import { StorybookProviders } from '../../../../common/mock/storybook_providers';
-import { mockRiskScoreState } from '../../../../timelines/components/side_panel/new_user_detail/__mocks__';
+import { StorybookProviders } from '../../../common/mock/storybook_providers';
+import { mockRiskScoreState } from '../../../timelines/components/side_panel/new_user_detail/__mocks__';
import { RiskSummary } from './risk_summary';
export default {
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.test.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.test.tsx
index fb89ef61ad79f6..5862ac7cd94640 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.test.tsx
@@ -7,11 +7,11 @@
import { render } from '@testing-library/react';
import React from 'react';
-import { TestProviders } from '../../../../common/mock';
-import { mockRiskScoreState } from '../../user_right/mocks';
+import { TestProviders } from '../../../common/mock';
+import { mockRiskScoreState } from '../../../flyout/entity_details/user_right/mocks';
import { RiskSummary } from './risk_summary';
-jest.mock('../../../../common/components/visualization_actions/visualization_embeddable');
+jest.mock('../../../common/components/visualization_actions/visualization_embeddable');
describe('RiskSummary', () => {
it('renders risk summary table', () => {
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx
index 09c57cc61e02b5..50259cd6ca1340 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx
@@ -21,15 +21,15 @@ import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { euiThemeVars } from '@kbn/ui-theme';
import { i18n } from '@kbn/i18n';
-import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
-import { ONE_WEEK_IN_HOURS } from '../../../../timelines/components/side_panel/new_user_detail/constants';
-import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';
-import { RiskScoreEntity } from '../../../../../common/entity_analytics/risk_engine';
-import type { RiskScoreState } from '../../../../explore/containers/risk_score';
-import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
-import { getRiskScoreSummaryAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary';
-import { ExpandablePanel } from '../../../shared/components/expandable_panel';
-import { UserDetailsLeftPanelTab } from '../../user_detais_left/tabs';
+import { UserDetailsLeftPanelTab } from '../../../flyout/entity_details/user_details_left/tabs';
+import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect';
+import { ONE_WEEK_IN_HOURS } from '../../../timelines/components/side_panel/new_user_detail/constants';
+import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date';
+import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine';
+import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
+import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel';
+import type { RiskScoreState } from '../../api/hooks/use_risk_score';
+import { getRiskScoreSummaryAttributes } from '../../lens_attributes/risk_score_summary';
export interface RiskSummaryProps {
riskScoreData: RiskScoreState;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx
similarity index 93%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx
index 9068c01a760ca3..c9b75129bb0a27 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx
@@ -8,15 +8,15 @@
import { render } from '@testing-library/react';
import React from 'react';
-import { TestProviders } from '../../../../../common/mock';
+import { TestProviders } from '../../../../common/mock';
import type { EuiHealthProps } from '@elastic/eui';
import { EuiHealth } from '@elastic/eui';
import { euiThemeVars } from '@kbn/ui-theme';
-import { RiskSeverity } from '../../../../../../common/search_strategy';
+import { RiskSeverity } from '../../../../../common/search_strategy';
import { RiskScoreLevel } from '.';
-import { SEVERITY_COLOR } from '../../../../../overview/components/detection_response/utils';
+import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils';
jest.mock('@elastic/eui', () => {
const original = jest.requireActual('@elastic/eui');
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.tsx
similarity index 87%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.tsx
index 9430690394b49d..0706eb117c5d74 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/common/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.tsx
@@ -12,9 +12,9 @@ import { EuiHealth, transparentize } from '@elastic/eui';
import styled, { css } from 'styled-components';
import { euiLightVars } from '@kbn/ui-theme';
-import { RISK_SEVERITY_COLOUR } from '../../../../../entity_analytics/common/utils';
-import { WithHoverActions } from '../../../../../common/components/with_hover_actions';
-import type { RiskSeverity } from '../../../../../../common/search_strategy';
+import { RISK_SEVERITY_COLOUR } from '../../../common/utils';
+import { WithHoverActions } from '../../../../common/components/with_hover_actions';
+import type { RiskSeverity } from '../../../../../common/search_strategy';
const RiskBadge = styled.div<{ $severity: RiskSeverity; $hideBackgroundColor: boolean }>`
${({ theme, $severity, $hideBackgroundColor }) => css`
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_badges.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_badges.tsx
similarity index 91%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_badges.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_badges.tsx
index 53c7e270f2e314..373a8dd5a56932 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_badges.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_badges.tsx
@@ -7,8 +7,8 @@
import { EuiFlexGroup, EuiNotificationBadge, EuiFlexItem } from '@elastic/eui';
import React from 'react';
-import { RISK_SEVERITY_COLOUR } from '../../../../entity_analytics/common/utils';
-import type { RiskSeverity } from '../../../../../common/search_strategy';
+import { RISK_SEVERITY_COLOUR } from '../../common/utils';
+import type { RiskSeverity } from '../../../../common/search_strategy';
import { RiskScoreLevel } from './common';
import type { SeverityCount } from './types';
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_bar.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_bar.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_bar.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_bar.tsx
index 847488869bcd3b..149d8f2cf74b94 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_bar.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_bar.tsx
@@ -9,8 +9,8 @@ import styled from 'styled-components';
import { EuiColorPaletteDisplay } from '@elastic/eui';
import React, { useMemo } from 'react';
-import { RISK_SEVERITY_COLOUR } from '../../../../entity_analytics/common/utils';
-import type { RiskSeverity } from '../../../../../common/search_strategy';
+import { RISK_SEVERITY_COLOUR } from '../../common/utils';
+import type { RiskSeverity } from '../../../../common/search_strategy';
import type { SeverityCount } from './types';
const StyledEuiColorPaletteDisplay = styled(EuiColorPaletteDisplay)`
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx
similarity index 91%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx
index 07297f9af4042e..ef978fdf80546b 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx
@@ -7,12 +7,12 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { SeverityFilterGroup } from './severity_filter_group';
-import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
-import { TestProviders } from '../../../../common/mock';
-import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock';
+import { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock';
const mockedTelemetry = createTelemetryServiceMock();
-jest.mock('../../../../common/lib/kibana', () => {
+jest.mock('../../../common/lib/kibana', () => {
return {
useKibana: () => ({
services: {
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.tsx
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.tsx
index 1ca7020bc818df..c5373199192a4d 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/severity_filter_group.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.tsx
@@ -16,12 +16,12 @@ import {
useEuiTheme,
} from '@elastic/eui';
-import { SEVERITY_UI_SORT_ORDER } from '../../../../entity_analytics/common/utils';
-import type { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy';
+import { SEVERITY_UI_SORT_ORDER } from '../../common/utils';
+import type { RiskScoreEntity, RiskSeverity } from '../../../../common/search_strategy';
import type { SeverityCount } from './types';
import { RiskScoreLevel } from './common';
-import { ENTITY_RISK_LEVEL } from '../translations';
-import { useKibana } from '../../../../common/lib/kibana';
+import { ENTITY_RISK_LEVEL } from '../risk_score/translations';
+import { useKibana } from '../../../common/lib/kibana';
interface SeverityItems {
risk: RiskSeverity;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/types.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/types.ts
similarity index 80%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/severity/types.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/severity/types.ts
index c4760b55b6a838..0c161ddd814702 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/severity/types.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/types.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import type { RiskSeverity } from '../../../../../common/search_strategy';
+import type { RiskSeverity } from '../../../../common/search_strategy';
export type SeverityCount = {
[k in RiskSeverity]: number;
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/styled_basic_table.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/styled_basic_table.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/styled_basic_table.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/styled_basic_table.tsx
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.test.tsx
similarity index 91%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.test.tsx
index 8b1f3e7cfffff7..710b5427a7479d 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.test.tsx
@@ -8,11 +8,10 @@
import { render } from '@testing-library/react';
import React from 'react';
import { TopRiskScoreContributors } from '.';
-import { TestProviders } from '../../../../common/mock';
-import type { RuleRisk } from '../../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import type { RuleRisk } from '../../../../common/search_strategy';
-jest.mock('../../../../common/containers/query_toggle');
-jest.mock('../../../containers/risk_score');
+jest.mock('../../../common/containers/query_toggle');
const testProps = {
riskScore: [],
setQuery: jest.fn(),
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.tsx
similarity index 91%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.tsx
index 9f33fc9b7bc278..587f3a60824691 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/index.tsx
@@ -10,13 +10,13 @@ import React, { useMemo } from 'react';
import type { EuiTableFieldDataColumnType } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiInMemoryTable } from '@elastic/eui';
-import { HeaderSection } from '../../../../common/components/header_section';
-import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
+import { HeaderSection } from '../../../common/components/header_section';
+import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect';
import * as i18n from './translations';
-import type { RuleRisk } from '../../../../../common/search_strategy';
+import type { RuleRisk } from '../../../../common/search_strategy';
-import { RuleLink } from '../../../../detection_engine/rule_management_ui/components/rules_table/use_columns';
+import { RuleLink } from '../../../detection_engine/rule_management_ui/components/rules_table/use_columns';
export interface TopRiskScoreContributorsProps {
loading: boolean;
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors/translations.ts
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors_alerts/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx
similarity index 78%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors_alerts/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx
index 4223acb1f44d4a..eac4de0fecf1c2 100644
--- a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors_alerts/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/index.tsx
@@ -10,21 +10,21 @@ import { TableId } from '@kbn/securitysolution-data-table';
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import type { Filter } from '@kbn/es-query';
-import { HeaderSection } from '../../../../common/components/header_section';
+import { HeaderSection } from '../../../common/components/header_section';
import * as i18n from './translations';
-import type { RiskInputs } from '../../../../../common/entity_analytics/risk_engine';
-import { RiskScoreEntity } from '../../../../../common/entity_analytics/risk_engine';
-import type { HostRiskScore, UserRiskScore } from '../../../../../common/search_strategy';
-import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../../common/constants';
-import { AlertsTableComponent } from '../../../../detections/components/alerts_table';
-import { GroupedAlertsTable } from '../../../../detections/components/alerts_table/alerts_grouping';
-import { useGlobalTime } from '../../../../common/containers/use_global_time';
-import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
-import { inputsSelectors } from '../../../../common/store/inputs';
-import { useUserData } from '../../../../detections/components/user_info';
-import { useSourcererDataView } from '../../../../common/containers/sourcerer';
-import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
+import type { RiskInputs } from '../../../../common/entity_analytics/risk_engine';
+import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine';
+import type { HostRiskScore, UserRiskScore } from '../../../../common/search_strategy';
+import { ALERTS_TABLE_REGISTRY_CONFIG_IDS } from '../../../../common/constants';
+import { AlertsTableComponent } from '../../../detections/components/alerts_table';
+import { GroupedAlertsTable } from '../../../detections/components/alerts_table/alerts_grouping';
+import { useGlobalTime } from '../../../common/containers/use_global_time';
+import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
+import { inputsSelectors } from '../../../common/store/inputs';
+import { useUserData } from '../../../detections/components/user_info';
+import { useSourcererDataView } from '../../../common/containers/sourcerer';
+import { SourcererScopeName } from '../../../common/store/sourcerer/model';
import { RiskInformationButtonEmpty } from '../risk_information';
export interface TopRiskScoreContributorsAlertsProps {
diff --git a/x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors_alerts/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/components/risk_score/top_risk_score_contributors_alerts/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/top_risk_score_contributors_alerts/translations.ts
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.test.tsx
similarity index 81%
rename from x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.test.tsx
index 78406353fb477a..3103a8437f384c 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.test.tsx
@@ -7,15 +7,17 @@
import React from 'react';
import { render } from '@testing-library/react';
-import { TestProviders } from '../../../../common/mock';
-import { useRiskScore, useRiskScoreKpi } from '../../../containers/risk_score';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
+import { TestProviders } from '../../common/mock';
+import { useQueryToggle } from '../../common/containers/query_toggle';
import { UserRiskScoreQueryTabBody } from './user_risk_score_tab_body';
-import { UsersType } from '../../store/model';
+import { UsersType } from '../../explore/users/store/model';
+import { useRiskScore } from '../api/hooks/use_risk_score';
+import { useRiskScoreKpi } from '../api/hooks/use_risk_score_kpi';
-jest.mock('../../../containers/risk_score');
-jest.mock('../../../../common/containers/query_toggle');
-jest.mock('../../../../common/lib/kibana');
+jest.mock('../api/hooks/use_risk_score_kpi');
+jest.mock('../api/hooks/use_risk_score');
+jest.mock('../../common/containers/query_toggle');
+jest.mock('../../common/lib/kibana');
describe('All users query tab body', () => {
const mockUseRiskScore = useRiskScore as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.tsx
similarity index 76%
rename from x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.tsx
index 4097d673f39828..c216d3786434e1 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/user_risk_score_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_tab_body.tsx
@@ -8,24 +8,21 @@
import React, { useEffect, useMemo, useState } from 'react';
import { noop } from 'lodash/fp';
-import { EnableRiskScore } from '../../../components/risk_score/enable_risk_score';
-import type { UsersComponentsQueryProps } from './types';
-import { manageQuery } from '../../../../common/components/page/manage_query';
-import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
-import type { State } from '../../../../common/store';
-
-import { UserRiskScoreTable } from '../../components/user_risk_score_table';
-import { usersSelectors } from '../../store';
-import {
- UserRiskScoreQueryId,
- useRiskScore,
- useRiskScoreKpi,
-} from '../../../containers/risk_score';
-import { useQueryToggle } from '../../../../common/containers/query_toggle';
-import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskScoresNoDataDetected } from '../../../components/risk_score/risk_score_onboarding/risk_score_no_data_detected';
-import { useRiskEngineStatus } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
-import { RiskScoreUpdatePanel } from '../../../../entity_analytics/components/risk_score_update_panel';
+import { useRiskScoreKpi } from '../api/hooks/use_risk_score_kpi';
+import { useRiskScore } from '../api/hooks/use_risk_score';
+import { UserRiskScoreQueryId } from '../common/utils';
+import { EnableRiskScore } from './enable_risk_score';
+import type { UsersComponentsQueryProps } from '../../explore/users/pages/navigation/types';
+import { manageQuery } from '../../common/components/page/manage_query';
+import { useDeepEqualSelector } from '../../common/hooks/use_selector';
+import type { State } from '../../common/store';
+import { UserRiskScoreTable } from './user_risk_score_table';
+import { usersSelectors } from '../../explore/users/store';
+import { useQueryToggle } from '../../common/containers/query_toggle';
+import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../common/search_strategy';
+import { RiskScoresNoDataDetected } from './risk_score_onboarding/risk_score_no_data_detected';
+import { useRiskEngineStatus } from '../api/hooks/use_risk_engine_status';
+import { RiskScoreUpdatePanel } from './risk_score_update_panel';
const UserRiskScoreTableManage = manageQuery(UserRiskScoreTable);
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.test.tsx
similarity index 93%
rename from x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.test.tsx
index 3f9fe129815098..5fbbcf30d13bfb 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.test.tsx
@@ -8,8 +8,8 @@ import React from 'react';
import { render } from '@testing-library/react';
import type { UserRiskScoreColumns } from '.';
import { getUserRiskScoreColumns } from './columns';
-import { TestProviders } from '../../../../common/mock';
-import { RiskScoreFields } from '../../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import { RiskScoreFields } from '../../../../common/search_strategy';
describe('getUserRiskScoreColumns', () => {
const defaultProps = {
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.tsx
similarity index 77%
rename from x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.tsx
index a23d62d0e83d5a..49eaf7b3cc26ba 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/columns.tsx
@@ -11,19 +11,19 @@ import {
SecurityCellActions,
SecurityCellActionsTrigger,
CellActionsMode,
-} from '../../../../common/components/cell_actions';
-import { escapeDataProviderId } from '../../../../common/components/drag_and_drop/helpers';
-import { getEmptyTagValue } from '../../../../common/components/empty_value';
+} from '../../../common/components/cell_actions';
+import { escapeDataProviderId } from '../../../common/components/drag_and_drop/helpers';
+import { getEmptyTagValue } from '../../../common/components/empty_value';
import type { UserRiskScoreColumns } from '.';
import * as i18n from './translations';
-import { RiskScoreLevel } from '../../../components/risk_score/severity/common';
-import type { Maybe, RiskSeverity } from '../../../../../common/search_strategy';
-import { RiskScoreEntity, RiskScoreFields } from '../../../../../common/search_strategy';
-import { UserDetailsLink } from '../../../../common/components/links';
-import { UsersTableType } from '../../store/model';
-import { ENTITY_RISK_LEVEL } from '../../../components/risk_score/translations';
-import { CELL_ACTIONS_TELEMETRY } from '../../../components/risk_score/constants';
-import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';
+import { RiskScoreLevel } from '../severity/common';
+import type { Maybe, RiskSeverity } from '../../../../common/search_strategy';
+import { RiskScoreEntity, RiskScoreFields } from '../../../../common/search_strategy';
+import { UserDetailsLink } from '../../../common/components/links';
+import { UsersTableType } from '../../../explore/users/store/model';
+import { ENTITY_RISK_LEVEL } from '../risk_score/translations';
+import { CELL_ACTIONS_TELEMETRY } from '../risk_score/constants';
+import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date';
export const getUserRiskScoreColumns = ({
dispatchSeverityUpdate,
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.test.tsx
similarity index 83%
rename from x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.test.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.test.tsx
index 2d3cedad6bea11..87905b248a7d97 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.test.tsx
@@ -9,10 +9,10 @@ import { render } from '@testing-library/react';
import { noop } from 'lodash';
import React from 'react';
import { UserRiskScoreTable } from '.';
-import type { UserRiskScore } from '../../../../../common/search_strategy';
-import { RiskSeverity } from '../../../../../common/search_strategy';
-import { TestProviders } from '../../../../common/mock';
-import { UsersType } from '../../store/model';
+import type { UserRiskScore } from '../../../../common/search_strategy';
+import { RiskSeverity } from '../../../../common/search_strategy';
+import { TestProviders } from '../../../common/mock';
+import { UsersType } from '../../../explore/users/store/model';
describe('UserRiskScoreTable', () => {
const username = 'test_user_name';
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.tsx
similarity index 83%
rename from x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.tsx
index 01a2df82b40c94..6a99e64d04b279 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/index.tsx
@@ -9,29 +9,29 @@ import React, { useMemo, useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import type { Columns, Criteria, ItemsPerRow } from '../../../components/paginated_table';
-import { PaginatedTable } from '../../../components/paginated_table';
+import type { Columns, Criteria, ItemsPerRow } from '../../../explore/components/paginated_table';
+import { PaginatedTable } from '../../../explore/components/paginated_table';
import { getUserRiskScoreColumns } from './columns';
-import * as i18nUsers from '../../pages/translations';
+import * as i18nUsers from '../../../explore/users/pages/translations';
import * as i18n from './translations';
-import { usersModel, usersSelectors, usersActions } from '../../store';
-import type { UserRiskScoreItem } from '../../../../../common/search_strategy/security_solution/users/common';
-import type { SeverityCount } from '../../../components/risk_score/severity/types';
-import { SeverityBadges } from '../../../components/risk_score/severity/severity_badges';
-import { SeverityBar } from '../../../components/risk_score/severity/severity_bar';
-import { SeverityFilterGroup } from '../../../components/risk_score/severity/severity_filter_group';
-import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
-import type { State } from '../../../../common/store';
+import { usersModel, usersSelectors, usersActions } from '../../../explore/users/store';
+import type { UserRiskScoreItem } from '../../../../common/search_strategy/security_solution/users/common';
+import type { SeverityCount } from '../severity/types';
+import { SeverityBadges } from '../severity/severity_badges';
+import { SeverityBar } from '../severity/severity_bar';
+import { SeverityFilterGroup } from '../severity/severity_filter_group';
+import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
+import type { State } from '../../../common/store';
import type {
RiskScoreFields,
RiskScoreSortField,
RiskSeverity,
UserRiskScore,
-} from '../../../../../common/search_strategy';
-import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskInformationButtonEmpty } from '../../../components/risk_score/risk_information';
+} from '../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
+import { RiskInformationButtonEmpty } from '../risk_information';
export const rowItems: ItemsPerRow[] = [
{
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/components/user_risk_score_table/translations.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/index.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/index.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/index.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/ingest_pipelines.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/ingest_pipelines.test.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/ingest_pipelines.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/ingest_pipelines.test.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/ingest_pipelines.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/ingest_pipelines.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/ingest_pipelines.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/ingest_pipelines.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/onboarding.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/onboarding.ts
similarity index 94%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/onboarding.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/onboarding.ts
index a8578d4d53f7e4..ff6344679455f2 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/onboarding.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/onboarding.ts
@@ -7,9 +7,9 @@
import type { HttpSetup, NotificationsStart } from '@kbn/core/public';
-import { INTERNAL_RISK_SCORE_URL } from '../../../../../../common/constants';
+import { INTERNAL_RISK_SCORE_URL } from '../../../../common/constants';
-import { RiskScoreEntity } from '../../../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
import {
HOST_RISK_SCORES_ENABLED_TITLE,
INSTALLATION_ERROR,
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/saved_objects.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/saved_objects.ts
similarity index 96%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/saved_objects.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/saved_objects.ts
index 6e5369f2337f90..4c3f863ddbeae7 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/saved_objects.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/saved_objects.ts
@@ -11,13 +11,13 @@ import type { DashboardStart } from '@kbn/dashboard-plugin/public';
import {
RISKY_HOSTS_DASHBOARD_TITLE,
RISKY_USERS_DASHBOARD_TITLE,
-} from '../../../../components/risk_score/constants';
+} from '../../components/risk_score/constants';
import {
prebuiltSavedObjectsBulkCreateUrl,
prebuiltSavedObjectsBulkDeleteUrl,
-} from '../../../../../../common/constants';
+} from '../../../../common/constants';
-import { RiskScoreEntity } from '../../../../../../common/search_strategy';
+import { RiskScoreEntity } from '../../../../common/search_strategy';
import {
DELETE_SAVED_OBJECTS_FAILURE,
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/stored_scripts.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/stored_scripts.test.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/stored_scripts.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/stored_scripts.test.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/stored_scripts.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/stored_scripts.ts
similarity index 97%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/stored_scripts.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/stored_scripts.ts
index 6a7fa486bc729e..243106325982a4 100644
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/stored_scripts.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/stored_scripts.ts
@@ -9,7 +9,7 @@ import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import {
RISK_SCORE_CREATE_STORED_SCRIPT,
RISK_SCORE_DELETE_STORED_SCRIPT,
-} from '../../../../../../common/constants';
+} from '../../../../common/constants';
import {
STORED_SCRIPT_CREATION_ERROR_MESSAGE,
STORED_SCRIPT_DELETION_ERROR_MESSAGE,
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/transforms.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/transforms.test.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/transforms.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/transforms.test.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/transforms.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/transforms.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/transforms.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/transforms.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/translations.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/translations.ts
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/types.ts b/x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/types.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/explore/containers/risk_score/onboarding/api/types.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/deprecated_risk_engine/api/types.ts
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_donut.test.ts.snap b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_donut.test.ts.snap
similarity index 100%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_donut.test.ts.snap
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_donut.test.ts.snap
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_over_time_area.test.ts.snap
similarity index 100%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_over_time_area.test.ts.snap
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_over_time_area.test.ts.snap
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_summary.test.ts.snap b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_summary.test.ts.snap
similarity index 100%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/__snapshots__/risk_score_summary.test.ts.snap
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/__snapshots__/risk_score_summary.test.ts.snap
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.test.ts
similarity index 82%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.test.ts
index df536a7e33d179..00b91ceb44e2b9 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.test.ts
@@ -6,13 +6,12 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { wrapper } from '../../../mocks';
-
-import { useLensAttributes } from '../../../use_lens_attributes';
+import { wrapper } from '../../common/components/visualization_actions/mocks';
+import { useLensAttributes } from '../../common/components/visualization_actions/use_lens_attributes';
import { getRiskScoreDonutAttributes } from './risk_score_donut';
-jest.mock('../../../../../containers/sourcerer', () => ({
+jest.mock('../../common/containers/sourcerer', () => ({
useSourcererDataView: jest.fn().mockReturnValue({
selectedPatterns: ['auditbeat-mytest-*'],
dataViewId: 'security-solution-my-test',
@@ -20,7 +19,7 @@ jest.mock('../../../../../containers/sourcerer', () => ({
}),
}));
-jest.mock('../../../../../utils/route/use_route_spy', () => ({
+jest.mock('../../common/utils/route/use_route_spy', () => ({
useRouteSpy: jest.fn().mockReturnValue([
{
detailName: 'undefined',
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.ts
similarity index 98%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.ts
index 5260a98c5b8740..dccdab8984607a 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_donut.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_donut.ts
@@ -6,7 +6,7 @@
*/
import { v4 as uuidv4 } from 'uuid';
-import type { GetLensAttributes } from '../../../types';
+import type { GetLensAttributes } from '../../common/components/visualization_actions/types';
const internalReferenceIdMapping: Record = { host: uuidv4(), user: uuidv4() };
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.test.ts
similarity index 89%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.test.ts
index 97dbe4ea17e48b..5a642e804befd2 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.test.ts
@@ -7,13 +7,12 @@
import { renderHook } from '@testing-library/react-hooks';
import type { XYState } from '@kbn/lens-plugin/public';
-import { wrapper } from '../../../mocks';
-
-import { useLensAttributes } from '../../../use_lens_attributes';
import { getRiskScoreOverTimeAreaAttributes } from './risk_score_over_time_area';
+import { useLensAttributes } from '../../common/components/visualization_actions/use_lens_attributes';
+import { wrapper } from '../../common/components/visualization_actions/mocks';
-jest.mock('../../../../../containers/sourcerer', () => ({
+jest.mock('../../common/containers/sourcerer', () => ({
useSourcererDataView: jest.fn().mockReturnValue({
selectedPatterns: ['auditbeat-mytest-*'],
dataViewId: 'security-solution-my-test',
@@ -21,7 +20,7 @@ jest.mock('../../../../../containers/sourcerer', () => ({
}),
}));
-jest.mock('../../../../../utils/route/use_route_spy', () => ({
+jest.mock('../../common/utils/route/use_route_spy', () => ({
useRouteSpy: jest.fn().mockReturnValue([
{
detailName: 'mockHost',
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.ts
similarity index 98%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.ts
index b100e5042a33a5..af76c9282873df 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_over_time_area.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_over_time_area.ts
@@ -6,7 +6,7 @@
*/
import { v4 as uuidv4 } from 'uuid';
-import type { GetLensAttributes } from '../../../types';
+import type { GetLensAttributes } from '../../common/components/visualization_actions/types';
const internalReferenceIdMapping: Record = { host: uuidv4(), user: uuidv4() };
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts
similarity index 85%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts
index e8af897c51cea1..6ef0abbf77693d 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts
@@ -5,15 +5,15 @@
* 2.0.
*/
-import { RiskScoreEntity } from '../../../../../../../common/entity_analytics/risk_engine';
+import { RiskScoreEntity } from '../../../common/entity_analytics/risk_engine';
import { renderHook } from '@testing-library/react-hooks';
-import { wrapper } from '../../../mocks';
-import { useLensAttributes } from '../../../use_lens_attributes';
import { getRiskScoreSummaryAttributes } from './risk_score_summary';
-import { RiskSeverity } from '../../../../../../../common/search_strategy';
+import { RiskSeverity } from '../../../common/search_strategy';
import type { MetricVisualizationState } from '@kbn/lens-plugin/public';
+import { wrapper } from '../../common/components/visualization_actions/mocks';
+import { useLensAttributes } from '../../common/components/visualization_actions/use_lens_attributes';
-jest.mock('../../../../../containers/sourcerer', () => ({
+jest.mock('../../common/containers/sourcerer', () => ({
useSourcererDataView: jest.fn().mockReturnValue({
selectedPatterns: ['auditbeat-mytest-*'],
dataViewId: 'security-solution-my-test',
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.ts
similarity index 93%
rename from x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.ts
rename to x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.ts
index 728c6e17713734..544badd18b5e98 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.ts
@@ -6,14 +6,10 @@
*/
import { v4 as uuidv4 } from 'uuid';
-import {
- SEVERITY_UI_SORT_ORDER,
- RISK_SEVERITY_COLOUR,
- RISK_SCORE_RANGES,
-} from '../../../../../../entity_analytics/common/utils';
-import type { RiskSeverity } from '../../../../../../../common/search_strategy';
-import { RiskScoreEntity, RiskScoreFields } from '../../../../../../../common/search_strategy';
-import type { LensAttributes } from '../../../types';
+import type { LensAttributes } from '@kbn/lens-embeddable-utils';
+import { SEVERITY_UI_SORT_ORDER, RISK_SEVERITY_COLOUR, RISK_SCORE_RANGES } from '../common/utils';
+import type { RiskSeverity } from '../../../common/search_strategy';
+import { RiskScoreEntity, RiskScoreFields } from '../../../common/search_strategy';
interface GetRiskScoreSummaryAttributesProps {
query?: string;
diff --git a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_dashboard.tsx
similarity index 92%
rename from x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx
rename to x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_dashboard.tsx
index 96476c90aa1806..76efd79b4d63d2 100644
--- a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_dashboard.tsx
@@ -7,7 +7,6 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
-import { EntityAnalyticsRiskScores } from '../components/entity_analytics/risk_score';
import { RiskScoreEntity } from '../../../common/search_strategy';
import { ENTITY_ANALYTICS } from '../../app/translations';
import { SpyRoute } from '../../common/utils/route/spy_routes';
@@ -16,15 +15,15 @@ import { useSourcererDataView } from '../../common/containers/sourcerer';
import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper';
import { HeaderPage } from '../../common/components/header_page';
import { LandingPageComponent } from '../../common/components/landing_page';
-
-import { EntityAnalyticsHeader } from '../components/entity_analytics/header';
-import { EntityAnalyticsAnomalies } from '../components/entity_analytics/anomalies';
import { SiemSearchBar } from '../../common/components/search_bar';
import { InputsModelId } from '../../common/store/inputs/constants';
import { FiltersGlobal } from '../../common/components/filters_global';
-import { useRiskEngineStatus } from '../../entity_analytics/api/hooks/use_risk_engine_status';
-import { RiskScoreUpdatePanel } from '../../entity_analytics/components/risk_score_update_panel';
+import { useRiskEngineStatus } from '../api/hooks/use_risk_engine_status';
+import { RiskScoreUpdatePanel } from '../components/risk_score_update_panel';
import { useHasSecurityCapability } from '../../helper_hooks';
+import { EntityAnalyticsHeader } from '../components/entity_analytics_header';
+import { EntityAnalyticsAnomalies } from '../components/entity_analytics_anomalies';
+import { EntityAnalyticsRiskScores } from '../components/entity_analytics_risk_score';
const EntityAnalyticsComponent = () => {
const { data: riskScoreEngineStatus } = useRiskEngineStatus();
diff --git a/x-pack/plugins/security_solution/public/explore/components/paginated_table/index.tsx b/x-pack/plugins/security_solution/public/explore/components/paginated_table/index.tsx
index 6c870c313a4128..cbee7c586bbf5d 100644
--- a/x-pack/plugins/security_solution/public/explore/components/paginated_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/paginated_table/index.tsx
@@ -41,7 +41,7 @@ import type {
} from '../../network/components/network_top_countries_table/columns';
import type { TlsColumns } from '../../network/components/tls_table/columns';
import type { UncommonProcessTableColumns } from '../../hosts/components/uncommon_process_table';
-import type { HostRiskScoreColumns } from '../../hosts/components/host_risk_score_table';
+import type { HostRiskScoreColumns } from '../../../entity_analytics/components/host_risk_score_table';
import type { UsersColumns } from '../../network/components/users_table/columns';
import { HeaderSection } from '../../../common/components/header_section';
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/translations.ts b/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/translations.ts
deleted file mode 100644
index 8cc275674d4e9f..00000000000000
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/all/translations.ts
+++ /dev/null
@@ -1,22 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_RISK_SCORE = i18n.translate(
- 'xpack.securitySolution.riskScore.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on risk score search`,
- }
-);
-
-export const FAIL_RISK_SCORE = i18n.translate(
- 'xpack.securitySolution.riskScore.failSearchDescription',
- {
- defaultMessage: `Failed to run search on risk score`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/api.ts b/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/api.ts
deleted file mode 100644
index 2cfaa2265f527c..00000000000000
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/feature_status/api.ts
+++ /dev/null
@@ -1,33 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { KibanaServices } from '../../../../common/lib/kibana';
-import { RISK_SCORE_INDEX_STATUS_API_URL } from '../../../../../common/constants';
-import type { RiskScoreEntity } from '../../../../../common/search_strategy';
-
-export const getRiskScoreIndexStatus = async (params: {
- query: {
- indexName: string;
- entity: RiskScoreEntity;
- };
- signal?: AbortSignal;
-}): Promise<{
- isDeprecated: boolean;
- isEnabled: boolean;
-}> => {
- const { indexName, entity } = params.query;
- return KibanaServices.get().http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(
- RISK_SCORE_INDEX_STATUS_API_URL,
- {
- method: 'GET',
- version: '1',
- query: { indexName, entity },
- asSystemRequest: true,
- signal: params.signal,
- }
- );
-};
diff --git a/x-pack/plugins/security_solution/public/explore/containers/risk_score/index.ts b/x-pack/plugins/security_solution/public/explore/containers/risk_score/index.ts
deleted file mode 100644
index 892bcd24dee747..00000000000000
--- a/x-pack/plugins/security_solution/public/explore/containers/risk_score/index.ts
+++ /dev/null
@@ -1,38 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type {
- HostRiskScore,
- UserRiskScore,
-} from '../../../../common/search_strategy/security_solution/risk_score';
-
-export * from './all';
-export * from './kpi';
-
-export enum UserRiskScoreQueryId {
- USERS_BY_RISK = 'UsersByRisk',
- USER_DETAILS_RISK_SCORE = 'UserDetailsRiskScore',
-}
-
-export enum HostRiskScoreQueryId {
- DEFAULT = 'HostRiskScore',
- HOST_DETAILS_RISK_SCORE = 'HostDetailsRiskScore',
- OVERVIEW_RISKY_HOSTS = 'OverviewRiskyHosts',
- HOSTS_BY_RISK = 'HostsByRisk',
-}
-
-export interface HostRisk {
- loading: boolean;
- isModuleEnabled: boolean;
- result?: HostRiskScore[];
-}
-
-export interface UserRisk {
- loading: boolean;
- isModuleEnabled: boolean;
- result?: UserRiskScore[];
-}
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx
index 292e106fa5d4d0..270a10b52155a7 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/hosts_table/columns.tsx
@@ -19,9 +19,9 @@ import type { HostsTableColumns } from '.';
import * as i18n from './translations';
import type { Maybe, RiskSeverity } from '../../../../../common/search_strategy';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { VIEW_HOSTS_BY_SEVERITY } from '../host_risk_score_table/translations';
-import { RiskScoreLevel } from '../../../components/risk_score/severity/common';
-import { ENTITY_RISK_LEVEL } from '../../../components/risk_score/translations';
+import { VIEW_HOSTS_BY_SEVERITY } from '../../../../entity_analytics/components/host_risk_score_table/translations';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
+import { ENTITY_RISK_LEVEL } from '../../../../entity_analytics/components/risk_score/translations';
export const getHostsColumns = (
showRiskColumn: boolean,
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/details/details_tabs.tsx
index cc21c96ac9405d..4a758dadc5cbd2 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/details/details_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/details/details_tabs.tsx
@@ -9,7 +9,7 @@ import React from 'react';
import { Routes, Route } from '@kbn/shared-ux-router';
import { TableId } from '@kbn/securitysolution-data-table';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskDetailsTabBody } from '../../../components/risk_score/risk_details_tab_body';
+import { RiskDetailsTabBody } from '../../../../entity_analytics/components/risk_details_tab_body';
import { HostsType, HostsTableType } from '../../store/model';
import { AnomaliesQueryTabBody } from '../../../../common/containers/anomalies/anomalies_query_tab_body';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.test.tsx
index 0ea63088628bfd..30083b2452d94c 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.test.tsx
@@ -8,12 +8,14 @@
import React from 'react';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../common/mock';
-import { useRiskScore, useRiskScoreKpi } from '../../../containers/risk_score';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { HostRiskScoreQueryTabBody } from './host_risk_score_tab_body';
import { HostsType } from '../../store/model';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
+import { useRiskScoreKpi } from '../../../../entity_analytics/api/hooks/use_risk_score_kpi';
-jest.mock('../../../containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score_kpi');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
jest.mock('../../../../common/containers/query_toggle');
jest.mock('../../../../common/lib/kibana');
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx
index 7139102bd35a55..c9ab211c8dc079 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/host_risk_score_tab_body.tsx
@@ -8,21 +8,19 @@
import React, { useEffect, useMemo, useState } from 'react';
import { EuiPanel } from '@elastic/eui';
import { noop } from 'lodash/fp';
-import { EnableRiskScore } from '../../../components/risk_score/enable_risk_score';
+import { HostRiskScoreQueryId } from '../../../../entity_analytics/common/utils';
+import { useRiskScoreKpi } from '../../../../entity_analytics/api/hooks/use_risk_score_kpi';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
+import { EnableRiskScore } from '../../../../entity_analytics/components/enable_risk_score';
import type { HostsComponentsQueryProps } from './types';
import { manageQuery } from '../../../../common/components/page/manage_query';
-import { HostRiskScoreTable } from '../../components/host_risk_score_table';
+import { HostRiskScoreTable } from '../../../../entity_analytics/components/host_risk_score_table';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
import { hostsModel, hostsSelectors } from '../../store';
import type { State } from '../../../../common/store';
-import {
- HostRiskScoreQueryId,
- useRiskScore,
- useRiskScoreKpi,
-} from '../../../containers/risk_score';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { EMPTY_SEVERITY_COUNT, RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskScoresNoDataDetected } from '../../../components/risk_score/risk_score_onboarding/risk_score_no_data_detected';
+import { RiskScoresNoDataDetected } from '../../../../entity_analytics/components/risk_score_onboarding/risk_score_no_data_detected';
import { useRiskEngineStatus } from '../../../../entity_analytics/api/hooks/use_risk_engine_status';
import { RiskScoreUpdatePanel } from '../../../../entity_analytics/components/risk_score_update_panel';
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.tsx b/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.tsx
index 7cf90c41fd7c3a..6948009ebb188b 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.tsx
@@ -27,9 +27,9 @@ import { usersActions, usersModel, usersSelectors } from '../../store';
import type { User } from '../../../../../common/search_strategy/security_solution/users/all';
import type { SortUsersField } from '../../../../../common/search_strategy/security_solution/users/common';
import type { RiskSeverity } from '../../../../../common/search_strategy';
-import { RiskScoreLevel } from '../../../components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { VIEW_USERS_BY_SEVERITY } from '../user_risk_score_table/translations';
+import { VIEW_USERS_BY_SEVERITY } from '../../../../entity_analytics/components/user_risk_score_table/translations';
import { SecurityPageName } from '../../../../app/types';
import { UsersTableType } from '../../store/model';
import { useNavigateTo } from '../../../../common/lib/kibana';
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/details/details_tabs.tsx
index d2649a33292040..9719dc0844e4ac 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/details/details_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/pages/details/details_tabs.tsx
@@ -8,7 +8,7 @@
import React from 'react';
import { Routes, Route } from '@kbn/shared-ux-router';
import { TableId } from '@kbn/securitysolution-data-table';
-import { RiskDetailsTabBody } from '../../../components/risk_score/risk_details_tab_body';
+import { RiskDetailsTabBody } from '../../../../entity_analytics/components/risk_details_tab_body';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
import { UsersTableType } from '../../store/model';
import { AnomaliesUserTable } from '../../../../common/components/ml/tables/anomalies_user_table';
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/users_tabs.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/users_tabs.tsx
index 034ee2a71ab9d7..bc6dd85e21e259 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/users_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/pages/users_tabs.tsx
@@ -16,7 +16,7 @@ import { AllUsersQueryTabBody, AuthenticationsQueryTabBody } from './navigation'
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
import { AnomaliesUserTable } from '../../../common/components/ml/tables/anomalies_user_table';
-import { UserRiskScoreQueryTabBody } from './navigation/user_risk_score_tab_body';
+import { UserRiskScoreQueryTabBody } from '../../../entity_analytics/components/user_risk_score_tab_body';
import { EventsQueryTabBody } from '../../../common/components/events_tab';
import { userNameExistsFilter } from './details/helpers';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx
index 0a8807b2fb75aa..b6aacd491a0557 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/entities_details.test.tsx
@@ -16,12 +16,12 @@ import { mockContextValue } from '../mocks/mock_context';
import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids';
import type { Anomalies } from '../../../../common/components/ml/types';
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { mockAnomalies } from '../../../../common/components/ml/mock';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
import { useHostRelatedUsers } from '../../../../common/containers/related_entities/related_users';
import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details';
import { useUserRelatedHosts } from '../../../../common/containers/related_entities/related_hosts';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
@@ -86,7 +86,7 @@ const mockUseHostDetails = useHostDetails as jest.Mock;
jest.mock('../../../../common/containers/related_entities/related_users');
const mockUseHostsRelatedUsers = useHostRelatedUsers as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseRiskScore = useRiskScore as jest.Mock;
jest.mock('../../../../explore/users/containers/users/observed_details');
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx
index b711e6d3d5f7ed..e8dfa71d0b4c21 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx
@@ -11,7 +11,6 @@ import type { Anomalies } from '../../../../common/components/ml/types';
import { TestProviders } from '../../../../common/mock';
import { HostDetails } from './host_details';
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { mockAnomalies } from '../../../../common/components/ml/mock';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
import { useHostRelatedUsers } from '../../../../common/containers/related_entities/related_users';
@@ -22,6 +21,7 @@ import {
HOST_DETAILS_RELATED_USERS_TABLE_TEST_ID,
} from './test_ids';
import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
@@ -83,7 +83,7 @@ const mockUseHostDetails = useHostDetails as jest.Mock;
jest.mock('../../../../common/containers/related_entities/related_users');
const mockUseHostsRelatedUsers = useHostRelatedUsers as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseRiskScore = useRiskScore as jest.Mock;
const timestamp = '2022-07-25T08:20:18.966Z';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx
index 3f68ef15956ede..711eb2a6500f10 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx
@@ -30,7 +30,7 @@ import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/a
import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
import { NetworkDetailsLink } from '../../../../common/components/links';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
import { InputsModelId } from '../../../../common/store/inputs/constants';
import {
@@ -49,7 +49,7 @@ import { useHostRelatedUsers } from '../../../../common/containers/related_entit
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { HOST_DETAILS_TEST_ID, HOST_DETAILS_RELATED_USERS_TABLE_TEST_ID } from './test_ids';
-import { ENTITY_RISK_LEVEL } from '../../../../explore/components/risk_score/translations';
+import { ENTITY_RISK_LEVEL } from '../../../../entity_analytics/components/risk_score/translations';
import { useHasSecurityCapability } from '../../../../helper_hooks';
const HOST_DETAILS_ID = 'entities-hosts-details';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx
index 1f2d5b464d4e97..77e42400001f3c 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx
@@ -11,7 +11,6 @@ import type { Anomalies } from '../../../../common/components/ml/types';
import { TestProviders } from '../../../../common/mock';
import { UserDetails } from './user_details';
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { mockAnomalies } from '../../../../common/components/ml/mock';
import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details';
import { useUserRelatedHosts } from '../../../../common/containers/related_entities/related_hosts';
@@ -22,6 +21,7 @@ import {
USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID,
} from './test_ids';
import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../../shared/components/test_ids';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
jest.mock('react-router-dom', () => {
const actual = jest.requireActual('react-router-dom');
@@ -80,7 +80,7 @@ const mockUseObservedUserDetails = useObservedUserDetails as jest.Mock;
jest.mock('../../../../common/containers/related_entities/related_hosts');
const mockUseUsersRelatedHosts = useUserRelatedHosts as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseRiskScore = useRiskScore as jest.Mock;
const timestamp = '2022-07-25T08:20:18.966Z';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx
index 1758bbc5b05d85..0aa2624f202ff1 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx
@@ -30,7 +30,7 @@ import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/a
import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
import { NetworkDetailsLink } from '../../../../common/components/links';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
import {
SecurityCellActions,
@@ -49,7 +49,7 @@ import { useUserRelatedHosts } from '../../../../common/containers/related_entit
import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID, USER_DETAILS_TEST_ID } from './test_ids';
-import { ENTITY_RISK_LEVEL } from '../../../../explore/components/risk_score/translations';
+import { ENTITY_RISK_LEVEL } from '../../../../entity_analytics/components/risk_score/translations';
import { useHasSecurityCapability } from '../../../../helper_hooks';
const USER_DETAILS_ID = 'entities-users-details';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx
index 117420358ded54..6388d5f8fb85dc 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/entities_overview.test.tsx
@@ -15,7 +15,6 @@ import {
} from './test_ids';
import { EntitiesOverview } from './entities_overview';
import { TestProviders } from '../../../../common/mock';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { useFirstLastSeen } from '../../../../common/containers/use_first_last_seen';
import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
@@ -26,6 +25,7 @@ import {
EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID,
EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID,
} from '../../../shared/components/test_ids';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
const from = '2022-04-05T12:00:00.000Z';
const to = '2022-04-08T12:00:00.;000Z';
@@ -49,7 +49,7 @@ const mockUseUserDetails = useObservedUserDetails as jest.Mock;
jest.mock('../../../../explore/users/containers/users/observed_details');
const mockUseRiskScore = useRiskScore as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseFirstLastSeen = useFirstLastSeen as jest.Mock;
jest.mock('../../../../common/containers/use_first_last_seen');
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx
index 36fb3731943c4a..f7fc4c2a22d90a 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.test.tsx
@@ -8,7 +8,6 @@ import React from 'react';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../common/mock';
import { HostEntityOverview } from './host_entity_overview';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
import { useFirstLastSeen } from '../../../../common/containers/use_first_last_seen';
import {
@@ -25,6 +24,7 @@ import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/co
import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
const hostName = 'host';
const osFamily = 'Windows';
@@ -63,7 +63,7 @@ const mockUseHostDetails = useHostDetails as jest.Mock;
jest.mock('../../../../explore/hosts/containers/hosts/details');
const mockUseRiskScore = useRiskScore as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseFirstLastSeen = useFirstLastSeen as jest.Mock;
jest.mock('../../../../common/containers/use_first_last_seen');
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx
index a1d42871a42e4f..dd90245a97f707 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/host_entity_overview.tsx
@@ -19,6 +19,7 @@ import { css } from '@emotion/css';
import { getOr } from 'lodash/fp';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { useRightPanelContext } from '../context';
import type { DescriptionList } from '../../../../../common/utility_types';
import {
@@ -30,10 +31,9 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
import { DescriptionListStyled } from '../../../../common/components/page';
import { OverviewDescriptionList } from '../../../../common/components/overview_description_list';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { useSourcererDataView } from '../../../../common/containers/sourcerer';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
import {
FAMILY,
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx
index bb776547d2a9fe..bef01182954700 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx
@@ -10,7 +10,6 @@ import { render } from '@testing-library/react';
import { RightPanelContext } from '../context';
import { INSIGHTS_HEADER_TEST_ID } from './test_ids';
import { TestProviders } from '../../../../common/mock';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { useFirstLastSeen } from '../../../../common/containers/use_first_last_seen';
import { useObservedUserDetails } from '../../../../explore/users/containers/users/observed_details';
import { useHostDetails } from '../../../../explore/hosts/containers/hosts/details';
@@ -20,6 +19,7 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { InsightsSection } from './insights_section';
import { useAlertPrevalence } from '../../../../common/containers/alerts/use_alert_prevalence';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
jest.mock('../../../../common/containers/alerts/use_alert_prevalence');
@@ -69,7 +69,7 @@ const mockUseUserDetails = useObservedUserDetails as jest.Mock;
jest.mock('../../../../explore/users/containers/users/observed_details');
const mockUseRiskScore = useRiskScore as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseFirstLastSeen = useFirstLastSeen as jest.Mock;
jest.mock('../../../../common/containers/use_first_last_seen');
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx
index c8673f41376f44..b36c21faf3f736 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.test.tsx
@@ -8,7 +8,6 @@ import React from 'react';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../common/mock';
import { UserEntityOverview } from './user_entity_overview';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
import { useFirstLastSeen } from '../../../../common/containers/use_first_last_seen';
import {
ENTITIES_USER_OVERVIEW_DOMAIN_TEST_ID,
@@ -25,6 +24,7 @@ import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
import { RightPanelContext } from '../context';
import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
const userName = 'user';
const domain = 'n54bg2lfc7';
@@ -63,7 +63,7 @@ const mockUseUserDetails = useObservedUserDetails as jest.Mock;
jest.mock('../../../../explore/users/containers/users/observed_details');
const mockUseRiskScore = useRiskScore as jest.Mock;
-jest.mock('../../../../explore/containers/risk_score');
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score');
const mockUseFirstLastSeen = useFirstLastSeen as jest.Mock;
jest.mock('../../../../common/containers/use_first_last_seen');
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx
index 313719ec1c0bac..34768540e93376 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/user_entity_overview.tsx
@@ -32,10 +32,11 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
import { DescriptionListStyled } from '../../../../common/components/page';
import { OverviewDescriptionList } from '../../../../common/components/overview_description_list';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { useSourcererDataView } from '../../../../common/containers/sourcerer';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
-import { useRiskScore } from '../../../../explore/containers/risk_score';
+import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
+
import {
USER_DOMAIN,
LAST_SEEN,
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/content.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/content.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/content.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/header.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/header.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/header.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/index.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx
similarity index 84%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx
index f86dc25ffe2195..61f408a5c0ade3 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx
@@ -9,14 +9,14 @@ import type { ReactElement } from 'react';
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
+import { getRiskInputTab } from '../../../entity_analytics/components/entity_details_flyout';
import { UserAssetTableType } from '../../../explore/users/store/model';
import { ManagedUserDatasetKey } from '../../../../common/search_strategy/security_solution/users/managed_details';
import type {
ManagedUserHits,
ManagedUserHit,
} from '../../../../common/search_strategy/security_solution/users/managed_details';
-import { ENTRA_TAB_TEST_ID, OKTA_TAB_TEST_ID, RISK_INPUTS_TAB_TEST_ID } from './test_ids';
-import { RiskInputsTab } from './tabs/risk_inputs';
+import { ENTRA_TAB_TEST_ID, OKTA_TAB_TEST_ID } from './test_ids';
import { AssetDocumentTab } from './tabs/asset_document';
import { RightPanelProvider } from '../../document_details/right/context';
@@ -54,18 +54,6 @@ export const useTabs = (managedUser: ManagedUserHits, alertIds: string[]): LeftP
return tabs;
}, [alertIds, managedUser]);
-const getRiskInputTab = (alertIds: string[]) => ({
- id: UserDetailsLeftPanelTab.RISK_INPUTS,
- 'data-test-subj': RISK_INPUTS_TAB_TEST_ID,
- name: (
-
- ),
- content: ,
-});
-
const getOktaTab = (oktaManagedUser: ManagedUserHit) => ({
id: UserDetailsLeftPanelTab.OKTA,
'data-test-subj': OKTA_TAB_TEST_ID,
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/asset_document.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.test.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/asset_document.test.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.test.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/asset_document.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/asset_document.tsx
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/asset_document.tsx
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/test_ids.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/tabs/test_ids.ts
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs/test_ids.ts
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/test_ids.ts
similarity index 85%
rename from x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/test_ids.ts
rename to x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/test_ids.ts
index b67efe48dca673..1b1bf00b48401e 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_detais_left/test_ids.ts
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/test_ids.ts
@@ -7,6 +7,5 @@
import { PREFIX } from '../../shared/test_ids';
-export const RISK_INPUTS_TAB_TEST_ID = `${PREFIX}RiskInputsTab` as const;
export const OKTA_TAB_TEST_ID = `${PREFIX}OktaTab` as const;
export const ENTRA_TAB_TEST_ID = `${PREFIX}EntraTab` as const;
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx
index d782849ce5ee43..9b99c42aeac7a5 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/content.tsx
@@ -8,6 +8,8 @@
import { EuiHorizontalRule } from '@elastic/eui';
import React from 'react';
+import { RiskSummary } from '../../../entity_analytics/components/risk_summary_flyout/risk_summary';
+import type { RiskScoreState } from '../../../entity_analytics/api/hooks/use_risk_score';
import { ManagedUser } from '../../../timelines/components/side_panel/new_user_detail/managed_user';
import type {
ManagedUserData,
@@ -15,11 +17,9 @@ import type {
} from '../../../timelines/components/side_panel/new_user_detail/types';
import { ObservedUser } from '../../../timelines/components/side_panel/new_user_detail/observed_user';
import type { RiskScoreEntity } from '../../../../common/search_strategy';
-import type { RiskScoreState } from '../../../explore/containers/risk_score';
-import { RiskSummary } from '../shared/components/risk_summary';
import { USER_PANEL_RISK_SCORE_QUERY_ID } from '.';
import { FlyoutBody } from '../../shared/components/flyout_body';
-import type { UserDetailsLeftPanelTab } from '../user_detais_left/tabs';
+import type { UserDetailsLeftPanelTab } from '../user_details_left/tabs';
interface UserPanelContentProps {
observedUser: ObservedUserData;
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.test.tsx
index 5ac5495c3a19f1..1c74e4ed23ea57 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.test.tsx
@@ -27,7 +27,7 @@ const mockProps: UserPanelProps = {
jest.mock('../../../common/components/visualization_actions/visualization_embeddable');
const mockedUseRiskScore = jest.fn().mockReturnValue(mockRiskScoreState);
-jest.mock('../../../explore/containers/risk_score', () => ({
+jest.mock('../../../entity_analytics/api/hooks/use_risk_score', () => ({
useRiskScore: () => mockedUseRiskScore(),
}));
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx
index fdc4c9fa703520..76168bc01c8421 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx
@@ -8,6 +8,7 @@
import React, { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
+import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import { ManagedUserDatasetKey } from '../../../../common/search_strategy/security_solution/users/managed_details';
import { useManagedUser } from '../../../timelines/components/side_panel/new_user_detail/hooks/use_managed_user';
import { useObservedUser } from '../../../timelines/components/side_panel/new_user_detail/hooks/use_observed_user';
@@ -17,14 +18,13 @@ import { getCriteriaFromUsersType } from '../../../common/components/ml/criteria
import { useGlobalTime } from '../../../common/containers/use_global_time';
import { AnomalyTableProvider } from '../../../common/components/ml/anomaly/anomaly_table_provider';
import { buildUserNamesFilter } from '../../../../common/search_strategy';
-import { useRiskScore } from '../../../explore/containers/risk_score';
import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine';
import { FlyoutLoading } from '../../shared/components/flyout_loading';
import { FlyoutNavigation } from '../../shared/components/flyout_navigation';
import { UserPanelContent } from './content';
import { UserPanelHeader } from './header';
-import { UserDetailsPanelKey } from '../user_detais_left';
-import type { UserDetailsLeftPanelTab } from '../user_detais_left/tabs';
+import { UserDetailsPanelKey } from '../user_details_left';
+import type { UserDetailsLeftPanelTab } from '../user_details_left/tabs';
export interface UserPanelProps extends Record {
contextID: string;
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts
index d677f79ca43228..88ab3c10241cb6 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import type { RiskScoreState } from '../../../../explore/containers/risk_score';
+import type { RiskScoreState } from '../../../../entity_analytics/api/hooks/use_risk_score';
import type { RiskScoreEntity, UserRiskScore } from '../../../../../common/search_strategy';
import { RiskSeverity } from '../../../../../common/search_strategy';
import { RiskCategories } from '../../../../../common/entity_analytics/risk_engine';
diff --git a/x-pack/plugins/security_solution/public/flyout/index.tsx b/x-pack/plugins/security_solution/public/flyout/index.tsx
index 01c14b3d3ed293..ef7e182324c630 100644
--- a/x-pack/plugins/security_solution/public/flyout/index.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/index.tsx
@@ -24,8 +24,8 @@ import { PreviewPanel, DocumentDetailsPreviewPanelKey } from './document_details
import { PreviewPanelProvider } from './document_details/preview/context';
import type { UserPanelExpandableFlyoutProps } from './entity_details/user_right';
import { UserPanel, UserPanelKey } from './entity_details/user_right';
-import type { UserDetailsPanelProps } from './entity_details/user_detais_left';
-import { UserDetailsPanel, UserDetailsPanelKey } from './entity_details/user_detais_left';
+import type { UserDetailsPanelProps } from './entity_details/user_details_left';
+import { UserDetailsPanel, UserDetailsPanelKey } from './entity_details/user_details_left';
/**
* List of all panels that will be used within the document details expandable flyout.
* This needs to be passed to the expandable flyout registeredPanels property.
diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx
index 7920bae8b22d79..b70a74d839bc98 100644
--- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx
+++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_log_expanded_tray.tsx
@@ -6,7 +6,7 @@
*/
import React, { memo, useMemo } from 'react';
-import { EuiCodeBlock, EuiFlexGroup, EuiFlexItem, EuiDescriptionList } from '@elastic/eui';
+import { EuiCodeBlock, EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { css, euiStyled } from '@kbn/kibana-react-plugin/common';
import { map } from 'lodash';
import { EndpointUploadActionResult } from '../../endpoint_upload_action_result';
@@ -96,7 +96,8 @@ const OutputContent = memo<{ action: MaybeImmutable; 'data-test-s
canAccessEndpointActionsLogManagement,
} = useUserPrivileges().endpointPrivileges;
- const { command, isCompleted, isExpired, wasSuccessful, errors } = action;
+ const { command: _command, isCompleted, isExpired, wasSuccessful, errors } = action;
+ const command = getUiCommand(_command);
if (errors?.length) {
return (
diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx
index 1e835e34ac7ef3..93e6719db3569a 100644
--- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/integration_tests/response_actions_log.test.tsx
@@ -34,6 +34,7 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint
import { useGetEndpointActionList as _useGetEndpointActionList } from '../../../hooks/response_actions/use_get_endpoint_action_list';
import { OUTPUT_MESSAGES } from '../translations';
import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator';
+import { getUiCommand } from '../components/hooks';
const useGetEndpointActionListMock = _useGetEndpointActionList as jest.Mock;
@@ -981,10 +982,12 @@ describe('Response actions history', () => {
render();
+ const outputCommand = getUiCommand(command);
+
const outputs = expandRows();
expect(outputs.map((n) => n.textContent)).toEqual([
- expect.stringContaining(`${command} completed successfully`),
- expect.stringContaining(`${command} completed successfully`),
+ expect.stringContaining(`${outputCommand} completed successfully`),
+ expect.stringContaining(`${outputCommand} completed successfully`),
]);
expect(
renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent)
@@ -1006,10 +1009,11 @@ describe('Response actions history', () => {
});
render();
+ const outputCommand = getUiCommand(command);
const outputs = expandRows();
expect(outputs.map((n) => n.textContent)).toEqual([
- `${command} failed`,
- `${command} failed`,
+ `${outputCommand} failed`,
+ `${outputCommand} failed`,
]);
expect(
renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent)
@@ -1032,10 +1036,11 @@ describe('Response actions history', () => {
});
render();
+ const outputCommand = getUiCommand(command);
const outputs = expandRows();
expect(outputs.map((n) => n.textContent)).toEqual([
- `${command} failed: action expired`,
- `${command} failed: action expired`,
+ `${outputCommand} failed: action expired`,
+ `${outputCommand} failed: action expired`,
]);
expect(
renderResult.getAllByTestId(`${testPrefix}-column-status`).map((n) => n.textContent)
diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts b/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts
index df3a5cf6d38a07..ef28ebc445e2ad 100644
--- a/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts
+++ b/x-pack/plugins/security_solution/public/management/cypress/support/create_and_enroll_endpoint_host_ci.ts
@@ -6,6 +6,8 @@
*/
import { kibanaPackageJson } from '@kbn/repo-info';
+import type { Client } from '@elastic/elasticsearch';
+
import type { ToolingLog } from '@kbn/tooling-log';
import type { KbnClient } from '@kbn/test/src/kbn_client';
import { isFleetServerRunning } from '../../../../scripts/endpoint/common/fleet_server/fleet_server_services';
@@ -28,6 +30,7 @@ import {
export interface CreateAndEnrollEndpointHostCIOptions
extends Pick {
+ esClient: Client;
kbnClient: KbnClient;
log: ToolingLog;
/** The fleet Agent Policy ID to use for enrolling the agent */
@@ -51,6 +54,7 @@ export interface CreateAndEnrollEndpointHostCIResponse {
*/
export const createAndEnrollEndpointHostCI = async ({
kbnClient,
+ esClient,
log,
agentPolicyId,
cpus,
@@ -122,7 +126,13 @@ export const createAndEnrollEndpointHostCI = async ({
await hostVm.exec(agentEnrollCommand);
- const { id: agentId } = await waitForHostToEnroll(kbnClient, log, hostVm.name, 240000);
+ const { id: agentId } = await waitForHostToEnroll(
+ kbnClient,
+ log,
+ hostVm.name,
+ 5 * 60 * 1000,
+ esClient
+ );
return {
hostname: hostVm.name,
diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
index 1c6e50b1cf2f4e..356082f7325da3 100644
--- a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
+++ b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
@@ -169,12 +169,13 @@ export const dataLoaders = (
endpointPackageVersion?: string;
agentPolicyName?: string;
}) => {
- const { kbnClient } = await stackServicesPromise;
+ const { kbnClient, log } = await stackServicesPromise;
return indexFleetEndpointPolicy(
kbnClient,
policyName,
endpointPackageVersion,
- agentPolicyName
+ agentPolicyName,
+ log
);
},
@@ -390,7 +391,7 @@ ${s1Info.status}
createEndpointHost: async (
options: Omit
): Promise => {
- const { kbnClient, log } = await stackServicesPromise;
+ const { kbnClient, log, esClient } = await stackServicesPromise;
let retryAttempt = 0;
const attemptCreateEndpointHost =
@@ -403,6 +404,7 @@ ${s1Info.status}
...options,
log,
kbnClient,
+ esClient,
})
: await createAndEnrollEndpointHost({
useClosestVersionMatch: true,
diff --git a/x-pack/plugins/security_solution/public/overview/components/common.tsx b/x-pack/plugins/security_solution/public/overview/components/common.tsx
index f9068a4507b75c..fcf4072db1afd2 100644
--- a/x-pack/plugins/security_solution/public/overview/components/common.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/common.tsx
@@ -8,7 +8,7 @@
import { EuiButtonIcon, EuiPopover, EuiPopoverTitle, EuiText } from '@elastic/eui';
import React, { useCallback, useState } from 'react';
import * as i18n from './translations';
-import { RiskScoreDocLink } from '../../explore/components/risk_score/risk_score_onboarding/risk_score_doc_link';
+import { RiskScoreDocLink } from '../../entity_analytics/components/risk_score_onboarding/risk_score_doc_link';
import type { RiskScoreEntity } from '../../../common/entity_analytics/risk_engine';
export const RiskScoreInfoTooltip: React.FC<{
diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/translations.ts b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/translations.ts
deleted file mode 100644
index cc538bedae1eaf..00000000000000
--- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/common/translations.ts
+++ /dev/null
@@ -1,26 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const TOTAL_LABEL = i18n.translate('xpack.securitySolution.entityAnalytics.totalLabel', {
- defaultMessage: 'Total',
-});
-
-export const HOST_RISK_TITLE = i18n.translate(
- 'xpack.securitySolution.entityAnalytics.hostsRiskDashboard.title',
- {
- defaultMessage: 'Host Risk Scores',
- }
-);
-
-export const USER_RISK_TITLE = i18n.translate(
- 'xpack.securitySolution.entityAnalytics.usersRiskDashboard.title',
- {
- defaultMessage: 'User Risk Scores',
- }
-);
diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx
index edbb6d03bf9a9d..b1714aace87a51 100644
--- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx
@@ -14,7 +14,7 @@ import { TestProviders } from '../../../common/mock';
import { HostOverview } from '.';
import { mockData } from './mock';
import { mockAnomalies } from '../../../common/components/ml/mock';
-import { useRiskScore } from '../../../explore/containers/risk_score/all';
+import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
const defaultProps = {
data: undefined,
@@ -25,7 +25,7 @@ const defaultProps = {
loading: true,
};
-jest.mock('../../../explore/containers/risk_score/all');
+jest.mock('../../../entity_analytics/api/hooks/use_risk_score');
const mockUseRiskScore = useRiskScore as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx
index 686828412977a8..a6409c587e0a6e 100644
--- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.tsx
@@ -10,6 +10,7 @@ import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-th
import { getOr } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
+import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import type { HostItem } from '../../../../common/search_strategy';
import { buildHostNamesFilter, RiskScoreEntity } from '../../../../common/search_strategy';
import { DEFAULT_DARK_MODE } from '../../../../common/constants';
@@ -35,9 +36,8 @@ import { DescriptionListStyled, OverviewWrapper } from '../../../common/componen
import * as i18n from './translations';
import { EndpointOverview } from './endpoint_overview';
import { OverviewDescriptionList } from '../../../common/components/overview_description_list';
-import { useRiskScore } from '../../../explore/containers/risk_score';
-import { RiskScoreLevel } from '../../../explore/components/risk_score/severity/common';
-import { RiskScoreHeaderTitle } from '../../../explore/components/risk_score/risk_score_onboarding/risk_score_header_title';
+import { RiskScoreLevel } from '../../../entity_analytics/components/severity/common';
+import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_onboarding/risk_score_header_title';
import type { SourcererScopeName } from '../../../common/store/sourcerer/model';
import { RiskScoreDocTooltip } from '../common';
diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx
index 185f2a0fc17b5a..74d78af12fde46 100644
--- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.test.tsx
@@ -12,7 +12,7 @@ import '../../../common/mock/match_media';
import { TestProviders } from '../../../common/mock';
import { mockAnomalies } from '../../../common/components/ml/mock';
-import { useRiskScore } from '../../../explore/containers/risk_score/all';
+import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import type { UserSummaryProps } from '.';
import { UserOverview } from '.';
@@ -25,7 +25,7 @@ const defaultProps = {
loading: false,
};
-jest.mock('../../../explore/containers/risk_score/all');
+jest.mock('../../../entity_analytics/api/hooks/use_risk_score');
const mockRiskScore = useRiskScore as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx
index 446fe215a695a9..572e4aab7e6dfa 100644
--- a/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/user_overview/index.tsx
@@ -10,6 +10,7 @@ import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-th
import { getOr } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
+import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import { buildUserNamesFilter, RiskScoreEntity } from '../../../../common/search_strategy';
import { DEFAULT_DARK_MODE } from '../../../../common/constants';
import type { DescriptionList } from '../../../../common/utility_types';
@@ -32,10 +33,9 @@ import { DescriptionListStyled, OverviewWrapper } from '../../../common/componen
import * as i18n from './translations';
import { OverviewDescriptionList } from '../../../common/components/overview_description_list';
-import { useRiskScore } from '../../../explore/containers/risk_score';
-import { RiskScoreLevel } from '../../../explore/components/risk_score/severity/common';
+import { RiskScoreLevel } from '../../../entity_analytics/components/severity/common';
import type { UserItem } from '../../../../common/search_strategy/security_solution/users/common';
-import { RiskScoreHeaderTitle } from '../../../explore/components/risk_score/risk_score_onboarding/risk_score_header_title';
+import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_onboarding/risk_score_header_title';
import type { SourcererScopeName } from '../../../common/store/sourcerer/model';
import { RiskScoreDocTooltip } from '../common';
diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
index c98dbc74d5a2cd..b4abb383319521 100644
--- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
+++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx
@@ -24,8 +24,8 @@ import { useCtiDashboardLinks } from '../containers/overview_cti_links';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { initialUserPrivilegesState } from '../../common/components/user_privileges/user_privileges_context';
import type { EndpointPrivileges } from '../../../common/endpoint/types';
-import { useRiskScore } from '../../explore/containers/risk_score';
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
+import { useRiskScore } from '../../entity_analytics/api/hooks/use_risk_score';
const mockNavigateToApp = jest.fn();
jest.mock('../../common/components/landing_page');
@@ -97,7 +97,7 @@ jest.mock('../containers/overview_cti_links/use_all_ti_data_sources');
const useAllTiDataSourcesMock = useAllTiDataSources as jest.Mock;
useAllTiDataSourcesMock.mockReturnValue(mockTiDataSources);
-jest.mock('../../explore/containers/risk_score');
+jest.mock('../../entity_analytics/api/hooks/use_risk_score');
const useRiskScoreMock = useRiskScore as jest.Mock;
useRiskScoreMock.mockReturnValue({ loading: false, data: [], isModuleEnabled: false });
diff --git a/x-pack/plugins/security_solution/public/overview/routes.tsx b/x-pack/plugins/security_solution/public/overview/routes.tsx
index 560daf686242e0..82214b2463bdfa 100644
--- a/x-pack/plugins/security_solution/public/overview/routes.tsx
+++ b/x-pack/plugins/security_solution/public/overview/routes.tsx
@@ -21,7 +21,7 @@ import { StatefulOverview } from './pages/overview';
import { DataQuality } from './pages/data_quality';
import { DetectionResponse } from './pages/detection_response';
import { PluginTemplateWrapper } from '../common/components/plugin_template_wrapper';
-import { EntityAnalyticsPage } from './pages/entity_analytics';
+import { EntityAnalyticsPage } from '../entity_analytics/pages/entity_analytics_dashboard';
import { SecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper';
import { LandingPage } from './pages/landing';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx
index 3f7702d490e9dc..f50142177ba8b6 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx
@@ -116,7 +116,7 @@ jest.mock(
}
);
jest.mock('../../../../detections/components/alerts_table/actions');
-jest.mock('../../../../explore/containers/risk_score', () => {
+jest.mock('../../../../entity_analytics/api/hooks/use_risk_score', () => {
return {
useRiskScore: jest.fn().mockReturnValue({
loading: true,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx
index a208a9ae3f41f1..590f120b196878 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx
@@ -18,7 +18,7 @@ import {
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/css';
-import type { UserDetailsLeftPanelTab } from '../../../../flyout/entity_details/user_detais_left/tabs';
+import type { UserDetailsLeftPanelTab } from '../../../../flyout/entity_details/user_details_left/tabs';
import { UserAssetTableType } from '../../../../explore/users/store/model';
import type { ManagedUserFields } from '../../../../../common/search_strategy/security_solution/users/managed_details';
import { ManagedUserDatasetKey } from '../../../../../common/search_strategy/security_solution/users/managed_details';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user_accordion.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user_accordion.tsx
index 9ae5a6433f0413..a03775f61cf26b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user_accordion.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user_accordion.tsx
@@ -11,7 +11,7 @@ import React from 'react';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { get } from 'lodash/fp';
-import { UserDetailsLeftPanelTab } from '../../../../flyout/entity_details/user_detais_left/tabs';
+import { UserDetailsLeftPanelTab } from '../../../../flyout/entity_details/user_details_left/tabs';
import { ExpandablePanel } from '../../../../flyout/shared/components/expandable_panel';
import type { ManagedUserFields } from '../../../../../common/search_strategy/security_solution/users/managed_details';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx
index 798588928bb777..fab77b92582f67 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/risk_score_field.tsx
@@ -15,8 +15,8 @@ import * as i18n from './translations';
import { RiskScoreEntity } from '../../../../../common/search_strategy';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
-import { RiskScoreLevel } from '../../../../explore/components/risk_score/severity/common';
-import type { RiskScoreState } from '../../../../explore/containers/risk_score';
+import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
+import type { RiskScoreState } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { RiskScoreDocTooltip } from '../../../../overview/components/common';
export const TooltipContainer = styled.div`
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/connectors_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/connectors_services.ts
new file mode 100644
index 00000000000000..3fb2971f1236d2
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/connectors_services.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { KbnClient } from '@kbn/test';
+import type { AllConnectorsResponseV1 } from '@kbn/actions-plugin/common/routes/connector/response';
+import type { bodySchema } from '@kbn/actions-plugin/server/routes/create';
+import type { TypeOf } from '@kbn/config-schema';
+import type { Connector } from '@kbn/actions-plugin/server/application/connector/types';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
+
+/**
+ * Retrieve list of configured Connectors
+ * @param kbnClient
+ */
+export const fetchConnectorsList = async (
+ kbnClient: KbnClient
+): Promise => {
+ return kbnClient
+ .request({
+ path: '/api/actions/connectors',
+ method: 'GET',
+ })
+ .catch(catchAxiosErrorFormatAndThrow)
+ .then((response) => response.data);
+};
+
+/**
+ * Returns the first connector instance (if any) of a given type
+ * @param kbnClient
+ * @param connectorTypeId
+ */
+export const fetchConnectorByType = async (
+ kbnClient: KbnClient,
+ connectorTypeId: string
+): Promise => {
+ const allConnectors = await fetchConnectorsList(kbnClient);
+
+ for (const connector of allConnectors) {
+ if (connector.connector_type_id === connectorTypeId) {
+ return connector;
+ }
+ }
+};
+
+type CreateConnectorBody = TypeOf;
+
+/**
+ * Creates a connector in the stack
+ * @param kbnClient
+ * @param createPayload
+ */
+export const createConnector = async (
+ kbnClient: KbnClient,
+ createPayload: CreateConnectorBody
+): Promise => {
+ return kbnClient
+ .request({
+ path: '/api/actions/connector',
+ method: 'POST',
+ body: createPayload,
+ })
+ .catch(catchAxiosErrorFormatAndThrow)
+ .then((response) => response.data);
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/detection_rules_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/detection_rules_services.ts
new file mode 100644
index 00000000000000..478501b00b89d9
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/detection_rules_services.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { KbnClient } from '@kbn/test';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
+import {
+ DETECTION_ENGINE_RULES_URL,
+ DETECTION_ENGINE_RULES_URL_FIND,
+} from '../../../common/constants';
+import type {
+ CreateRuleRequestBody,
+ FindRulesRequestQuery,
+ FindRulesResponse,
+ RuleResponse,
+} from '../../../common/api/detection_engine';
+
+/**
+ * Creates a detection engine rule
+ * @param kbnClient
+ * @param payload
+ */
+export const createRule = async (
+ kbnClient: KbnClient,
+ payload: Partial = {}
+): Promise => {
+ return kbnClient
+ .request({
+ path: DETECTION_ENGINE_RULES_URL,
+ method: 'POST',
+ body: {
+ type: 'query',
+ index: [
+ 'apm-*-transaction*',
+ 'auditbeat-*',
+ 'endgame-*',
+ 'filebeat-*',
+ 'logs-*',
+ 'packetbeat-*',
+ 'traces-apm*',
+ 'winlogbeat-*',
+ '-*elastic-cloud-logs-*',
+ ],
+ filters: [],
+ language: 'kuery',
+ query: '_id:*',
+ author: [],
+ false_positives: [],
+ references: [],
+ risk_score: 21,
+ risk_score_mapping: [],
+ severity: 'low',
+ severity_mapping: [],
+ threat: [],
+ name: `Test rule - ${Math.random().toString(36).substring(2)}`,
+ description: `Test rule created from: ${__filename}`,
+ tags: [],
+ license: '',
+ interval: '1m',
+ from: 'now-120s',
+ to: 'now',
+ meta: {
+ from: '1m',
+ kibana_siem_app_url: kbnClient.resolveUrl('/app/security'),
+ },
+ actions: [],
+ enabled: true,
+ throttle: 'no_actions',
+
+ ...payload,
+ },
+ headers: { 'elastic-api-version': '2023-10-31' },
+ })
+ .catch(catchAxiosErrorFormatAndThrow)
+ .then((response) => response.data);
+};
+
+/**
+ * Query the Detection Rules
+ * @param kbnClient
+ * @param query
+ */
+export const findRules = async (
+ kbnClient: KbnClient,
+ query: Partial = {}
+): Promise => {
+ return kbnClient
+ .request({
+ path: DETECTION_ENGINE_RULES_URL_FIND,
+ method: 'GET',
+ headers: { 'elastic-api-version': '2023-10-31' },
+ query,
+ })
+ .catch(catchAxiosErrorFormatAndThrow)
+ .then((response) => response.data);
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts
index a69f348c366ebe..cf0672b113ac70 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_metadata_services.ts
@@ -10,7 +10,11 @@ import type { KbnClient } from '@kbn/test';
import type { WriteResponseBase } from '@elastic/elasticsearch/lib/api/types';
import { clone, merge } from 'lodash';
import type { DeepPartial } from 'utility-types';
-import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
+import {
+ RETRYABLE_TRANSIENT_ERRORS,
+ retryOnError,
+} from '../../../common/endpoint/data_loaders/utils';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import type { GetMetadataListRequestQuery } from '../../../common/api/endpoint';
import { resolvePathVariables } from '../../../public/common/utils/resolve_path_variables';
import {
@@ -19,6 +23,7 @@ import {
METADATA_DATASTREAM,
} from '../../../common/endpoint/constants';
import type { HostInfo, HostMetadata, MetadataListResponse } from '../../../common/endpoint/types';
+import { HostStatus } from '../../../common/endpoint/types';
import { EndpointDocGenerator } from '../../../common/endpoint/generate_data';
const endpointGenerator = new EndpointDocGenerator();
@@ -163,15 +168,15 @@ export const waitForEndpointToStreamData = async (
let found: HostInfo | undefined;
while (!found && !hasTimedOut()) {
- found = await fetchEndpointMetadata(kbnClient, endpointAgentId).catch((error) => {
- // Ignore `not found` (404) responses. Endpoint could be new and thus documents might not have
- // been streamed yet.
- if (error?.response?.status === 404) {
- return undefined;
- }
-
- throw error;
- });
+ found = await retryOnError(
+ async () =>
+ fetchEndpointMetadataList(kbnClient, {
+ kuery: `united.endpoint.agent.id: "${endpointAgentId}"`,
+ }).then((response) => {
+ return response.data.filter((record) => record.host_status === HostStatus.HEALTHY)[0];
+ }),
+ RETRYABLE_TRANSIENT_ERRORS
+ );
if (!found) {
// sleep and check again
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts
index bb322482feebf2..c9d5b84eb167bf 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts
@@ -49,8 +49,8 @@ import {
retryOnError,
} from '../../../../common/endpoint/data_loaders/utils';
import { isServerlessKibanaFlavor } from '../stack_services';
-import type { FormattedAxiosError } from '../format_axios_error';
-import { catchAxiosErrorFormatAndThrow } from '../format_axios_error';
+import type { FormattedAxiosError } from '../../../../common/endpoint/format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../../common/endpoint/format_axios_error';
import {
ensureFleetSetup,
fetchFleetOutputs,
@@ -247,113 +247,120 @@ const startFleetServerWithDocker = async ({
let agentVersion = version || (await getAgentVersionMatchingCurrentStack(kbnClient));
const localhostRealIp = getLocalhostRealIp();
const fleetServerUrl = `https://${localhostRealIp}:${port}`;
+ const isServerless = await isServerlessKibanaFlavor(kbnClient);
+ const esURL = new URL(await getFleetElasticsearchOutputHost(kbnClient));
+ const containerName = `dev-fleet-server.${port}`;
log.info(
`Starting a new fleet server using Docker\n Agent version: ${agentVersion}\n Server URL: ${fleetServerUrl}`
);
- const response: StartedServer = await log.indent(4, async () => {
- const isServerless = await isServerlessKibanaFlavor(kbnClient);
- const esURL = new URL(await getFleetElasticsearchOutputHost(kbnClient));
- const containerName = `dev-fleet-server.${port}`;
- const hostname = `dev-fleet-server.${port}.${Math.random().toString(32).substring(2, 6)}`;
- let containerId = '';
- let fleetServerVersionInfo = '';
-
- if (isLocalhost(esURL.hostname)) {
- esURL.hostname = localhostRealIp;
- }
+ let retryAttempt = isServerless ? 0 : 1;
+ const attemptServerlessFleetServerSetup = async (): Promise => {
+ return log.indent(4, async () => {
+ const hostname = `dev-fleet-server.${port}.${Math.random().toString(32).substring(2, 6)}`;
+ let containerId = '';
+ let fleetServerVersionInfo = '';
+
+ if (isLocalhost(esURL.hostname)) {
+ esURL.hostname = localhostRealIp;
+ }
- if (isServerless) {
- log.info(`Kibana running in serverless mode.
+ if (isServerless) {
+ log.info(`Kibana running in serverless mode.
- will install/run standalone Fleet Server
- version adjusted to [latest] from [${agentVersion}]`);
- agentVersion = 'latest';
- } else {
- assert.ok(!!policyId, '`policyId` is required');
- assert.ok(!!serviceToken, '`serviceToken` is required');
- }
-
- // Create the `elastic` network to use with all containers
- await maybeCreateDockerNetwork(log);
+ agentVersion = 'latest';
+ } else {
+ assert.ok(!!policyId, '`policyId` is required');
+ assert.ok(!!serviceToken, '`serviceToken` is required');
+ }
- try {
- const dockerArgs = isServerless
- ? getFleetServerStandAloneDockerArgs({
- containerName,
- hostname,
- port,
- esUrl: esURL.toString(),
- agentVersion,
+ // Create the `elastic` network to use with all containers
+ await maybeCreateDockerNetwork(log);
+ try {
+ const dockerArgs = isServerless
+ ? getFleetServerStandAloneDockerArgs({
+ containerName,
+ hostname,
+ port,
+ esUrl: esURL.toString(),
+ agentVersion,
+ })
+ : getFleetServerManagedDockerArgs({
+ containerName,
+ hostname,
+ port,
+ serviceToken,
+ policyId,
+ agentVersion,
+ esUrl: esURL.toString(),
+ });
+
+ await execa('docker', ['kill', containerName])
+ .then(() => {
+ log.info(
+ `Killed an existing container with name [${containerName}]. New one will be started.`
+ );
})
- : getFleetServerManagedDockerArgs({
- containerName,
- hostname,
- port,
- serviceToken,
- policyId,
- agentVersion,
- esUrl: esURL.toString(),
+ .catch((error) => {
+ if (!/no such container/i.test(error.message)) {
+ log.verbose(`Attempt to kill currently running fleet-server container with name [${containerName}] was unsuccessful:
+ ${error}`);
+ }
});
- await execa('docker', ['kill', containerName])
- .then(() => {
- log.info(
- `Killed an existing container with name [${containerName}]. New one will be started.`
- );
- })
- .catch((error) => {
- if (!/no such container/i.test(error.message)) {
- log.verbose(`Attempt to kill currently running fleet-server container with name [${containerName}] was unsuccessful:
- ${error}`);
- }
- });
+ log.verbose(`docker arguments:\n${dockerArgs.join(' ')}`);
- log.verbose(`docker arguments:\n${dockerArgs.join(' ')}`);
+ containerId = (await execa('docker', dockerArgs)).stdout;
- containerId = (await execa('docker', dockerArgs)).stdout;
+ log.info(`Fleet server started`);
- log.info(`Fleet server started`);
+ if (!isServerless) {
+ await addFleetServerHostToFleetSettings(kbnClient, log, fleetServerUrl);
+ }
- if (!isServerless) {
- await addFleetServerHostToFleetSettings(kbnClient, log, fleetServerUrl);
- }
+ await updateFleetElasticsearchOutputHostNames(kbnClient, log);
- await updateFleetElasticsearchOutputHostNames(kbnClient, log);
+ if (isServerless) {
+ log.info(`Waiting for server [${hostname}] to register with Elasticsearch`);
+ await waitForFleetServerToRegisterWithElasticsearch(kbnClient, hostname, 180000);
+ } else {
+ await waitForHostToEnroll(kbnClient, log, hostname, 120000);
+ }
- if (isServerless) {
- log.info(`Waiting for server [${hostname}] to register with Elasticsearch`);
+ fleetServerVersionInfo = isServerless
+ ? // `/usr/bin/fleet-server` process does not seem to support a `--version` type of argument
+ 'Running latest standalone fleet server'
+ : (
+ await execa('docker', [
+ 'exec',
+ containerName,
+ '/bin/bash',
+ '-c',
+ '/usr/share/elastic-agent/elastic-agent version',
+ ]).catch((err) => {
+ log.verbose(
+ `Failed to retrieve agent version information from running instance.`,
+ err
+ );
+ return { stdout: 'Unable to retrieve version information' };
+ })
+ ).stdout;
+ } catch (error) {
+ if (retryAttempt < 1) {
+ retryAttempt++;
+ log.error(`Failed to start fleet server, retrying. Error: ${error.message}`);
+ log.verbose(dump(error));
+ return attemptServerlessFleetServerSetup();
+ }
- await waitForFleetServerToRegisterWithElasticsearch(kbnClient, hostname, 180000);
- } else {
- await waitForHostToEnroll(kbnClient, log, hostname, 120000);
+ log.error(dump(error));
+ throw error;
}
- fleetServerVersionInfo = isServerless
- ? // `/usr/bin/fleet-server` process does not seem to support a `--version` type of argument
- 'Running latest standalone fleet server'
- : (
- await execa('docker', [
- 'exec',
- containerName,
- '/bin/bash',
- '-c',
- '/usr/share/elastic-agent/elastic-agent version',
- ]).catch((err) => {
- log.verbose(
- `Failed to retrieve agent version information from running instance.`,
- err
- );
- return { stdout: 'Unable to retrieve version information' };
- })
- ).stdout;
- } catch (error) {
- log.error(dump(error));
- throw error;
- }
-
- const info = `Container Name: ${containerName}
+ const info = `Container Name: ${containerName}
Container Id: ${containerId}
Fleet-server version:
${fleetServerVersionInfo.replace(/\n/g, '\n ')}
@@ -363,26 +370,29 @@ Shell access: ${chalk.cyan(`docker exec -it ${containerName} /bin/bash`)
Kill container: ${chalk.cyan(`docker kill ${containerId}`)}
`;
- return {
- type: 'docker',
- name: containerName,
- id: containerId,
- url: fleetServerUrl,
- info,
- stop: async () => {
- log.info(
- `Stopping (kill) fleet server. Container name [${containerName}] id [${containerId}]`
- );
- await execa('docker', ['kill', containerId]);
- },
- stopNow: () => {
- log.info(
- `Stopping (kill) fleet server. Container name [${containerName}] id [${containerId}]`
- );
- execa.sync('docker', ['kill', containerId]);
- },
- };
- });
+ return {
+ type: 'docker',
+ name: containerName,
+ id: containerId,
+ url: fleetServerUrl,
+ info,
+ stop: async () => {
+ log.info(
+ `Stopping (kill) fleet server. Container name [${containerName}] id [${containerId}]`
+ );
+ await execa('docker', ['kill', containerId]);
+ },
+ stopNow: () => {
+ log.info(
+ `Stopping (kill) fleet server. Container name [${containerName}] id [${containerId}]`
+ );
+ execa.sync('docker', ['kill', containerId]);
+ },
+ };
+ });
+ };
+
+ const response: StartedServer = await attemptServerlessFleetServerSetup();
log.info(`Done. Fleet server up and running`);
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts
index 1ca0e3ef12273d..b1ff5e8464a00d 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts
@@ -9,20 +9,20 @@ import { map, memoize, pick } from 'lodash';
import type { Client, estypes } from '@elastic/elasticsearch';
import type {
Agent,
+ AgentPolicy,
AgentStatus,
+ CreateAgentPolicyRequest,
+ CreateAgentPolicyResponse,
+ CreatePackagePolicyRequest,
+ CreatePackagePolicyResponse,
GetAgentPoliciesRequest,
GetAgentPoliciesResponse,
GetAgentsResponse,
+ GetInfoResponse,
+ GetOneAgentPolicyResponse,
GetPackagePoliciesRequest,
GetPackagePoliciesResponse,
- CreateAgentPolicyRequest,
- AgentPolicy,
- CreateAgentPolicyResponse,
- CreatePackagePolicyResponse,
- CreatePackagePolicyRequest,
PackagePolicy,
- GetInfoResponse,
- GetOneAgentPolicyResponse,
PostFleetSetupResponse,
} from '@kbn/fleet-plugin/common';
import {
@@ -47,13 +47,13 @@ import {
outputRoutesService,
} from '@kbn/fleet-plugin/common/services';
import type {
+ DeleteAgentPolicyResponse,
EnrollmentAPIKey,
+ GenerateServiceTokenResponse,
GetAgentsRequest,
GetEnrollmentAPIKeysResponse,
- PostAgentUnenrollResponse,
- GenerateServiceTokenResponse,
GetOutputsResponse,
- DeleteAgentPolicyResponse,
+ PostAgentUnenrollResponse,
} from '@kbn/fleet-plugin/common/types';
import nodeFetch from 'node-fetch';
import semver from 'semver';
@@ -68,10 +68,9 @@ import {
createToolingLogger,
RETRYABLE_TRANSIENT_ERRORS,
retryOnError,
- wrapErrorAndRejectPromise,
} from '../../../common/endpoint/data_loaders/utils';
import { fetchKibanaStatus } from './stack_services';
-import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator';
const fleetGenerator = new FleetAgentGenerator();
@@ -161,12 +160,14 @@ export const fetchFleetAgents = async (
* @param log
* @param hostname
* @param timeoutMs
+ * @param esClient
*/
export const waitForHostToEnroll = async (
kbnClient: KbnClient,
log: ToolingLog,
hostname: string,
- timeoutMs: number = 30000
+ timeoutMs: number = 30000,
+ esClient: Client | undefined = undefined
): Promise => {
log.info(`Waiting for host [${hostname}] to enroll with fleet`);
@@ -212,6 +213,12 @@ export const waitForHostToEnroll = async (
log.debug(`Host [${hostname}] has been enrolled with fleet`);
log.verbose(found);
+ // Workaround for united metadata sometimes being unable to find docs in .fleet-agents index. This
+ // seems to be a timing issue with the index refresh.
+ await esClient?.search({
+ index: AGENTS_INDEX,
+ });
+
return found;
};
@@ -401,9 +408,7 @@ export const getAgentFileName = (agentVersion: string): string => {
const downloadArch =
{ arm64: 'arm64', x64: 'x86_64' }[process.arch as string] ??
`UNSUPPORTED_ARCHITECTURE_${process.arch}`;
- const fileName = `elastic-agent-${agentVersion}-linux-${downloadArch}`;
-
- return fileName;
+ return `elastic-agent-${agentVersion}-linux-${downloadArch}`;
};
interface ElasticArtifactSearchResponse {
@@ -560,8 +565,7 @@ export const unEnrollFleetAgent = async (
* Un-enrolls a Fleet agent
*
* @param kbnClient
- * @param agentId
- * @param force
+ * @param policyId
*/
export const getAgentPolicyEnrollmentKey = async (
kbnClient: KbnClient,
@@ -655,7 +659,7 @@ interface EnrollHostVmWithFleetOptions {
/**
* Installs the Elastic agent on the provided Host VM and enrolls with it Fleet.
*
- * NOTE: this method assumes that FLeet-Server is already setup and running.
+ * NOTE: this method assumes that Fleet-Server is already setup and running.
*
* @param hostVm
* @param kbnClient
@@ -772,13 +776,13 @@ export const getOrCreateDefaultAgentPolicy = async ({
});
if (existingPolicy.items[0]) {
- log.info(`Re-using existing Fleet test agent policy`);
+ log.info(`Re-using existing Fleet test agent policy: [${existingPolicy.items[0].name}]`);
log.verbose(existingPolicy.items[0]);
return existingPolicy.items[0];
}
- log.info(`Creating new default test/dev Fleet agent policy`);
+ log.info(`Creating default test/dev Fleet agent policy with name: [${policyName}]`);
const newAgentPolicyData: CreateAgentPolicyRequest['body'] = {
name: policyName,
@@ -797,7 +801,7 @@ export const getOrCreateDefaultAgentPolicy = async ({
body: newAgentPolicyData,
})
.then((response) => response.data.item)
- .catch(wrapErrorAndRejectPromise);
+ .catch(catchAxiosErrorFormatAndThrow);
log.verbose(newAgentPolicy);
@@ -823,7 +827,7 @@ export const createIntegrationPolicy = async (
},
})
.then((response) => response.data.item)
- .catch(wrapErrorAndRejectPromise);
+ .catch(catchAxiosErrorFormatAndThrow);
};
/**
@@ -842,7 +846,7 @@ export const fetchPackageInfo = async (
method: 'GET',
})
.then((response) => response.data.item)
- .catch(wrapErrorAndRejectPromise);
+ .catch(catchAxiosErrorFormatAndThrow);
};
interface AddSentinelOneIntegrationToAgentPolicyOptions {
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/random_policy_id_generator.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/random_policy_id_generator.ts
index 3b494d3bfe9cb2..3c58f44b3d266f 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/random_policy_id_generator.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/random_policy_id_generator.ts
@@ -12,7 +12,7 @@ import {
PACKAGE_POLICY_API_ROUTES,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
} from '@kbn/fleet-plugin/common/constants';
-import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
import { setupFleetForEndpoint } from '../../../common/endpoint/data_loaders/setup_fleet_for_endpoint';
import type { GetPolicyListResponse } from '../../../public/management/pages/policy/types';
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/role_and_user_loader.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/role_and_user_loader.ts
index f8c51d52550184..eb493e22dafafd 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/role_and_user_loader.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/role_and_user_loader.ts
@@ -14,7 +14,7 @@ import { inspect } from 'util';
import type { AxiosError } from 'axios';
import type { EndpointSecurityRoleDefinitions } from './roles_users';
import { getAllEndpointSecurityRoles } from './roles_users';
-import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { COMMON_API_HEADERS } from './constants';
const ignoreHttp409Error = (error: AxiosError) => {
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts
index 13fc2a1a0ffba2..f95ee808408ed7 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts
@@ -17,7 +17,7 @@ import type { ClientOptions } from '@elastic/elasticsearch/lib/client';
import fs from 'fs';
import { CA_CERT_PATH } from '@kbn/dev-utils';
import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils';
-import { catchAxiosErrorFormatAndThrow } from './format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import { isLocalhost } from './is_localhost';
import { getLocalhostRealIp } from './network_services';
import { createSecuritySuperuser } from './security_user_services';
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts
index 17c74b1bf6fc3a..4209b554b2732b 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/vm_services.ts
@@ -178,10 +178,10 @@ export const getMultipassVmCountNotice = async (threshold: number = 1): Promise<
if (output.list.length > threshold) {
return `-----------------------------------------------------------------
${chalk.red('NOTE:')} ${chalk.bold(
- chalk.cyan(`You currently have ${chalk.red(output.list.length)} VMs running.`)
- )} Remember to delete those
- no longer being used.
- View running VMs: ${chalk.bold('multipass list')}
+ chalk.red(`You currently have ${chalk.red(output.list.length)} VMs running.`)
+ )}
+ Remember to delete those no longer being used.
+ View running VMs: ${chalk.cyan('multipass list')}
-----------------------------------------------------------------
`;
}
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts
index f15c08ee875002..fe9053795737a9 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts
@@ -8,15 +8,22 @@
import type { ToolingLog } from '@kbn/tooling-log';
import type { AxiosRequestConfig } from 'axios';
import axios from 'axios';
+import type { KbnClient } from '@kbn/test';
+import { SENTINELONE_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/common/sentinelone/constants';
+import { type RuleResponse } from '../../../common/api/detection_engine';
+import { dump } from '../endpoint_agent_runner/utils';
import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils';
import type {
S1SitesListApiResponse,
S1AgentPackage,
S1AgentPackageListApiResponse,
} from './types';
-import { catchAxiosErrorFormatAndThrow } from '../common/format_axios_error';
+import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error';
import type { HostVm } from '../common/types';
+import { createConnector, fetchConnectorByType } from '../common/connectors_services';
+import { createRule, findRules } from '../common/detection_rules_services';
+
interface S1ClientOptions {
/** The base URL for SentinelOne */
url: string;
@@ -200,6 +207,13 @@ export const installSentinelOneAgent = async ({
const status = (await hostVm.exec(`sudo ${installPath} control status`)).stdout;
+ try {
+ // Generate an alert in SentinelOne
+ await hostVm.exec('nslookup amazon.com');
+ } catch (e) {
+ log?.warning(`Attempted to generate an alert on SentinelOne host failed: ${e.message}`);
+ }
+
log.info('done');
return {
@@ -208,3 +222,73 @@ export const installSentinelOneAgent = async ({
};
});
};
+
+interface CreateSentinelOneStackConnectorIfNeededOptions {
+ kbnClient: KbnClient;
+ log: ToolingLog;
+ s1Url: string;
+ s1ApiToken: string;
+ name?: string;
+}
+
+export const createSentinelOneStackConnectorIfNeeded = async ({
+ kbnClient,
+ log,
+ s1ApiToken,
+ s1Url,
+ name = 'SentinelOne Dev instance',
+}: CreateSentinelOneStackConnectorIfNeededOptions): Promise => {
+ const connector = await fetchConnectorByType(kbnClient, SENTINELONE_CONNECTOR_ID);
+
+ if (connector) {
+ log.debug(`Nothing to do. A connector for SentinelOne is already configured`);
+ log.verbose(dump(connector));
+ return;
+ }
+
+ log.info(`Creating SentinelOne Connector with name: ${name}`);
+
+ await createConnector(kbnClient, {
+ name,
+ config: {
+ url: s1Url,
+ },
+ secrets: {
+ token: s1ApiToken,
+ },
+ connector_type_id: SENTINELONE_CONNECTOR_ID,
+ });
+};
+
+export const createDetectionEngineSentinelOneRuleIfNeeded = async (
+ kbnClient: KbnClient,
+ log: ToolingLog
+): Promise => {
+ const ruleName = 'Promote SentinelOne alerts';
+ const sentinelOneAlertsIndexPattern = 'logs-sentinel_one.alert';
+ const ruleQueryValue = 'observer.serial_number:*';
+
+ const { data } = await findRules(kbnClient, {
+ filter: `(alert.attributes.params.query: "${ruleQueryValue}" AND alert.attributes.params.index: ${sentinelOneAlertsIndexPattern})`,
+ });
+
+ if (data.length) {
+ log.info(
+ `Detection engine rule for SentinelOne alerts already exists [${data[0].name}]. No need to create a new one.`
+ );
+
+ return data[0];
+ }
+
+ log.info(`Creating new detection engine rule named [${ruleName}] for SentinelOne`);
+
+ const createdRule = await createRule(kbnClient, {
+ index: [sentinelOneAlertsIndexPattern],
+ query: ruleQueryValue,
+ from: 'now-3660s',
+ });
+
+ log.verbose(dump(createdRule));
+
+ return createdRule;
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts
index 3fad8bf0223bb7..3416b8d4e51b6f 100644
--- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts
+++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts
@@ -21,7 +21,12 @@ import {
fetchAgentPolicy,
getOrCreateDefaultAgentPolicy,
} from '../common/fleet_services';
-import { installSentinelOneAgent, S1Client } from './common';
+import {
+ createDetectionEngineSentinelOneRuleIfNeeded,
+ createSentinelOneStackConnectorIfNeeded,
+ installSentinelOneAgent,
+ S1Client,
+} from './common';
import { createVm, generateVmName, getMultipassVmCountNotice } from '../common/vm_services';
import { createKbnClient } from '../common/stack_services';
@@ -157,11 +162,16 @@ const runCli: RunFn = async ({ log, flags }) => {
agentPolicyId,
});
} else {
- log.info(
+ log.debug(
`No host VM created for Fleet agent policy [${agentPolicyName}]. It already shows to have [${agents}] enrolled`
);
}
+ await Promise.all([
+ createSentinelOneStackConnectorIfNeeded({ kbnClient, log, s1ApiToken, s1Url }),
+ createDetectionEngineSentinelOneRuleIfNeeded(kbnClient, log),
+ ]);
+
log.info(`Done!
${hostVm.info()}
diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts
index 3fb28c1c5099db..4a1c1332916db0 100644
--- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts
@@ -9,6 +9,7 @@ import type {
ElasticsearchClient,
KibanaRequest,
Logger,
+ LoggerFactory,
SavedObjectsClientContract,
} from '@kbn/core/server';
import type { ExceptionListClient, ListsServerExtensionRegistrar } from '@kbn/lists-plugin/server';
@@ -52,6 +53,7 @@ import type { AppFeaturesService } from '../lib/app_features_service/app_feature
export interface EndpointAppContextServiceSetupContract {
securitySolutionRequestContextFactory: IRequestContextFactory;
cloud: CloudSetup;
+ loggerFactory: LoggerFactory;
}
export interface EndpointAppContextServiceStartContract {
@@ -172,6 +174,14 @@ export class EndpointAppContextService {
return this.startDependencies.fleetAuthzService;
}
+ public createLogger(...contextParts: string[]) {
+ if (!this.setupDependencies?.loggerFactory) {
+ throw new EndpointAppContentServicesNotStartedError();
+ }
+
+ return this.setupDependencies.loggerFactory.get(...contextParts);
+ }
+
public async getEndpointAuthz(request: KibanaRequest): Promise {
const fleetAuthz = await this.getFleetAuthzService().fromRequest(request);
const userRoles = this.security?.authc.getCurrentUser(request)?.roles ?? [];
diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts b/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts
deleted file mode 100644
index 906402b877e0e0..00000000000000
--- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/base_actions_provider.ts
+++ /dev/null
@@ -1,86 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
-import type { CasesClient } from '@kbn/cases-plugin/server';
-import type { Logger } from '@kbn/logging';
-import type { EndpointAppContext } from '../../types';
-import type { ResponseActionsProvider } from './types';
-import type {
- ActionDetails,
- GetProcessesActionOutputContent,
- KillOrSuspendProcessRequestBody,
- KillProcessActionOutputContent,
- ResponseActionExecuteOutputContent,
- ResponseActionGetFileOutputContent,
- ResponseActionGetFileParameters,
- ResponseActionParametersWithPidOrEntityId,
- ResponseActionsExecuteParameters,
- ResponseActionUploadOutputContent,
- ResponseActionUploadParameters,
- SuspendProcessActionOutputContent,
-} from '../../../../common/endpoint/types';
-import type {
- IsolationRouteRequestBody,
- ExecuteActionRequestBody,
- GetProcessesRequestBody,
- ResponseActionGetFileRequestBody,
- UploadActionApiRequestBody,
-} from '../../../../common/api/endpoint';
-
-export interface BaseActionsProviderOptions {
- endpointContext: EndpointAppContext;
- esClient: ElasticsearchClient;
- casesClient?: CasesClient;
- /** Username that will be stored along with the action's ES documents */
- username: string;
-}
-
-export abstract class BaseResponseActionsClient implements ResponseActionsProvider {
- protected readonly log: Logger;
-
- constructor(protected readonly options: BaseActionsProviderOptions) {
- this.log = options.endpointContext.logFactory.get(this.constructor.name ?? 'ActionsProvider');
- }
-
- // TODO:PT implement a generic way to update cases without relying on the Attachments being endpoint agents
- // protected async updateCases(): Promise {
- // throw new Error('Method not yet implemented');
- // }
-
- public abstract isolate(options: IsolationRouteRequestBody): Promise;
-
- public abstract release(options: IsolationRouteRequestBody): Promise;
-
- public abstract killProcess(
- options: KillOrSuspendProcessRequestBody
- ): Promise<
- ActionDetails
- >;
-
- public abstract suspendProcess(
- options: KillOrSuspendProcessRequestBody
- ): Promise<
- ActionDetails
- >;
-
- public abstract runningProcesses(
- options: GetProcessesRequestBody
- ): Promise>;
-
- public abstract getFile(
- options: ResponseActionGetFileRequestBody
- ): Promise>;
-
- public abstract execute(
- options: ExecuteActionRequestBody
- ): Promise>;
-
- public abstract upload(
- options: UploadActionApiRequestBody
- ): Promise>;
-}
diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts
index 6376bf45540415..477d1e70be966d 100644
--- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts
@@ -101,6 +101,7 @@ export const createMockEndpointAppContextService = (
const fleetFromHostFilesClientMock = createFleetFromHostFilesClientMock();
const fleetToHostFilesClientMock = createFleetToHostFilesClientMock();
const fleetActionsClientMock = createFleetActionsClientMock();
+ const loggerFactory = loggingSystemMock.create();
return {
start: jest.fn(),
@@ -108,6 +109,7 @@ export const createMockEndpointAppContextService = (
experimentalFeatures: {
...allowedExperimentalValues,
},
+ createLogger: jest.fn((...parts) => loggerFactory.get(...parts)),
getManifestManager: jest.fn().mockReturnValue(mockManifestManager ?? jest.fn()),
getEndpointMetadataService: jest.fn(() => mockEndpointMetadataContext.endpointMetadataService),
getInternalFleetServices: jest.fn(() => mockEndpointMetadataContext.fleetServices),
@@ -133,6 +135,7 @@ export const createMockEndpointAppContextServiceSetupContract =
return {
securitySolutionRequestContextFactory: requestContextFactoryMock.create(),
cloud: cloudMock.createSetup(),
+ loggerFactory: loggingSystemMock.create(),
};
};
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts
index 8f7682c83daadf..055ef330cb08fa 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts
@@ -62,13 +62,30 @@ import * as ActionDetailsService from '../../services/actions/action_details_by_
import { CaseStatuses } from '@kbn/cases-components';
import { getEndpointAuthzInitialStateMock } from '../../../../common/endpoint/service/authz/mocks';
import { actionCreateService } from '../../services/actions';
+import { getResponseActionsClient as _getResponseActionsClient } from '../../services';
import type { UploadActionApiRequestBody } from '../../../../common/api/endpoint';
import type { FleetToHostFileClientInterface } from '@kbn/fleet-plugin/server';
import type { HapiReadableStream, SecuritySolutionRequestHandlerContext } from '../../../types';
import { createHapiReadableStreamMock } from '../../services/actions/mocks';
import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator';
import { CustomHttpRequestError } from '../../../utils/custom_http_request_error';
-import { omit } from 'lodash';
+import { omit, set } from 'lodash';
+import type { ResponseActionAgentType } from '../../../../common/endpoint/service/response_actions/constants';
+import { responseActionsClientMock } from '../../services/actions/clients/mocks';
+import type { ActionsApiRequestHandlerContext } from '@kbn/actions-plugin/server';
+
+jest.mock('../../services', () => {
+ const realModule = jest.requireActual('../../services');
+
+ return {
+ ...realModule,
+ getResponseActionsClient: jest.fn((...args) => {
+ return realModule.getResponseActionsClient(...args);
+ }),
+ };
+});
+
+const getResponseActionsClientMock = _getResponseActionsClient;
interface CallRouteInterface {
body?: ResponseActionRequestBody;
@@ -1085,7 +1102,7 @@ describe('Response actions', () => {
});
afterEach(() => {
- jest.resetAllMocks();
+ jest.clearAllMocks();
});
it('should create a file', async () => {
@@ -1147,4 +1164,96 @@ describe('Response actions', () => {
});
});
});
+
+ describe('and `responseActionsSentinelOneV1Enabled` feature flag is enabled', () => {
+ let testSetup: HttpApiTestSetupMock;
+ let httpRequestMock: ReturnType;
+ let httpHandlerContextMock: HttpApiTestSetupMock['httpHandlerContextMock'];
+ let httpResponseMock: HttpApiTestSetupMock['httpResponseMock'];
+ let callHandler: () => ReturnType;
+
+ beforeEach(async () => {
+ testSetup = createHttpApiTestSetupMock();
+
+ ({ httpHandlerContextMock, httpResponseMock } = testSetup);
+ httpRequestMock = testSetup.createRequestMock();
+
+ testSetup.endpointAppContextMock.experimentalFeatures = {
+ ...testSetup.endpointAppContextMock.experimentalFeatures,
+ responseActionsSentinelOneV1Enabled: true,
+ };
+
+ httpHandlerContextMock.actions = Promise.resolve({
+ getActionsClient: () => responseActionsClientMock.createConnectorActionsClient(),
+ } as unknown as jest.Mocked);
+
+ // Set the esClient to be used in the handler context
+ // eslint-disable-next-line require-atomic-updates
+ httpHandlerContextMock.core = Promise.resolve(
+ set(
+ await httpHandlerContextMock.core,
+ 'elasticsearch.client.asInternalUser',
+ responseActionsClientMock.createConstructorOptions().esClient
+ )
+ );
+
+ httpRequestMock = testSetup.createRequestMock({
+ body: {
+ endpoint_ids: ['123-456'],
+ },
+ });
+ registerResponseActionRoutes(testSetup.routerMock, testSetup.endpointAppContextMock);
+
+ (testSetup.endpointAppContextMock.service.getEndpointMetadataService as jest.Mock) = jest
+ .fn()
+ .mockReturnValue({
+ getMetadataForEndpoints: jest.fn().mockResolvedValue([
+ {
+ elastic: {
+ agent: {
+ id: '123-456',
+ },
+ },
+ },
+ ]),
+ });
+
+ const handler = testSetup.getRegisteredVersionedRoute(
+ 'post',
+ ISOLATE_HOST_ROUTE_V2,
+ '2023-10-31'
+ ).routeHandler as RequestHandler;
+
+ callHandler = () => handler(httpHandlerContextMock, httpRequestMock, httpResponseMock);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it.each([
+ ['undefined', undefined],
+ ['blank value', ''],
+ ['endpoint', 'endpoint'],
+ ])(
+ 'should use endpoint response actions client when agentType is: %s',
+ async (_, agentTypeValue) => {
+ if (agentTypeValue !== undefined) {
+ httpRequestMock.body.agent_type = agentTypeValue as ResponseActionAgentType;
+ }
+ await callHandler();
+
+ expect(getResponseActionsClientMock).toHaveBeenCalledWith('endpoint', expect.anything());
+ expect(httpResponseMock.ok).toHaveBeenCalled();
+ }
+ );
+
+ it('should use SentinelOne response actions client when agent type is sentinel_one', async () => {
+ httpRequestMock.body.agent_type = 'sentinel_one';
+ await callHandler();
+
+ expect(getResponseActionsClientMock).toHaveBeenCalledWith('sentinel_one', expect.anything());
+ expect(httpResponseMock.ok).toHaveBeenCalled();
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts
index c8669869833fe4..4e8fdc997572af 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts
@@ -7,8 +7,10 @@
import type { RequestHandler } from '@kbn/core/server';
import type { TypeOf } from '@kbn/config-schema';
+import { dump } from '../../utils/dump';
+import { getResponseActionsClient } from '../../services';
+import type { ResponseActionsClient } from '../../services/actions/clients/lib/types';
import { CustomHttpRequestError } from '../../../utils/custom_http_request_error';
-import { EndpointActionsClient } from '../../services/actions/clients';
import type {
NoParametersRequestSchema,
ResponseActionsRequestBody,
@@ -290,50 +292,77 @@ function responseActionRequestHandler {
+ logger.debug(`response action [${command}]:\n${dump(req.body)}`);
+
+ // Note: because our API schemas are defined as module static variables (as opposed to a
+ // `getter` function), we need to include this additional validation here, since
+ // `agent_type` is included in the schema independent of the feature flag
+ if (
+ req.body.agent_type &&
+ !endpointContext.experimentalFeatures.responseActionsSentinelOneV1Enabled
+ ) {
+ return errorHandler(
+ logger,
+ res,
+ new CustomHttpRequestError(`[request body.agent_type]: feature is disabled`, 400)
+ );
+ }
+
const user = endpointContext.service.security?.authc.getCurrentUser(req);
const esClient = (await context.core).elasticsearch.client.asInternalUser;
const casesClient = await endpointContext.service.getCasesClient(req);
- const actionsClient = new EndpointActionsClient({
- esClient,
- casesClient,
- endpointContext,
- username: user?.username ?? 'unknown',
- });
+ const connectorActions = (await context.actions).getActionsClient();
+ const responseActionsClient: ResponseActionsClient = getResponseActionsClient(
+ req.body.agent_type || 'endpoint',
+ {
+ esClient,
+ casesClient,
+ endpointService: endpointContext.service,
+ username: user?.username || 'unknown',
+ connectorActions,
+ }
+ );
try {
let action: ActionDetails;
switch (command) {
case 'isolate':
- action = await actionsClient.isolate(req.body);
+ action = await responseActionsClient.isolate(req.body);
break;
case 'unisolate':
- action = await actionsClient.release(req.body);
+ action = await responseActionsClient.release(req.body);
break;
case 'running-processes':
- action = await actionsClient.runningProcesses(req.body);
+ action = await responseActionsClient.runningProcesses(req.body);
break;
case 'execute':
- action = await actionsClient.execute(req.body as ExecuteActionRequestBody);
+ action = await responseActionsClient.execute(req.body as ExecuteActionRequestBody);
break;
case 'suspend-process':
- action = await actionsClient.suspendProcess(req.body as KillOrSuspendProcessRequestBody);
+ action = await responseActionsClient.suspendProcess(
+ req.body as KillOrSuspendProcessRequestBody
+ );
break;
case 'kill-process':
- action = await actionsClient.killProcess(req.body as KillOrSuspendProcessRequestBody);
+ action = await responseActionsClient.killProcess(
+ req.body as KillOrSuspendProcessRequestBody
+ );
break;
case 'get-file':
- action = await actionsClient.getFile(req.body as ResponseActionGetFileRequestBody);
+ action = await responseActionsClient.getFile(
+ req.body as ResponseActionGetFileRequestBody
+ );
break;
case 'upload':
- action = await actionsClient.upload(req.body as UploadActionApiRequestBody);
+ action = await responseActionsClient.upload(req.body as UploadActionApiRequestBody);
break;
default:
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts
index 452bf46316d603..54d536e314edd7 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.test.ts
@@ -127,11 +127,7 @@ describe('When using `getActionDetailsById()', () => {
body: {
query: {
bool: {
- filter: [
- { term: { action_id: '123' } },
- { term: { input_type: 'endpoint' } },
- { term: { type: 'INPUT_ACTION' } },
- ],
+ filter: [{ term: { action_id: '123' } }],
},
},
},
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts
index 9c85a193d712d8..8e3d6ddad12b87 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_details_by_id.ts
@@ -31,11 +31,11 @@ import { NotFoundError } from '../../errors';
import { ACTION_RESPONSE_INDICES, ACTIONS_SEARCH_PAGE_SIZE } from './constants';
import type { EndpointMetadataService } from '../metadata';
-export const getActionDetailsById = async (
+export const getActionDetailsById = async (
esClient: ElasticsearchClient,
metadataService: EndpointMetadataService,
actionId: string
-): Promise => {
+): Promise => {
let actionRequestsLogEntries: EndpointActivityLogAction[];
let normalizedActionRequest: ReturnType | undefined;
@@ -52,11 +52,7 @@ export const getActionDetailsById = async (
body: {
query: {
bool: {
- filter: [
- { term: { action_id: actionId } },
- { term: { input_type: 'endpoint' } },
- { term: { type: 'INPUT_ACTION' } },
- ],
+ filter: [{ term: { action_id: actionId } }],
},
},
},
@@ -148,5 +144,5 @@ export const getActionDetailsById = async (
parameters: normalizedActionRequest.parameters,
};
- return actionDetails;
+ return actionDetails as T;
};
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts
index 2b5813fa6c909e..a999659f2273a7 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint_actions_client.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { dump } from '../../../utils/dump';
import type { HapiReadableStream } from '../../../../types';
import type { ResponseActionsApiCommandNames } from '../../../../../common/endpoint/service/response_actions/constants';
import { updateCases } from '../create/update_cases';
@@ -17,7 +18,7 @@ import type {
UploadActionApiRequestBody,
ResponseActionsRequestBody,
} from '../../../../../common/api/endpoint';
-import { BaseResponseActionsClient } from '../../../lib/response_actions/base_actions_provider';
+import { ResponseActionsClientImpl } from './lib/base_response_actions_client';
import type {
ActionDetails,
HostMetadata,
@@ -32,18 +33,16 @@ import type {
ResponseActionUploadOutputContent,
ResponseActionUploadParameters,
SuspendProcessActionOutputContent,
- HostMetadataInterface,
- ImmutableObject,
} from '../../../../../common/endpoint/types';
-export class EndpointActionsClient extends BaseResponseActionsClient {
+export class EndpointActionsClient extends ResponseActionsClientImpl {
private async checkAgentIds(ids: string[]): Promise<{
valid: string[];
invalid: string[];
allValid: boolean;
hosts: HostMetadata[];
}> {
- const foundEndpointHosts = await this.options.endpointContext.service
+ const foundEndpointHosts = await this.options.endpointService
.getEndpointMetadataService()
.getMetadataForEndpoints(this.options.esClient, [...new Set(ids)]);
const validIds = foundEndpointHosts.map((endpoint: HostMetadata) => endpoint.elastic.agent.id);
@@ -72,26 +71,24 @@ export class EndpointActionsClient extends BaseResponseActionsClient {
user: { username: this.options.username },
};
- const response = await this.options.endpointContext.service
+ const response = await this.options.endpointService
.getActionCreateService()
.createAction(createPayload, agentIds.valid);
- await this.updateCases(createPayload, agentIds.hosts);
+ try {
+ await updateCases({
+ casesClient: this.options.casesClient,
+ endpointData: agentIds.hosts,
+ createActionPayload: createPayload,
+ });
+ } catch (err) {
+ // failures during update of cases should not cause the response action to fail. Just log error
+ this.log.warn(`failed to update cases: ${err.message}\n${dump(err)}`);
+ }
return response as TResponse;
}
- protected async updateCases(
- createActionPayload: CreateActionPayload,
- endpointData: Array>
- ): Promise {
- return updateCases({
- casesClient: this.options.casesClient,
- createActionPayload,
- endpointData,
- });
- }
-
async isolate(options: IsolationRouteRequestBody): Promise {
return this.handleResponseAction('isolate', options);
}
@@ -155,7 +152,7 @@ export class EndpointActionsClient extends BaseResponseActionsClient {
async upload(
options: UploadActionApiRequestBody
): Promise> {
- const fleetFiles = await this.options.endpointContext.service.getFleetToHostFilesClient();
+ const fleetFiles = await this.options.endpointService.getFleetToHostFilesClient();
const fileStream = options.file as HapiReadableStream;
const { file: _, parameters: userParams, ...actionPayload } = options;
const uploadParameters: ResponseActionUploadParameters = {
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts
new file mode 100644
index 00000000000000..300f2fa56cade1
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/errors.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/* eslint-disable max-classes-per-file */
+
+import type { ResponseActionsApiCommandNames } from '../../../../../common/endpoint/service/response_actions/constants';
+import { dump } from '../../../utils/dump';
+import { CustomHttpRequestError } from '../../../../utils/custom_http_request_error';
+
+/**
+ * Errors associated with Response Actions clients
+ */
+export class ResponseActionsClientError extends CustomHttpRequestError {
+ toJSON() {
+ return {
+ message: this.message,
+ statusCode: this.statusCode,
+ meta: this.meta,
+ stack: this.stack,
+ };
+ }
+
+ toString() {
+ return JSON.stringify(dump(this.toJSON()), null, 2);
+ }
+}
+
+export class ResponseActionsNotSupportedError extends ResponseActionsClientError {
+ constructor(
+ responseAction?: ResponseActionsApiCommandNames,
+ statusCode: number = 405,
+ meta?: unknown
+ ) {
+ super(`Action ${responseAction ? `[${responseAction}] ` : ''}not supported`, statusCode, meta);
+ }
+}
+
+export class UnsupportedResponseActionsAgentTypeError extends ResponseActionsClientError {
+ constructor(message: string, statusCode = 501, meta?: unknown) {
+ super(message, statusCode, meta);
+ }
+}
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.test.ts
new file mode 100644
index 00000000000000..f0cee2a616462b
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.test.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { GetResponseActionsClientConstructorOptions } from '../..';
+import { responseActionsClientMock } from './mocks';
+import { RESPONSE_ACTION_AGENT_TYPE } from '../../../../../common/endpoint/service/response_actions/constants';
+import { getResponseActionsClient } from '../..';
+import { ResponseActionsClientImpl } from './lib/base_response_actions_client';
+import { UnsupportedResponseActionsAgentTypeError } from './errors';
+
+describe('getResponseActionsClient()', () => {
+ let options: GetResponseActionsClientConstructorOptions;
+
+ beforeEach(() => {
+ options = {
+ ...responseActionsClientMock.createConstructorOptions(),
+ connectorActions: responseActionsClientMock.createConnectorActionsClient(),
+ };
+ });
+
+ it.each(RESPONSE_ACTION_AGENT_TYPE)(
+ 'should return a response actions client for agentType: %s',
+ (agentType) => {
+ expect(getResponseActionsClient(agentType, options)).toBeInstanceOf(
+ ResponseActionsClientImpl
+ );
+ }
+ );
+
+ it(`should throw error if agentType is not supported`, () => {
+ expect(() => getResponseActionsClient('foo', options)).toThrow(
+ UnsupportedResponseActionsAgentTypeError
+ );
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.ts
new file mode 100644
index 00000000000000..b60e9419208b97
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/get_response_actions_client.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { SentinelOneActionsClientOptions } from './sentinelone/sentinel_one_actions_client';
+import type { ResponseActionsClient } from './lib/types';
+import type { ResponseActionsClientOptions } from './lib/base_response_actions_client';
+import { EndpointActionsClient } from './endpoint_actions_client';
+import { SentinelOneActionsClient } from './sentinelone/sentinel_one_actions_client';
+import { UnsupportedResponseActionsAgentTypeError } from './errors';
+import type { ResponseActionAgentType } from '../../../../../common/endpoint/service/response_actions/constants';
+
+export type GetResponseActionsClientConstructorOptions = ResponseActionsClientOptions &
+ SentinelOneActionsClientOptions;
+
+/**
+ * Retrieve a response actions client for an agent type
+ * @param agentType
+ * @param constructorOptions
+ *
+ * @throws UnsupportedResponseActionsAgentTypeError
+ */
+export const getResponseActionsClient = (
+ agentType: string | ResponseActionAgentType,
+ constructorOptions: GetResponseActionsClientConstructorOptions
+): ResponseActionsClient => {
+ switch (agentType) {
+ case 'endpoint':
+ return new EndpointActionsClient(constructorOptions);
+ case 'sentinel_one':
+ return new SentinelOneActionsClient(constructorOptions);
+ }
+
+ throw new UnsupportedResponseActionsAgentTypeError(
+ `Agent type [${agentType}] does not support response actions`
+ );
+};
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts
index 83a11352dfe950..dba0f0771de1f8 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/index.ts
@@ -5,4 +5,8 @@
* 2.0.
*/
-export { EndpointActionsClient } from './endpoint_actions_client';
+export * from './endpoint_actions_client';
+export * from './sentinelone/sentinel_one_actions_client';
+export * from './get_response_actions_client';
+
+export * from './lib/types';
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts
new file mode 100644
index 00000000000000..7b6d991e28f0bc
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.test.ts
@@ -0,0 +1,515 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ResponseActionsClient } from './types';
+import type {
+ ResponseActionsClientUpdateCasesOptions,
+ ResponseActionsClientWriteActionRequestToEndpointIndexOptions,
+ ResponseActionsClientWriteActionResponseToEndpointIndexOptions,
+} from './base_response_actions_client';
+import { ResponseActionsClientImpl } from './base_response_actions_client';
+import type {
+ ActionDetails,
+ LogsEndpointAction,
+ LogsEndpointActionResponse,
+} from '../../../../../../common/endpoint/types';
+import type { EndpointAppContextService } from '../../../../endpoint_app_context_services';
+import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
+import { ResponseActionsClientError, ResponseActionsNotSupportedError } from '../errors';
+import type { CasesClientMock } from '@kbn/cases-plugin/server/client/mocks';
+import type { CasesByAlertIDParams } from '@kbn/cases-plugin/server/client/cases/get';
+import type { Logger } from '@kbn/logging';
+import { getActionDetailsById as _getActionDetailsById } from '../../action_details_by_id';
+import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
+import type { TransportResult } from '@elastic/elasticsearch';
+import { ENDPOINT_ACTIONS_INDEX } from '../../../../../../common/endpoint/constants';
+import type { DeepMutable } from '../../../../../../common/endpoint/types/utility_types';
+import { set } from 'lodash';
+import { responseActionsClientMock } from '../mocks';
+
+jest.mock('../../action_details_by_id', () => {
+ const original = jest.requireActual('../../action_details_by_id');
+
+ return {
+ ...original,
+ getActionDetailsById: jest.fn(original.getActionDetailsById),
+ };
+});
+
+const getActionDetailsByIdMock = _getActionDetailsById as jest.Mock;
+
+describe('ResponseActionsClientImpl base class', () => {
+ let esClient: ElasticsearchClientMock;
+ let endpointAppContextService: EndpointAppContextService;
+ let baseClassMock: MockClassWithExposedProtectedMembers;
+ let casesClient: CasesClientMock;
+ let logger: Logger;
+
+ beforeEach(async () => {
+ const constructorOptions = responseActionsClientMock.createConstructorOptions();
+
+ esClient = constructorOptions.esClient;
+ casesClient = constructorOptions.casesClient;
+ endpointAppContextService = constructorOptions.endpointService;
+ logger = endpointAppContextService.createLogger();
+ baseClassMock = new MockClassWithExposedProtectedMembers(constructorOptions);
+ });
+
+ afterEach(() => {
+ getActionDetailsByIdMock.mockClear();
+ });
+
+ describe('Public methods', () => {
+ const methods: Array = [
+ 'isolate',
+ 'release',
+ 'killProcess',
+ 'suspendProcess',
+ 'runningProcesses',
+ 'getFile',
+ 'execute',
+ 'upload',
+ ];
+
+ it.each(methods)('should throw Not Supported error for %s()', async (method) => {
+ // @ts-expect-error ignoring input type to method since they all should throw
+ const responsePromise = baseClassMock[method]({});
+
+ await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsNotSupportedError);
+ await expect(responsePromise).rejects.toHaveProperty('statusCode', 405);
+ });
+ });
+
+ describe('#updateCases()', () => {
+ const KNOWN_ALERT_ID_1 = 'alert-1';
+ const KNOWN_ALERT_ID_2 = 'alert-2';
+ const KNOWN_ALERT_ID_3 = 'alert-3';
+
+ let updateCasesOptions: Required;
+
+ beforeEach(async () => {
+ (casesClient.cases.getCasesByAlertID as jest.Mock).mockImplementation(
+ async ({ alertID }: CasesByAlertIDParams) => {
+ if (alertID === KNOWN_ALERT_ID_1) {
+ return [{ id: 'case-1' }, { id: 'case-2' }, { id: 'case-3' }];
+ }
+
+ if (alertID === KNOWN_ALERT_ID_2) {
+ return [{ id: 'case-3' }];
+ }
+
+ if (alertID === KNOWN_ALERT_ID_3) {
+ return [];
+ }
+
+ throw new Error('test: alert id not found');
+ }
+ );
+
+ updateCasesOptions = {
+ command: 'isolate',
+ caseIds: ['case-999'],
+ alertIds: [KNOWN_ALERT_ID_1, KNOWN_ALERT_ID_2, KNOWN_ALERT_ID_3],
+ comment: 'this is a case comment',
+ hosts: [
+ {
+ hostId: '1-2-3',
+ hostname: 'foo-one',
+ },
+ {
+ hostId: '4-5-6',
+ hostname: 'foo-two',
+ },
+ ],
+ };
+ });
+
+ it('should do nothing if no caseIds nor alertIds are provided', async () => {
+ updateCasesOptions.caseIds.length = 0;
+ updateCasesOptions.alertIds.length = 0;
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(casesClient.cases.getCasesByAlertID).not.toHaveBeenCalled();
+ expect(casesClient.attachments.bulkCreate).not.toHaveBeenCalled();
+ expect(logger.debug).toHaveBeenCalledWith(
+ "Nothing to do. 'caseIds' and 'alertIds' are empty"
+ );
+ });
+
+ it('should do nothing if no hosts were provided', async () => {
+ updateCasesOptions.hosts.length = 0;
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(casesClient.cases.getCasesByAlertID).not.toHaveBeenCalled();
+ expect(casesClient.attachments.bulkCreate).not.toHaveBeenCalled();
+ expect(logger.debug).toHaveBeenCalledWith("Nothing to do. 'hosts' is empty");
+ });
+
+ it('should do nothing if cases client was not provided', async () => {
+ const mockInstance = new MockClassWithExposedProtectedMembers({
+ esClient,
+ endpointService: endpointAppContextService,
+ username: 'foo',
+ });
+ await mockInstance.updateCases(updateCasesOptions);
+
+ expect(casesClient.cases.getCasesByAlertID).not.toHaveBeenCalled();
+ expect(casesClient.attachments.bulkCreate).not.toHaveBeenCalled();
+ expect(logger.debug).toHaveBeenCalledWith(
+ 'No CasesClient available. Skipping updates to Cases!'
+ );
+ });
+
+ it('should retrieve caseIds from alerts if alertIds was provided', async () => {
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(casesClient.cases.getCasesByAlertID).toHaveBeenCalledTimes(3);
+ });
+
+ it('should not error is retrieving case id for alert fails', async () => {
+ updateCasesOptions.alertIds.push('invalid-alert-id');
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(casesClient.cases.getCasesByAlertID).toHaveBeenCalledTimes(4);
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Attempt to get cases for alertID [invalid-alert-id]')
+ );
+ });
+
+ it('should do nothing if alertIDs were not associated with any cases', async () => {
+ updateCasesOptions.caseIds.length = 0;
+ updateCasesOptions.alertIds = [KNOWN_ALERT_ID_3];
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(logger.debug).toHaveBeenCalledWith(`Nothing to do. Alert IDs are not tied to Cases`);
+ });
+
+ it('should update cases with an attachment for each host', async () => {
+ const updateResponse = await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(updateResponse).toBeUndefined();
+ expect(casesClient.attachments.bulkCreate).toHaveBeenCalledTimes(4);
+ expect(casesClient.attachments.bulkCreate).toHaveBeenLastCalledWith({
+ attachments: [
+ {
+ actions: {
+ targets: [
+ {
+ endpointId: '1-2-3',
+ hostname: 'foo-one',
+ },
+ {
+ endpointId: '4-5-6',
+ hostname: 'foo-two',
+ },
+ ],
+ type: 'isolate',
+ },
+ comment: 'this is a case comment',
+ owner: 'securitySolution',
+ type: 'actions',
+ },
+ {
+ actions: {
+ targets: [
+ {
+ endpointId: '1-2-3',
+ hostname: 'foo-one',
+ },
+ {
+ endpointId: '4-5-6',
+ hostname: 'foo-two',
+ },
+ ],
+ type: 'isolate',
+ },
+ comment: 'this is a case comment',
+ owner: 'securitySolution',
+ type: 'actions',
+ },
+ {
+ actions: {
+ targets: [
+ {
+ endpointId: '1-2-3',
+ hostname: 'foo-one',
+ },
+ {
+ endpointId: '4-5-6',
+ hostname: 'foo-two',
+ },
+ ],
+ type: 'isolate',
+ },
+ comment: 'this is a case comment',
+ owner: 'securitySolution',
+ type: 'actions',
+ },
+ {
+ actions: {
+ targets: [
+ {
+ endpointId: '1-2-3',
+ hostname: 'foo-one',
+ },
+ {
+ endpointId: '4-5-6',
+ hostname: 'foo-two',
+ },
+ ],
+ type: 'isolate',
+ },
+ comment: 'this is a case comment',
+ owner: 'securitySolution',
+ type: 'actions',
+ },
+ ],
+ caseId: 'case-3',
+ });
+ });
+
+ it('should not error if update to a case fails', async () => {
+ (casesClient.attachments.bulkCreate as jest.Mock).mockImplementation(async (options) => {
+ if (options.caseId === 'case-2') {
+ throw new Error('update filed to case-2');
+ }
+ });
+ await baseClassMock.updateCases(updateCasesOptions);
+
+ expect(logger.warn).toHaveBeenCalledWith(
+ expect.stringContaining('Attempt to update case ID [case-2] failed:')
+ );
+ });
+ });
+
+ describe('#fetchActionDetails()', () => {
+ it('should retrieve action details', async () => {
+ await baseClassMock.fetchActionDetails('one').catch(() => {
+ // just ignoring error
+ });
+
+ expect(getActionDetailsByIdMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.anything(),
+ 'one'
+ );
+ });
+ });
+
+ describe('#writeActionRequestToEndpointIndex()', () => {
+ let esIndexDocResponse: TransportResult;
+ let indexDocOptions: DeepMutable;
+ let expectedIndexDoc: LogsEndpointAction;
+
+ beforeEach(() => {
+ esIndexDocResponse = {
+ body: {
+ result: 'created',
+ _id: '123',
+ _index: ENDPOINT_ACTIONS_INDEX,
+ _version: 1,
+ },
+ statusCode: 201,
+ headers: {},
+ meta: {},
+ warnings: null,
+ } as TransportResult;
+
+ indexDocOptions = {
+ command: 'isolate',
+ agent_type: 'endpoint',
+ endpoint_ids: ['one'],
+ comment: 'test comment',
+ rule_name: undefined,
+ rule_id: undefined,
+ alert_ids: undefined,
+ case_ids: undefined,
+ hosts: undefined,
+ parameters: undefined,
+ file: undefined,
+ };
+
+ expectedIndexDoc = {
+ '@timestamp': expect.any(String),
+ EndpointActions: {
+ action_id: expect.any(String),
+ data: {
+ command: 'isolate',
+ comment: 'test comment',
+ },
+ expiration: expect.any(String),
+ input_type: 'endpoint',
+ type: 'INPUT_ACTION',
+ },
+ agent: {
+ id: ['one'],
+ },
+ user: {
+ id: 'foo',
+ },
+ };
+
+ // @ts-expect-error TS2345: Argument of type... Due to the fact that the method definition is overloaded
+ esClient.index.mockResolvedValue(esIndexDocResponse);
+ });
+
+ it('should return indexed record on success', async () => {
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should set `EndpointActions.input_type` to the correct value', async () => {
+ indexDocOptions.agent_type = 'sentinel_one';
+ set(expectedIndexDoc, 'EndpointActions.input_type', 'sentinel_one');
+
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should include alert_ids if any were provided', async () => {
+ indexDocOptions.alert_ids = ['one', 'two'];
+ set(expectedIndexDoc, 'EndpointActions.data.alert_id', indexDocOptions.alert_ids);
+
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should include hosts if any where provided', async () => {
+ indexDocOptions.hosts = { hostA: { name: 'host a' } };
+ set(expectedIndexDoc, 'EndpointActions.data.hosts', indexDocOptions.hosts);
+
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should include Rule information if rule_id and rule_name were provided', async () => {
+ indexDocOptions.rule_id = '1-2-3';
+ indexDocOptions.rule_name = 'rule 123';
+ expectedIndexDoc.rule = {
+ name: indexDocOptions.rule_name,
+ id: indexDocOptions.rule_id,
+ };
+
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should NOT include Rule information if rule_id or rule_name are missing', async () => {
+ indexDocOptions.rule_id = '1-2-3';
+
+ await expect(
+ baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions)
+ ).resolves.toEqual(expectedIndexDoc);
+ });
+
+ it('should error if index of document did not return a 201', async () => {
+ esIndexDocResponse.statusCode = 200;
+ const responsePromise = baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions);
+
+ await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsClientError);
+ await expect(responsePromise).rejects.toHaveProperty('statusCode', 500);
+ });
+
+ it('should throw ResponseActionsClientError if operation fails', async () => {
+ esClient.index.mockImplementation(async () => {
+ throw new Error('test error');
+ });
+ const responsePromise = baseClassMock.writeActionRequestToEndpointIndex(indexDocOptions);
+
+ await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsClientError);
+ await expect(responsePromise).rejects.toHaveProperty('statusCode', 500);
+ await expect(responsePromise).rejects.toHaveProperty(
+ 'message',
+ expect.stringContaining('Failed to create action request document:')
+ );
+ });
+ });
+
+ describe('#writeActionResponseToEndpointIndex()', () => {
+ let actionResponseOptions: ResponseActionsClientWriteActionResponseToEndpointIndexOptions;
+
+ beforeEach(() => {
+ actionResponseOptions = {
+ actionId: '1-2-3',
+ agentId: '123',
+ error: { message: 'test error' },
+ data: {
+ command: 'isolate',
+ comment: 'some comment',
+ output: undefined,
+ },
+ };
+ });
+
+ it('should return indexed record on success', async () => {
+ await expect(
+ baseClassMock.writeActionResponseToEndpointIndex(actionResponseOptions)
+ ).resolves.toEqual({
+ '@timestamp': expect.any(String),
+ EndpointActions: {
+ action_id: '1-2-3',
+ completed_at: expect.any(String),
+ data: {
+ command: 'isolate',
+ comment: 'some comment',
+ },
+ started_at: expect.any(String),
+ },
+ agent: {
+ id: '123',
+ },
+ error: {
+ message: 'test error',
+ },
+ });
+ });
+
+ it('should throw ResponseActionsClientError if operation fails', async () => {
+ esClient.index.mockImplementation(async () => {
+ throw new Error('oh oh');
+ });
+ const responsePromise =
+ baseClassMock.writeActionResponseToEndpointIndex(actionResponseOptions);
+
+ await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsClientError);
+ await expect(responsePromise).rejects.toHaveProperty(
+ 'message',
+ expect.stringContaining('Failed to create action response document: ')
+ );
+ await expect(responsePromise).rejects.toHaveProperty('statusCode', 500);
+ });
+ });
+});
+
+class MockClassWithExposedProtectedMembers extends ResponseActionsClientImpl {
+ public async updateCases(options: ResponseActionsClientUpdateCasesOptions): Promise {
+ return super.updateCases(options);
+ }
+
+ public async fetchActionDetails(
+ actionId: string
+ ): Promise {
+ return super.fetchActionDetails(actionId);
+ }
+
+ public async writeActionRequestToEndpointIndex(
+ actionRequest: ResponseActionsClientWriteActionRequestToEndpointIndexOptions
+ ): Promise {
+ return super.writeActionRequestToEndpointIndex(actionRequest);
+ }
+
+ public async writeActionResponseToEndpointIndex(
+ options: ResponseActionsClientWriteActionResponseToEndpointIndexOptions
+ ): Promise> {
+ return super.writeActionResponseToEndpointIndex(options);
+ }
+}
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts
new file mode 100644
index 00000000000000..cea80bd3f9bcda
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts
@@ -0,0 +1,366 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
+import type { CasesClient } from '@kbn/cases-plugin/server';
+import type { Logger } from '@kbn/logging';
+import { v4 as uuidv4 } from 'uuid';
+import { AttachmentType } from '@kbn/cases-plugin/common';
+import type { BulkCreateArgs } from '@kbn/cases-plugin/server/client/attachments/types';
+import type { EndpointAppContextService } from '../../../../endpoint_app_context_services';
+import { APP_ID } from '../../../../../../common';
+import type { ResponseActionsApiCommandNames } from '../../../../../../common/endpoint/service/response_actions/constants';
+import { getActionDetailsById } from '../../action_details_by_id';
+import { ResponseActionsClientError, ResponseActionsNotSupportedError } from '../errors';
+import {
+ addRuleInfoToAction,
+ getActionParameters,
+ getActionRequestExpiration,
+} from '../../create/write_action_to_indices';
+import {
+ ENDPOINT_ACTION_RESPONSES_INDEX,
+ ENDPOINT_ACTIONS_INDEX,
+} from '../../../../../../common/endpoint/constants';
+import type { ResponseActionsClient } from './types';
+import type {
+ ActionDetails,
+ GetProcessesActionOutputContent,
+ KillOrSuspendProcessRequestBody,
+ KillProcessActionOutputContent,
+ ResponseActionExecuteOutputContent,
+ ResponseActionGetFileOutputContent,
+ ResponseActionGetFileParameters,
+ ResponseActionParametersWithPidOrEntityId,
+ ResponseActionsExecuteParameters,
+ ResponseActionUploadOutputContent,
+ ResponseActionUploadParameters,
+ SuspendProcessActionOutputContent,
+ LogsEndpointAction,
+ EndpointActionDataParameterTypes,
+ LogsEndpointActionResponse,
+} from '../../../../../../common/endpoint/types';
+import type {
+ IsolationRouteRequestBody,
+ ExecuteActionRequestBody,
+ GetProcessesRequestBody,
+ ResponseActionGetFileRequestBody,
+ UploadActionApiRequestBody,
+ ResponseActionsRequestBody,
+} from '../../../../../../common/api/endpoint';
+import type { CreateActionPayload } from '../../create/types';
+import { dump } from '../../../../utils/dump';
+
+export interface ResponseActionsClientOptions {
+ endpointService: EndpointAppContextService;
+ esClient: ElasticsearchClient;
+ casesClient?: CasesClient;
+ /** Username that will be stored along with the action's ES documents */
+ username: string;
+}
+
+export interface ResponseActionsClientUpdateCasesOptions {
+ /** The Response Action that was taken */
+ command: ResponseActionsApiCommandNames;
+ /** the list of hosts that received the response action `command` */
+ hosts: Array<{
+ hostname: string;
+ hostId: string;
+ }>;
+ caseIds?: string[];
+ /** If defined, any case that the alert is included in will also receive an update */
+ alertIds?: string[];
+ /** Comment to include in the Case attachment */
+ comment?: string;
+}
+
+export type ResponseActionsClientWriteActionRequestToEndpointIndexOptions =
+ ResponseActionsRequestBody &
+ Pick;
+
+export type ResponseActionsClientWriteActionResponseToEndpointIndexOptions<
+ TOutputContent extends object = object
+> = {
+ agentId: LogsEndpointActionResponse['agent']['id'];
+ actionId: string;
+} & Pick &
+ Pick['EndpointActions'], 'data'>;
+
+/**
+ * Base class for a Response Actions client
+ */
+export class ResponseActionsClientImpl implements ResponseActionsClient {
+ protected readonly log: Logger;
+
+ constructor(protected readonly options: ResponseActionsClientOptions) {
+ this.log = options.endpointService.createLogger(
+ this.constructor.name ?? 'ResponseActionsClient'
+ );
+ }
+
+ /**
+ * Update cases with information about the hosts that received a response action.
+ *
+ * **NOTE:** Failures during update will not cause this operation to fail - it will only log the errors
+ * @protected
+ */
+ protected async updateCases({
+ command,
+ hosts,
+ caseIds = [],
+ alertIds = [],
+ comment = '',
+ }: ResponseActionsClientUpdateCasesOptions): Promise {
+ if (caseIds.length === 0 && alertIds.length === 0) {
+ this.log.debug(`Nothing to do. 'caseIds' and 'alertIds' are empty`);
+ return;
+ }
+
+ if (hosts.length === 0) {
+ this.log.debug(`Nothing to do. 'hosts' is empty`);
+ return;
+ }
+
+ const casesClient = this.options.casesClient;
+
+ if (!casesClient) {
+ this.log.debug(`No CasesClient available. Skipping updates to Cases!`);
+ return;
+ }
+
+ const casesFromAlertIds = await Promise.all(
+ alertIds.map((alertID) => {
+ return casesClient.cases
+ .getCasesByAlertID({ alertID, options: { owner: APP_ID } })
+ .then((casesFound) => {
+ return casesFound.map((caseInfo) => caseInfo.id);
+ })
+ .catch((err) => {
+ this.log.warn(
+ `Attempt to get cases for alertID [${alertID}][owner: ${APP_ID}] failed with: ${err.message}`
+ );
+
+ // We don't fail everything here. Just log it and keep going
+ return [] as string[];
+ });
+ })
+ ).then((results) => {
+ return results.flat();
+ });
+
+ const allCases = [...new Set([...caseIds, ...casesFromAlertIds])];
+
+ if (allCases.length === 0) {
+ this.log.debug(`Nothing to do. Alert IDs are not tied to Cases`);
+ return;
+ }
+
+ this.log.debug(`Updating cases:\n${dump(allCases)}`);
+
+ // Create an attachment for each case that includes info. about the response actions taken against the hosts
+ const attachments = allCases.map(() => ({
+ type: AttachmentType.actions,
+ comment,
+ actions: {
+ targets: hosts.map(({ hostId: endpointId, hostname }) => ({ endpointId, hostname })),
+ type: command,
+ },
+ owner: APP_ID,
+ })) as BulkCreateArgs['attachments'];
+
+ const casesUpdateResponse = await Promise.all(
+ allCases.map((caseId) =>
+ casesClient.attachments
+ .bulkCreate({
+ caseId,
+ attachments,
+ })
+ .catch((err) => {
+ // Log any error, BUT: do not fail execution
+ this.log.warn(
+ `Attempt to update case ID [${caseId}] failed: ${err.message}\n${dump(err)}`
+ );
+ return null;
+ })
+ )
+ );
+
+ this.log.debug(`Update to cases done:\n${dump(casesUpdateResponse)}`);
+ }
+
+ /**
+ * Returns the action details for a given response action id
+ * @param actionId
+ * @protected
+ */
+ protected async fetchActionDetails(
+ actionId: string
+ ): Promise {
+ return getActionDetailsById(
+ this.options.esClient,
+ this.options.endpointService.getEndpointMetadataService(),
+ actionId
+ );
+ }
+
+ /**
+ * Creates a Response Action request document in the Endpoint index (`.logs-endpoint.actions-default`)
+ * @protected
+ */
+ protected async writeActionRequestToEndpointIndex(
+ actionRequest: ResponseActionsClientWriteActionRequestToEndpointIndexOptions
+ ): Promise {
+ const doc: LogsEndpointAction = {
+ '@timestamp': new Date().toISOString(),
+ agent: {
+ id: actionRequest.endpoint_ids,
+ },
+ EndpointActions: {
+ action_id: uuidv4(),
+ expiration: getActionRequestExpiration(),
+ type: 'INPUT_ACTION',
+ input_type: actionRequest.agent_type ?? 'endpoint',
+ data: {
+ command: actionRequest.command,
+ comment: actionRequest.comment ?? undefined,
+ ...(actionRequest.alert_ids ? { alert_id: actionRequest.alert_ids } : {}),
+ ...(actionRequest.hosts ? { hosts: actionRequest.hosts } : {}),
+ parameters: getActionParameters(actionRequest) as EndpointActionDataParameterTypes,
+ },
+ },
+ user: {
+ id: this.options.username,
+ },
+ ...addRuleInfoToAction(actionRequest),
+ };
+
+ try {
+ const logsEndpointActionsResult = await this.options.esClient.index(
+ {
+ index: ENDPOINT_ACTIONS_INDEX,
+ document: doc,
+ refresh: 'wait_for',
+ },
+ { meta: true }
+ );
+
+ if (logsEndpointActionsResult.statusCode !== 201) {
+ throw new ResponseActionsClientError(
+ `Failed to create (index) action request document. StatusCode: [${logsEndpointActionsResult.statusCode}] Result: ${logsEndpointActionsResult.body.result}`,
+ 500,
+ logsEndpointActionsResult
+ );
+ }
+
+ return doc;
+ } catch (err) {
+ if (!(err instanceof ResponseActionsClientError)) {
+ throw new ResponseActionsClientError(
+ `Failed to create action request document: ${err.message}`,
+ 500,
+ err
+ );
+ }
+
+ throw err;
+ }
+ }
+
+ /**
+ * Writes a Response Action response document to the Endpoint index
+ * @param options
+ * @protected
+ */
+ protected async writeActionResponseToEndpointIndex({
+ actionId,
+ error,
+ agentId,
+ data,
+ }: ResponseActionsClientWriteActionResponseToEndpointIndexOptions): Promise<
+ LogsEndpointActionResponse
+ > {
+ const timestamp = new Date().toISOString();
+ const doc: LogsEndpointActionResponse = {
+ '@timestamp': timestamp,
+ agent: {
+ id: agentId,
+ },
+ EndpointActions: {
+ action_id: actionId,
+ started_at: timestamp,
+ completed_at: timestamp,
+ data,
+ },
+ error,
+ };
+
+ this.log.debug(`Writing response action response:\n${dump(doc)}`);
+
+ await this.options.esClient
+ .index({
+ index: ENDPOINT_ACTION_RESPONSES_INDEX,
+ document: doc,
+ refresh: 'wait_for',
+ })
+ .catch((err) => {
+ throw new ResponseActionsClientError(
+ `Failed to create action response document: ${err.message}`,
+ err.statusCode ?? 500,
+ err
+ );
+ });
+
+ return doc;
+ }
+
+ public async isolate(options: IsolationRouteRequestBody): Promise {
+ throw new ResponseActionsNotSupportedError('isolate');
+ }
+
+ public async release(options: IsolationRouteRequestBody): Promise {
+ throw new ResponseActionsNotSupportedError('unisolate');
+ }
+
+ public async killProcess(
+ options: KillOrSuspendProcessRequestBody
+ ): Promise<
+ ActionDetails
+ > {
+ throw new ResponseActionsNotSupportedError('kill-process');
+ }
+
+ public async suspendProcess(
+ options: KillOrSuspendProcessRequestBody
+ ): Promise<
+ ActionDetails
+ > {
+ throw new ResponseActionsNotSupportedError('suspend-process');
+ }
+
+ public async runningProcesses(
+ options: GetProcessesRequestBody
+ ): Promise> {
+ throw new ResponseActionsNotSupportedError('running-processes');
+ }
+
+ public async getFile(
+ options: ResponseActionGetFileRequestBody
+ ): Promise> {
+ throw new ResponseActionsNotSupportedError('get-file');
+ }
+
+ public async execute(
+ options: ExecuteActionRequestBody
+ ): Promise> {
+ throw new ResponseActionsNotSupportedError('execute');
+ }
+
+ public async upload(
+ options: UploadActionApiRequestBody
+ ): Promise> {
+ throw new ResponseActionsNotSupportedError('upload');
+ }
+}
diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts
similarity index 93%
rename from x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts
rename to x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts
index 5578128cf4248c..c6407dceae9ddb 100644
--- a/x-pack/plugins/security_solution/server/endpoint/lib/response_actions/types.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts
@@ -18,19 +18,19 @@ import type {
ResponseActionsExecuteParameters,
ResponseActionUploadOutputContent,
ResponseActionUploadParameters,
-} from '../../../../common/endpoint/types';
+} from '../../../../../../common/endpoint/types';
import type {
IsolationRouteRequestBody,
GetProcessesRequestBody,
ResponseActionGetFileRequestBody,
ExecuteActionRequestBody,
UploadActionApiRequestBody,
-} from '../../../../common/api/endpoint';
+} from '../../../../../../common/api/endpoint';
/**
* The interface required for a Response Actions provider
*/
-export interface ResponseActionsProvider {
+export interface ResponseActionsClient {
isolate: (options: IsolationRouteRequestBody) => Promise;
release: (options: IsolationRouteRequestBody) => Promise;
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts
new file mode 100644
index 00000000000000..27773a898b9329
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/mocks.ts
@@ -0,0 +1,209 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ActionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock';
+import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock';
+import type { ConnectorWithExtraFindData } from '@kbn/actions-plugin/server/application/connector/types';
+import { SENTINELONE_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/common/sentinelone/constants';
+import type { DeepPartial } from 'utility-types';
+import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common';
+import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
+import type { CasesClientMock } from '@kbn/cases-plugin/server/client/mocks';
+import { createCasesClientMock } from '@kbn/cases-plugin/server/client/mocks';
+import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';
+import { merge } from 'lodash';
+import type * as esTypes from '@elastic/elasticsearch/lib/api/types';
+import type { TransportResult } from '@elastic/elasticsearch';
+import type { AttachmentsSubClient } from '@kbn/cases-plugin/server/client/attachments/client';
+import { BaseDataGenerator } from '../../../../../common/endpoint/data_generators/base_data_generator';
+import {
+ createActionRequestsEsSearchResultsMock,
+ createActionResponsesEsSearchResultsMock,
+} from '../mocks';
+import {
+ ENDPOINT_ACTION_RESPONSES_INDEX,
+ ENDPOINT_ACTIONS_INDEX,
+} from '../../../../../common/endpoint/constants';
+import type { DeepMutable } from '../../../../../common/endpoint/types/utility_types';
+import { EndpointAppContextService } from '../../../endpoint_app_context_services';
+import {
+ createMockEndpointAppContextServiceSetupContract,
+ createMockEndpointAppContextServiceStartContract,
+} from '../../../mocks';
+import type { IsolationRouteRequestBody } from '../../../../../common/api/endpoint';
+import type { ResponseActionsClientOptions } from './lib/base_response_actions_client';
+import { ACTION_RESPONSE_INDICES } from '../constants';
+
+export interface ResponseActionsClientOptionsMock extends ResponseActionsClientOptions {
+ esClient: ElasticsearchClientMock;
+ casesClient?: CasesClientMock;
+}
+
+const createConstructorOptionsMock = (): Required => {
+ const esClient = elasticsearchServiceMock.createScopedClusterClient().asInternalUser;
+ const casesClient = createCasesClientMock();
+ const endpointService = new EndpointAppContextService();
+
+ esClient.index.mockImplementation((async (payload) => {
+ switch (payload.index) {
+ case ENDPOINT_ACTIONS_INDEX:
+ case ENDPOINT_ACTION_RESPONSES_INDEX:
+ return createEsIndexTransportResponseMock({ body: { _index: payload.index } });
+ default:
+ throw new Error(`no esClient.index() mock defined for index ${payload.index}`);
+ }
+ }) as typeof esClient.index);
+
+ esClient.search.mockImplementation(async (payload) => {
+ if (payload) {
+ switch (payload.index) {
+ case ENDPOINT_ACTIONS_INDEX:
+ return createActionRequestsEsSearchResultsMock();
+ case ACTION_RESPONSE_INDICES:
+ return createActionResponsesEsSearchResultsMock();
+ }
+ }
+
+ return BaseDataGenerator.toEsSearchResponse([]);
+ });
+
+ (casesClient.attachments.bulkCreate as jest.Mock).mockImplementation(
+ (async () => {}) as unknown as jest.Mocked['bulkCreate']
+ );
+
+ endpointService.setup(createMockEndpointAppContextServiceSetupContract());
+ endpointService.start(createMockEndpointAppContextServiceStartContract());
+
+ return {
+ esClient,
+ casesClient,
+ endpointService,
+ username: 'foo',
+ };
+};
+
+const createEsIndexTransportResponseMock = (
+ overrides: DeepPartial> = {}
+): TransportResult => {
+ const responseDoc: TransportResult = {
+ body: {
+ _id: 'indexed-1-2-3',
+ _index: 'some-index',
+ _primary_term: 1,
+ result: 'created',
+ _seq_no: 1,
+ _shards: {
+ failed: 0,
+ successful: 1,
+ total: 1,
+ },
+ _version: 1,
+ },
+ statusCode: 201,
+ headers: {},
+ warnings: null,
+ meta: {
+ context: {},
+ name: 'foo',
+ request: {
+ params: {
+ method: 'GET',
+ path: 'some/path',
+ },
+ options: {},
+ id: 'some-id',
+ },
+ connection: null,
+ attempts: 1,
+ aborted: false,
+ },
+ };
+
+ return merge(responseDoc, overrides);
+};
+
+const createIsolateOptionsMock = (
+ overrides: Partial = {}
+): DeepMutable => {
+ const isolateOptions: IsolationRouteRequestBody = {
+ agent_type: 'endpoint',
+ endpoint_ids: ['1-2-3'],
+ comment: 'test comment',
+ };
+
+ return merge(isolateOptions, overrides);
+};
+
+const createConnectorActionsClientMock = (): ActionsClientMock => {
+ const client = actionsClientMock.create();
+
+ // Mock result of retrieving list of connectors
+ (client.getAll as jest.Mock).mockImplementation(async () => {
+ const result: ConnectorWithExtraFindData[] = [
+ // SentinelOne connector
+ createConnectorMock({
+ actionTypeId: SENTINELONE_CONNECTOR_ID,
+ id: 's1-connector-instance-id',
+ }),
+ ];
+
+ return result;
+ });
+
+ (client.execute as jest.Mock).mockImplementation(async () => {
+ return createConnectorAcitonExecuteResponseMock();
+ });
+
+ return client;
+};
+
+const createConnectorMock = (
+ overrides: DeepPartial = {}
+): ConnectorWithExtraFindData => {
+ return merge(
+ {
+ id: 'connector-mock-id-1',
+ actionTypeId: '.some-type',
+ name: 'some mock name',
+ isMissingSecrets: false,
+ config: {},
+ isPreconfigured: false,
+ isDeprecated: false,
+ isSystemAction: false,
+ referencedByCount: 0,
+ },
+ overrides
+ );
+};
+
+const createConnectorAcitonExecuteResponseMock = (
+ overrides: DeepPartial> = {}
+): ActionTypeExecutorResult<{}> => {
+ const result: ActionTypeExecutorResult<{}> = {
+ actionId: 'execute-response-mock-1',
+ data: undefined,
+ message: 'some mock message',
+ serviceMessage: 'some mock service message',
+ retry: true,
+ status: 'ok',
+ };
+
+ return merge(result, overrides);
+};
+
+export const responseActionsClientMock = Object.freeze({
+ createConstructorOptions: createConstructorOptionsMock,
+ createIsolateOptions: createIsolateOptionsMock,
+ createReleaseOptions: createIsolateOptionsMock,
+ // TODO:PT add more methods to get option mocks for other class methods
+
+ createIndexedResponse: createEsIndexTransportResponseMock,
+
+ createConnectorActionsClient: createConnectorActionsClientMock,
+ createConnector: createConnectorMock,
+ createConnectorActionExecuteResponse: createConnectorAcitonExecuteResponseMock,
+});
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts
new file mode 100644
index 00000000000000..a56b54ce2e65fa
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ResponseActionsClient } from '../lib/types';
+import { responseActionsClientMock } from '../mocks';
+import type { SentinelOneActionsClientOptions } from '../../..';
+import { SentinelOneActionsClient } from '../../..';
+import { getActionDetailsById as _getActionDetailsById } from '../../action_details_by_id';
+import { ResponseActionsClientError, ResponseActionsNotSupportedError } from '../errors';
+import type { ActionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock';
+
+jest.mock('../../action_details_by_id', () => {
+ const originalMod = jest.requireActual('../../action_details_by_id');
+
+ return {
+ ...originalMod,
+ getActionDetailsById: jest.fn(originalMod.getActionDetailsById),
+ };
+});
+
+const getActionDetailsByIdMock = _getActionDetailsById as jest.Mock;
+
+describe('SentinelOneActionsClient class', () => {
+ let classConstructorOptions: SentinelOneActionsClientOptions;
+ let s1ActionsClient: ResponseActionsClient;
+ let connectorActionsMock: ActionsClientMock;
+
+ const createS1IsolateOptions = () =>
+ responseActionsClientMock.createIsolateOptions({ agent_type: 'sentinel_one' });
+
+ beforeEach(() => {
+ connectorActionsMock = responseActionsClientMock.createConnectorActionsClient();
+
+ connectorActionsMock.getAll();
+
+ classConstructorOptions = {
+ ...responseActionsClientMock.createConstructorOptions(),
+ connectorActions: connectorActionsMock,
+ };
+ s1ActionsClient = new SentinelOneActionsClient(classConstructorOptions);
+ });
+
+ it.each([
+ 'release',
+ 'killProcess',
+ 'suspendProcess',
+ 'runningProcesses',
+ 'getFile',
+ 'execute',
+ 'upload',
+ ] as Array)(
+ 'should throw an un-supported error for %s',
+ async (methodName) => {
+ // @ts-expect-error Purposely passing in empty object for options
+ await expect(s1ActionsClient[methodName]({})).rejects.toBeInstanceOf(
+ ResponseActionsNotSupportedError
+ );
+ }
+ );
+
+ it('should error if unable to retrieve list of connectors', async () => {
+ connectorActionsMock.getAll.mockImplementation(async () => {
+ throw new Error('oh oh');
+ });
+ const responsePromise = s1ActionsClient.isolate(createS1IsolateOptions());
+
+ await expect(responsePromise).rejects.toBeInstanceOf(ResponseActionsClientError);
+ await expect(responsePromise).rejects.toHaveProperty(
+ 'message',
+ expect.stringContaining('Unable to retrieve list of stack connectors:')
+ );
+ await expect(responsePromise).rejects.toHaveProperty('statusCode', 400);
+ });
+
+ it('should error if retrieving connectors fails', async () => {
+ (connectorActionsMock.getAll as jest.Mock).mockImplementation(async () => {
+ throw new Error('oh oh');
+ });
+
+ await expect(s1ActionsClient.isolate(createS1IsolateOptions())).rejects.toMatchObject({
+ message: `Unable to retrieve list of stack connectors: oh oh`,
+ statusCode: 400,
+ });
+ });
+
+ it.each([
+ ['no connector defined', async () => []],
+ [
+ 'deprecated connector',
+ async () => [responseActionsClientMock.createConnector({ isDeprecated: true })],
+ ],
+ [
+ 'missing secrets',
+ async () => [responseActionsClientMock.createConnector({ isMissingSecrets: true })],
+ ],
+ ])('should error if: %s', async (_, getAllImplementation) => {
+ (connectorActionsMock.getAll as jest.Mock).mockImplementation(getAllImplementation);
+
+ await expect(s1ActionsClient.isolate(createS1IsolateOptions())).rejects.toMatchObject({
+ message: `No SentinelOne stack connector found`,
+ statusCode: 400,
+ });
+ });
+
+ it('should error if multiple agent ids are received', async () => {
+ const payload = createS1IsolateOptions();
+ payload.endpoint_ids.push('second-host-id');
+
+ await expect(s1ActionsClient.isolate(payload)).rejects.toMatchObject({
+ message: `[body.endpoint_ids]: Multiple agents IDs not currently supported for SentinelOne`,
+ statusCode: 400,
+ });
+ });
+
+ describe(`#isolate()`, () => {
+ it('should send action to sentinelone', async () => {
+ await s1ActionsClient.isolate(createS1IsolateOptions());
+
+ expect(connectorActionsMock.execute as jest.Mock).toHaveBeenCalledWith({
+ actionId: 's1-connector-instance-id',
+ params: {
+ subAction: 'isolateHost',
+ subActionParams: {
+ uuid: '1-2-3',
+ },
+ },
+ });
+ });
+
+ it('should write action request and response to endpoint indexes', async () => {
+ await s1ActionsClient.isolate(createS1IsolateOptions());
+
+ expect(classConstructorOptions.esClient.index).toHaveBeenCalledTimes(1);
+ // FIXME:PT once we start writing the Response, check above should be removed and new assertion added for it
+ expect(classConstructorOptions.esClient.index).toHaveBeenNthCalledWith(
+ 1,
+ {
+ document: {
+ '@timestamp': expect.any(String),
+ EndpointActions: {
+ action_id: expect.any(String),
+ data: { command: 'isolate', comment: 'test comment', parameters: undefined },
+ expiration: expect.any(String),
+ input_type: 'sentinel_one',
+ type: 'INPUT_ACTION',
+ },
+ agent: { id: ['1-2-3'] },
+ user: { id: 'foo' },
+ },
+ index: '.logs-endpoint.actions-default',
+ refresh: 'wait_for',
+ },
+ { meta: true }
+ );
+ });
+
+ it('should return action details', async () => {
+ await s1ActionsClient.isolate(createS1IsolateOptions());
+
+ expect(getActionDetailsByIdMock).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts
new file mode 100644
index 00000000000000..4837f427a926b6
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts
@@ -0,0 +1,148 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ActionsClient } from '@kbn/actions-plugin/server';
+import {
+ SENTINELONE_CONNECTOR_ID,
+ SUB_ACTION,
+} from '@kbn/stack-connectors-plugin/common/sentinelone/constants';
+import type { ConnectorWithExtraFindData } from '@kbn/actions-plugin/server/application/connector/types';
+import { once } from 'lodash';
+import type { ActionTypeExecutorResult } from '@kbn/actions-plugin/common';
+import { dump } from '../../../../utils/dump';
+import { ResponseActionsClientError } from '../errors';
+import type { ActionDetails } from '../../../../../../common/endpoint/types';
+import type {
+ IsolationRouteRequestBody,
+ BaseActionRequestBody,
+} from '../../../../../../common/api/endpoint';
+import type { ResponseActionsClientOptions } from '../lib/base_response_actions_client';
+import { ResponseActionsClientImpl } from '../lib/base_response_actions_client';
+
+export type SentinelOneActionsClientOptions = ResponseActionsClientOptions & {
+ connectorActions: ActionsClient;
+};
+
+export class SentinelOneActionsClient extends ResponseActionsClientImpl {
+ private readonly connectorActionsClient: ActionsClient;
+ private readonly getConnector: () => Promise;
+
+ constructor({ connectorActions, ...options }: SentinelOneActionsClientOptions) {
+ super(options);
+ this.connectorActionsClient = connectorActions;
+
+ this.getConnector = once(async () => {
+ let connectorList: ConnectorWithExtraFindData[] = [];
+
+ try {
+ connectorList = await this.connectorActionsClient.getAll();
+ } catch (err) {
+ throw new ResponseActionsClientError(
+ `Unable to retrieve list of stack connectors: ${err.message}`,
+ // failure here is likely due to Authz, but because we don't have a good way to determine that,
+ // the `statusCode` below is set to `400` instead of `401`.
+ 400,
+ err
+ );
+ }
+ const connector = connectorList.find(({ actionTypeId, isDeprecated, isMissingSecrets }) => {
+ return actionTypeId === SENTINELONE_CONNECTOR_ID && !isDeprecated && !isMissingSecrets;
+ });
+
+ if (!connector) {
+ throw new ResponseActionsClientError(
+ `No SentinelOne stack connector found`,
+ 400,
+ connectorList
+ );
+ }
+
+ this.log.debug(`Using SentinelOne stack connector: ${connector.name} (${connector.id})`);
+
+ return connector;
+ });
+ }
+
+ /**
+ * Sends actions to SentinelOne directly
+ * @private
+ */
+ private async sendAction(
+ actionType: SUB_ACTION,
+ actionParams: object
+ // FIXME:PT type properly the options above once PR 168441 for 8.12 merges
+ ): Promise> {
+ const { id: connectorId } = await this.getConnector();
+ const executeOptions: Parameters[0] = {
+ actionId: connectorId,
+ params: {
+ subAction: actionType,
+ subActionParams: actionParams,
+ },
+ };
+
+ this.log.debug(
+ `calling connector actions 'execute()' for SentinelOne with:\n${dump(executeOptions)}`
+ );
+
+ const actionSendResponse = await this.connectorActionsClient.execute(executeOptions);
+
+ if (actionSendResponse.status === 'error') {
+ this.log.error(dump(actionSendResponse));
+
+ throw new ResponseActionsClientError(
+ `Attempt to send [${actionType}] to SentinelOne failed: ${
+ actionSendResponse.serviceMessage || actionSendResponse.message
+ }`,
+ 500,
+ actionSendResponse
+ );
+ }
+
+ this.log.debug(`Response:\n${dump(actionSendResponse)}`);
+
+ return actionSendResponse;
+ }
+
+ private async validateRequest(payload: BaseActionRequestBody): Promise {
+ if (payload.endpoint_ids.length > 1) {
+ throw new ResponseActionsClientError(
+ `[body.endpoint_ids]: Multiple agents IDs not currently supported for SentinelOne`,
+ 400
+ );
+ }
+ }
+
+ async isolate(options: IsolationRouteRequestBody): Promise {
+ // TODO:PT support multiple agents
+ await this.validateRequest(options);
+
+ const agentUUID = options.endpoint_ids[0];
+
+ await this.sendAction(SUB_ACTION.ISOLATE_HOST, {
+ uuid: agentUUID,
+ });
+
+ // FIXME:PT need to grab data from the response above and store it with the Request or Response documents on our side
+
+ const actionRequestDoc = await this.writeActionRequestToEndpointIndex({
+ ...options,
+ command: 'isolate',
+ });
+
+ // TODO: un-comment code below once we have proper authz given to `kibana_system` account (security issue #8190)
+ // await this.writeActionResponseToEndpointIndex({
+ // actionId: actionRequestDoc.EndpointActions.action_id,
+ // agentId: actionRequestDoc.agent.id,
+ // data: {
+ // command: actionRequestDoc.EndpointActions.data.command,
+ // },
+ // });
+
+ return this.fetchActionDetails(actionRequestDoc.EndpointActions.action_id);
+ }
+}
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts
index 6df43004acb1a7..b502ec082c69b7 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/write_action_to_indices.ts
@@ -179,20 +179,18 @@ const createFailedActionResponseEntry = async ({
}
};
-const addRuleInfoToAction = (
- payload: CreateActionPayload
-):
- | {
- rule: { id: string; name: string };
- }
- | undefined => {
+export const addRuleInfoToAction = (
+ payload: Pick
+): Pick => {
if (payload.rule_id && payload.rule_name) {
return { rule: { id: payload.rule_id, name: payload.rule_name } };
}
+
+ return {};
};
-const getActionParameters = (
- action: CreateActionPayload
+export const getActionParameters = (
+ action: Pick
): ResponseActionsExecuteParameters | Readonly<{}> | undefined => {
// set timeout to 4h (if not specified or when timeout is specified as 0) when command is `execute`
if (action.command === 'execute') {
@@ -206,3 +204,7 @@ const getActionParameters = (
// for all other commands return the parameters as is
return action.parameters ?? undefined;
};
+
+export const getActionRequestExpiration = (): string => {
+ return moment().add(2, 'weeks').toISOString();
+};
diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts
index 67a8167ad5fa1e..49f8a89944d372 100644
--- a/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/index.ts
@@ -11,3 +11,4 @@ export { getActionList, getActionListByStatus } from './action_list';
export { getPendingActionsSummary } from './pending_actions_summary';
export { validateActionId } from './validate_action_id';
export * from './create';
+export * from './clients';
diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/dump.ts b/x-pack/plugins/security_solution/server/endpoint/utils/dump.ts
new file mode 100644
index 00000000000000..ae05b73fac05a4
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/endpoint/utils/dump.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { inspect } from 'util';
+
+/**
+ * Safely traverse some content (object, array, etc) and stringify it
+ * @param content
+ * @param depth
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const dump = (content: any, depth = 8): string => {
+ return inspect(content, { depth });
+};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts
index 846c714a9c099b..024f1b123ff997 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts
@@ -80,6 +80,8 @@ import {
ALERT_RULE_THREAT,
ALERT_RULE_EXCEPTIONS_LIST,
ALERT_RULE_IMMUTABLE,
+ ALERT_HOST_CRITICALITY,
+ ALERT_USER_CRITICALITY,
} from '../../../../../../common/field_maps/field_names';
import type { CompleteRule, RuleParams } from '../../../rule_schema';
import { commonParamsCamelToSnake, typeSpecificCamelToSnake } from '../../../rule_management';
@@ -256,6 +258,9 @@ export const buildAlert = (
'kibana.alert.rule.risk_score': params.riskScore,
'kibana.alert.rule.severity': params.severity,
'kibana.alert.rule.building_block_type': params.buildingBlockType,
+ // asset criticality fields will be enriched before ingestion
+ [ALERT_HOST_CRITICALITY]: undefined,
+ [ALERT_USER_CRITICALITY]: undefined,
};
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts
index 41ede6563524ca..70c554231a0e12 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/threshold.ts
@@ -156,11 +156,7 @@ export const thresholdExecutor = async ({
let createResult: GenericBulkCreateResponse;
let newSignalHistory: ThresholdSignalHistory;
- if (
- alertSuppression?.duration &&
- runOpts?.experimentalFeatures?.alertSuppressionForThresholdRuleEnabled &&
- hasPlatinumLicense
- ) {
+ if (alertSuppression?.duration && hasPlatinumLicense) {
const suppressedResults = await bulkCreateSuppressedThresholdAlerts({
buckets,
completeRule,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts
index e19e7ad1bc0eee..efbf39d815aeac 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/__mocks__/alerts.ts
@@ -68,6 +68,8 @@ import {
ALERT_RULE_TIMELINE_TITLE,
ALERT_RULE_INDICES,
ALERT_RULE_TIMESTAMP_OVERRIDE,
+ ALERT_HOST_CRITICALITY,
+ ALERT_USER_CRITICALITY,
} from '../../../../../../../common/field_maps/field_names';
export const createAlert = (
@@ -194,6 +196,8 @@ export const createAlert = (
rule_name_override: undefined,
timestamp_override: undefined,
},
+ [ALERT_HOST_CRITICALITY]: undefined,
+ [ALERT_USER_CRITICALITY]: undefined,
...data,
},
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts
index 982de01b8bae78..874556fb94dae2 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/create_single_field_match_enrichment.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { flatten, chunk } from 'lodash';
+import { chunk } from 'lodash';
import { searchEnrichments } from './search_enrichments';
import { makeSingleFieldMatchQuery } from './utils/requests';
import { getEventValue, getFieldValue } from './utils/events';
@@ -22,12 +22,14 @@ export const createSingleFieldMatchEnrichment: CreateFieldsMatchEnrichment = asy
createEnrichmentFunction,
name,
enrichmentResponseFields,
+ extraFilters,
}) => {
try {
logger.debug(`Enrichment ${name}: started`);
- const eventsWithField = events.filter((event) => getEventValue(event, mappingField.eventField));
- const eventsMapByFieldValue = eventsWithField.reduce((acc, event) => {
+ const eventsToEnrich = events.filter((event) => getEventValue(event, mappingField.eventField));
+
+ const eventsMapByFieldValue = eventsToEnrich.reduce((acc, event) => {
const eventFieldValue = getEventValue(event, mappingField.eventField);
if (!eventFieldValue) return {};
@@ -39,6 +41,7 @@ export const createSingleFieldMatchEnrichment: CreateFieldsMatchEnrichment = asy
}, {} as { [key: string]: typeof events });
const uniqueEventsValuesToSearchBy = Object.keys(eventsMapByFieldValue);
+
const chunksUniqueEventsValuesToSearchBy = chunk(uniqueEventsValuesToSearchBy, MAX_CLAUSES);
const getAllEnrichment = chunksUniqueEventsValuesToSearchBy
@@ -46,6 +49,7 @@ export const createSingleFieldMatchEnrichment: CreateFieldsMatchEnrichment = asy
makeSingleFieldMatchQuery({
values: enrichmentValuesChunk,
searchByField: mappingField.enrichmentField,
+ extraFilters,
})
)
.filter((query) => query.query?.bool?.should?.length > 0)
@@ -59,11 +63,9 @@ export const createSingleFieldMatchEnrichment: CreateFieldsMatchEnrichment = asy
})
);
- const enrichmentsResults = (await Promise.allSettled(getAllEnrichment))
+ const enrichments = (await Promise.allSettled(getAllEnrichment))
.filter((result) => result.status === 'fulfilled')
- .map((result) => (result as PromiseFulfilledResult)?.value);
-
- const enrichments = flatten(enrichmentsResults);
+ .flatMap((result) => (result as PromiseFulfilledResult)?.value);
if (enrichments.length === 0) {
logger.debug(`Enrichment ${name}: no enrichment found`);
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/asset_criticality.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/asset_criticality.ts
new file mode 100644
index 00000000000000..e2bd3319062ae1
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/asset_criticality.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { cloneDeep } from 'lodash';
+import {
+ ALERT_HOST_CRITICALITY,
+ ALERT_USER_CRITICALITY,
+} from '../../../../../../../common/field_maps/field_names';
+import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment';
+import type { CreateCriticalityEnrichment, CreateEnrichmentFunction } from '../types';
+import { getFieldValue } from '../utils/events';
+import { getAssetCriticalityIndex } from '../../../../../../../common/entity_analytics/asset_criticality';
+
+const enrichmentResponseFields = ['id_value', 'criticality_level'];
+
+const getExtraFiltersForEnrichment = (field: string) => [
+ {
+ match: {
+ id_field: {
+ query: field,
+ },
+ },
+ },
+];
+
+const createEnrichmentFactoryFunction =
+ (
+ alertField: typeof ALERT_HOST_CRITICALITY | typeof ALERT_USER_CRITICALITY
+ ): CreateEnrichmentFunction =>
+ (enrichment) =>
+ (event) => {
+ const criticality = getFieldValue(enrichment, 'criticality_level');
+
+ if (!criticality) {
+ return event;
+ }
+ const newEvent = cloneDeep(event);
+ if (criticality && newEvent._source) {
+ newEvent._source[alertField] = criticality;
+ }
+ return newEvent;
+ };
+
+export const createHostAssetCriticalityEnrichments: CreateCriticalityEnrichment = async ({
+ services,
+ logger,
+ events,
+ spaceId,
+}) => {
+ return createSingleFieldMatchEnrichment({
+ name: 'Host Asset Criticality',
+ index: [getAssetCriticalityIndex(spaceId)],
+ services,
+ logger,
+ events,
+ mappingField: {
+ eventField: 'host.name',
+ enrichmentField: 'id_value',
+ },
+ enrichmentResponseFields,
+ extraFilters: getExtraFiltersForEnrichment('host.name'),
+ createEnrichmentFunction: createEnrichmentFactoryFunction(ALERT_HOST_CRITICALITY),
+ });
+};
+
+export const createUserAssetCriticalityEnrichments: CreateCriticalityEnrichment = async ({
+ services,
+ logger,
+ events,
+ spaceId,
+}) => {
+ return createSingleFieldMatchEnrichment({
+ name: 'User Asset Criticality',
+ index: [getAssetCriticalityIndex(spaceId)],
+ services,
+ logger,
+ events,
+ mappingField: {
+ eventField: 'user.name',
+ enrichmentField: 'id_value',
+ },
+ enrichmentResponseFields,
+ extraFilters: getExtraFiltersForEnrichment('user.name'),
+ createEnrichmentFunction: createEnrichmentFactoryFunction(ALERT_USER_CRITICALITY),
+ });
+};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts
index 6b18979c0d3c0d..1b34f6cb878595 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/host_risk.ts
@@ -10,23 +10,9 @@ import { cloneDeep } from 'lodash';
import { getHostRiskIndex } from '../../../../../../../common/search_strategy/security_solution/risk_score/common';
import { RiskScoreFields } from '../../../../../../../common/search_strategy/security_solution/risk_score/all';
import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment';
-import type { CreateRiskEnrichment, GetIsRiskScoreAvailable } from '../types';
+import type { CreateRiskEnrichment } from '../types';
import { getFieldValue } from '../utils/events';
-export const getIsHostRiskScoreAvailable: GetIsRiskScoreAvailable = async ({
- spaceId,
- services,
- isNewRiskScoreModuleInstalled,
-}) => {
- const isHostRiskScoreIndexExist = await services.scopedClusterClient.asCurrentUser.indices.exists(
- {
- index: getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled),
- }
- );
-
- return isHostRiskScoreIndexExist;
-};
-
export const createHostRiskEnrichments: CreateRiskEnrichment = async ({
services,
logger,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts
index b0e8d87f3019fb..27ae894f281343 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/user_risk.ts
@@ -9,23 +9,9 @@ import { cloneDeep } from 'lodash';
import { getUserRiskIndex } from '../../../../../../../common/search_strategy/security_solution/risk_score/common';
import { RiskScoreFields } from '../../../../../../../common/search_strategy/security_solution/risk_score/all';
import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment';
-import type { CreateRiskEnrichment, GetIsRiskScoreAvailable } from '../types';
+import type { CreateRiskEnrichment } from '../types';
import { getFieldValue } from '../utils/events';
-export const getIsUserRiskScoreAvailable: GetIsRiskScoreAvailable = async ({
- services,
- spaceId,
- isNewRiskScoreModuleInstalled,
-}) => {
- const isUserRiskScoreIndexExist = await services.scopedClusterClient.asCurrentUser.indices.exists(
- {
- index: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled),
- }
- );
-
- return isUserRiskScoreIndexExist;
-};
-
export const createUserRiskEnrichments: CreateRiskEnrichment = async ({
services,
logger,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts
index 8f98b5bfe04b75..4c87c6f5a82720 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts
@@ -11,26 +11,20 @@ import { enrichEvents } from '.';
import { searchEnrichments } from './search_enrichments';
import { ruleExecutionLogMock } from '../../../rule_monitoring/mocks';
import { createAlert } from './__mocks__/alerts';
-import { getIsHostRiskScoreAvailable } from './enrichment_by_type/host_risk';
-import { getIsUserRiskScoreAvailable } from './enrichment_by_type/user_risk';
+import { isIndexExist } from './utils/is_index_exist';
+
+import { allowedExperimentalValues } from '../../../../../../common';
jest.mock('./search_enrichments', () => ({
searchEnrichments: jest.fn(),
}));
const mockSearchEnrichments = searchEnrichments as jest.Mock;
-jest.mock('./enrichment_by_type/host_risk', () => ({
- ...jest.requireActual('./enrichment_by_type/host_risk'),
- getIsHostRiskScoreAvailable: jest.fn(),
-}));
-const mockGetIsHostRiskScoreAvailable = getIsHostRiskScoreAvailable as jest.Mock;
-
-jest.mock('./enrichment_by_type/user_risk', () => ({
- ...jest.requireActual('./enrichment_by_type/user_risk'),
- getIsUserRiskScoreAvailable: jest.fn(),
+jest.mock('./utils/is_index_exist', () => ({
+ isIndexExist: jest.fn(),
}));
-const mockGetIsUserRiskScoreAvailable = getIsUserRiskScoreAvailable as jest.Mock;
+const mockIsIndexExist = isIndexExist as jest.Mock;
const hostEnrichmentResponse = [
{
@@ -66,6 +60,30 @@ const userEnrichmentResponse = [
},
];
+const assetCriticalityUserResponse = [
+ {
+ fields: {
+ id_value: ['user name 1'],
+ criticality_level: ['important'],
+ },
+ },
+];
+
+const assetCriticalityHostResponse = [
+ {
+ fields: {
+ id_value: ['host name 2'],
+ criticality_level: ['very_important'],
+ },
+ },
+ {
+ fields: {
+ id_value: ['host name 1'],
+ criticality_level: ['low'],
+ },
+ },
+];
+
describe('enrichEvents', () => {
let ruleExecutionLogger: ReturnType;
let alertServices: RuleExecutorServicesMock;
@@ -76,11 +94,13 @@ describe('enrichEvents', () => {
ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create();
alertServices = alertsMock.createRuleExecutorServices();
});
+ afterEach(() => {
+ mockIsIndexExist.mockClear();
+ });
it('return the same events, if risk indexes are not available', async () => {
mockSearchEnrichments.mockImplementation(() => []);
- mockGetIsUserRiskScoreAvailable.mockImplementation(() => false);
- mockGetIsHostRiskScoreAvailable.mockImplementation(() => false);
+ mockIsIndexExist.mockImplementation(() => false);
const events = [
createAlert('1', createEntity('host', 'host name')),
createAlert('2', createEntity('user', 'user name')),
@@ -97,8 +117,7 @@ describe('enrichEvents', () => {
it('return the same events, if there no fields', async () => {
mockSearchEnrichments.mockImplementation(() => []);
- mockGetIsUserRiskScoreAvailable.mockImplementation(() => true);
- mockGetIsHostRiskScoreAvailable.mockImplementation(() => true);
+ mockIsIndexExist.mockImplementation(() => true);
const events = [createAlert('1'), createAlert('2')];
const enrichedEvents = await enrichEvents({
logger: ruleExecutionLogger,
@@ -110,12 +129,11 @@ describe('enrichEvents', () => {
expect(enrichedEvents).toEqual(events);
});
- it('return enriched events', async () => {
+ it('return enriched events with risk score', async () => {
mockSearchEnrichments
.mockReturnValueOnce(hostEnrichmentResponse)
.mockReturnValueOnce(userEnrichmentResponse);
- mockGetIsUserRiskScoreAvailable.mockImplementation(() => true);
- mockGetIsHostRiskScoreAvailable.mockImplementation(() => true);
+ mockIsIndexExist.mockImplementation(() => true);
const enrichedEvents = await enrichEvents({
logger: ruleExecutionLogger,
@@ -159,14 +177,57 @@ describe('enrichEvents', () => {
]);
});
+ it('return enriched events with asset criticality', async () => {
+ mockSearchEnrichments
+ .mockReturnValueOnce(assetCriticalityUserResponse)
+ .mockReturnValueOnce(assetCriticalityHostResponse);
+
+ // disable risk score enrichments
+ mockIsIndexExist.mockImplementationOnce(() => false);
+ mockIsIndexExist.mockImplementationOnce(() => false);
+ mockIsIndexExist.mockImplementationOnce(() => false);
+ // enable for asset criticality
+ mockIsIndexExist.mockImplementation(() => true);
+
+ const enrichedEvents = await enrichEvents({
+ logger: ruleExecutionLogger,
+ services: alertServices,
+ events: [
+ createAlert('1', {
+ ...createEntity('host', 'host name 1'),
+ ...createEntity('user', 'user name 1'),
+ }),
+ createAlert('2', createEntity('host', 'user name 1')),
+ ],
+ spaceId: 'default',
+ experimentalFeatures: {
+ ...allowedExperimentalValues,
+ entityAnalyticsAssetCriticalityEnabled: true,
+ },
+ });
+
+ expect(enrichedEvents).toEqual([
+ createAlert('1', {
+ ...createEntity('user', 'user name 1'),
+ ...createEntity('host', 'host name 1'),
+
+ 'kibana.alert.host.criticality_level': 'low',
+ 'kibana.alert.user.criticality_level': 'important',
+ }),
+ createAlert('2', {
+ ...createEntity('host', 'user name 1'),
+ }),
+ ]);
+ });
+
it('if some enrichments failed, another work as expected', async () => {
mockSearchEnrichments
.mockImplementationOnce(() => {
throw new Error('1');
})
.mockImplementationOnce(() => userEnrichmentResponse);
- mockGetIsUserRiskScoreAvailable.mockImplementation(() => true);
- mockGetIsHostRiskScoreAvailable.mockImplementation(() => true);
+ mockIsIndexExist.mockImplementation(() => true);
+ mockIsIndexExist.mockImplementation(() => true);
const enrichedEvents = await enrichEvents({
logger: ruleExecutionLogger,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts
index a27cc55801820d..cfd51f21e20cd2 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts
@@ -5,21 +5,26 @@
* 2.0.
*/
-import {
- createHostRiskEnrichments,
- getIsHostRiskScoreAvailable,
-} from './enrichment_by_type/host_risk';
+import { createHostRiskEnrichments } from './enrichment_by_type/host_risk';
+
+import { createUserRiskEnrichments } from './enrichment_by_type/user_risk';
import {
- createUserRiskEnrichments,
- getIsUserRiskScoreAvailable,
-} from './enrichment_by_type/user_risk';
+ createHostAssetCriticalityEnrichments,
+ createUserAssetCriticalityEnrichments,
+} from './enrichment_by_type/asset_criticality';
+import { getAssetCriticalityIndex } from '../../../../../../common/entity_analytics/asset_criticality';
import type {
EnrichEventsFunction,
EventsMapByEnrichments,
CreateEnrichEventsFunction,
} from './types';
import { applyEnrichmentsToEvents } from './utils/transforms';
+import { isIndexExist } from './utils/is_index_exist';
+import {
+ getHostRiskIndex,
+ getUserRiskIndex,
+} from '../../../../../../common/search_strategy/security_solution/risk_score/common';
export const enrichEvents: EnrichEventsFunction = async ({
services,
@@ -29,23 +34,30 @@ export const enrichEvents: EnrichEventsFunction = async ({
experimentalFeatures,
}) => {
try {
- const enrichments = [];
+ const enrichments: Array> = [];
logger.debug('Alert enrichments started');
const isNewRiskScoreModuleAvailable = experimentalFeatures?.riskScoringRoutesEnabled ?? false;
+ const isAssetCriticalityEnabled =
+ experimentalFeatures?.entityAnalyticsAssetCriticalityEnabled ?? false;
let isNewRiskScoreModuleInstalled = false;
if (isNewRiskScoreModuleAvailable) {
- isNewRiskScoreModuleInstalled = await getIsHostRiskScoreAvailable({
- spaceId,
+ isNewRiskScoreModuleInstalled = await isIndexExist({
services,
- isNewRiskScoreModuleInstalled: true,
+ index: getHostRiskIndex(spaceId, true, true),
});
}
const [isHostRiskScoreIndexExist, isUserRiskScoreIndexExist] = await Promise.all([
- getIsHostRiskScoreAvailable({ spaceId, services, isNewRiskScoreModuleInstalled }),
- getIsUserRiskScoreAvailable({ spaceId, services, isNewRiskScoreModuleInstalled }),
+ isIndexExist({
+ services,
+ index: getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled),
+ }),
+ isIndexExist({
+ services,
+ index: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled),
+ }),
]);
if (isHostRiskScoreIndexExist) {
@@ -72,9 +84,34 @@ export const enrichEvents: EnrichEventsFunction = async ({
);
}
+ if (isAssetCriticalityEnabled) {
+ const assetCriticalityIndexExist = await isIndexExist({
+ services,
+ index: getAssetCriticalityIndex(spaceId),
+ });
+ if (assetCriticalityIndexExist) {
+ enrichments.push(
+ createUserAssetCriticalityEnrichments({
+ services,
+ logger,
+ events,
+ spaceId,
+ })
+ );
+ enrichments.push(
+ createHostAssetCriticalityEnrichments({
+ services,
+ logger,
+ events,
+ spaceId,
+ })
+ );
+ }
+ }
+
const allEnrichmentsResults = await Promise.allSettled(enrichments);
- const allFulfilledEnrichmentsResults = allEnrichmentsResults
+ const allFulfilledEnrichmentsResults: EventsMapByEnrichments[] = allEnrichmentsResults
.filter((result) => result.status === 'fulfilled')
.map((result) => (result as PromiseFulfilledResult)?.value);
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts
index 73c703235edcac..70f710630da37a 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/types.ts
@@ -6,6 +6,7 @@
*/
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
+import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import type { Filter } from '@kbn/es-query';
import type { ExperimentalFeatures } from '../../../../../../common';
@@ -27,7 +28,6 @@ export type EnrichmentFunction = (
e: EventsForEnrichment
) => EventsForEnrichment;
-//
export interface EventsMapByEnrichments {
[id: string]: EnrichmentFunction[];
}
@@ -48,11 +48,6 @@ interface BasedEnrichParamters {
events: Array>;
}
-interface SingleMappingField {
- eventField: string;
- enrichmentField: string;
-}
-
export type GetEventValue = (
events: EventsForEnrichment,
path: string
@@ -60,9 +55,10 @@ export type GetEventValue = (
export type GetFieldValue = (events: EnrichmentType, path: string) => string | undefined;
-export type MakeSingleFieldMatchQuery = (params: {
+export type MakeSingleFieldMatchQuery = (params: {
values: string[];
searchByField: string;
+ extraFilters?: QueryDslQueryContainer[];
}) => Filter;
export type SearchEnrichments = (params: {
@@ -79,6 +75,8 @@ export type GetIsRiskScoreAvailable = (params: {
isNewRiskScoreModuleInstalled: boolean;
}) => Promise;
+export type IsIndexExist = (params: { services: RuleServices; index: string }) => Promise;
+
export type CreateRiskEnrichment = (
params: BasedEnrichParamters & {
spaceId: string;
@@ -86,13 +84,28 @@ export type CreateRiskEnrichment = (
}
) => Promise;
+export type CreateCriticalityEnrichment = (
+ params: BasedEnrichParamters & {
+ spaceId: string;
+ }
+) => Promise;
+
+export type CreateEnrichmentFunction = (enrichmentDoc: EnrichmentType) => EnrichmentFunction;
+
export type CreateFieldsMatchEnrichment = (
params: BasedEnrichParamters & {
name: string;
index: string[];
- mappingField: SingleMappingField;
+ mappingField: {
+ /** The field on events which contains the value we'll use to build a query. */
+ eventField: string;
+ /** Used in a `match` query to find documents that match the values of `eventField`. */
+ enrichmentField: string;
+ };
+ /** Specifies which fields should be returned when querying the enrichment index. */
enrichmentResponseFields: string[];
- createEnrichmentFunction: (enrichmentDoc: EnrichmentType) => EnrichmentFunction;
+ createEnrichmentFunction: CreateEnrichmentFunction;
+ extraFilters?: QueryDslQueryContainer[];
}
) => Promise;
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts
index 54eea14c6e94a5..b08b0f2b362b8e 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/events.ts
@@ -6,7 +6,8 @@
*/
import { get } from 'lodash';
-import type { GetEventValue, GetFieldValue } from '../types';
+import type { BaseFieldsLatest } from '../../../../../../../common/api/detection_engine/model/alerts';
+import type { EventsForEnrichment, GetEventValue, GetFieldValue } from '../types';
export const getEventValue: GetEventValue = (event, path) => {
const value = get(event, `_source.${path}`) || event?._source?.[path];
@@ -19,3 +20,30 @@ export const getEventValue: GetEventValue = (event, path) => {
};
export const getFieldValue: GetFieldValue = (event, path) => get(event?.fields, path)?.[0];
+
+/** Given an eventField, returns a map of values found in that field to the events that contain that value. */
+export function getEventsMapByFieldValue(
+ events: Array>,
+ eventField: string
+): Record<
+ /** values found in mappingField.eventField */ string,
+ /** Array of events with the corresponding value */ typeof events
+> {
+ const eventsWithField = events.filter((event) => getEventValue(event, eventField));
+
+ const eventsMapByFieldValue: Record<
+ /** values found in mappingField.eventField */ string,
+ /** Array of events with the corresponding value */ typeof events
+ > = eventsWithField.reduce((acc, event) => {
+ const eventFieldValue = getEventValue(event, eventField);
+
+ if (!eventFieldValue) return {};
+
+ acc[eventFieldValue] ??= [];
+ acc[eventFieldValue].push(event);
+
+ return acc;
+ }, {} as { [key: string]: typeof events });
+
+ return eventsMapByFieldValue;
+}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/is_index_exist.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/is_index_exist.ts
new file mode 100644
index 00000000000000..eb7813c3504166
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/is_index_exist.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { IsIndexExist } from '../types';
+
+export const isIndexExist: IsIndexExist = async ({ services, index }) => {
+ const isAssetCriticalityIndexExist =
+ await services.scopedClusterClient.asInternalUser.indices.exists({
+ index,
+ });
+
+ return isAssetCriticalityIndexExist;
+};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts
index 8cae81c8ef3b09..b8f3253bb69f76 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.test.ts
@@ -20,6 +20,7 @@ describe('makeSingleFieldMatchQuery', () => {
query: {
bool: {
should: [],
+ filter: [],
minimum_should_match: 1,
},
},
@@ -58,6 +59,46 @@ describe('makeSingleFieldMatchQuery', () => {
},
},
],
+ filter: [],
+ minimum_should_match: 1,
+ },
+ },
+ });
+ });
+
+ it('return query with extra filters', () => {
+ expect(
+ makeSingleFieldMatchQuery({
+ values: [],
+ searchByField: 'host.name',
+ extraFilters: [
+ {
+ match: {
+ id_field: {
+ query: 'host.name',
+ },
+ },
+ },
+ ],
+ })
+ ).toEqual({
+ meta: {
+ alias: null,
+ negate: false,
+ disabled: false,
+ },
+ query: {
+ bool: {
+ should: [],
+ filter: [
+ {
+ match: {
+ id_field: {
+ query: 'host.name',
+ },
+ },
+ },
+ ],
minimum_should_match: 1,
},
},
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts
index b4567481691b6b..704fb89812e4a7 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/utils/requests.ts
@@ -7,7 +7,12 @@
import type { MakeSingleFieldMatchQuery } from '../types';
-export const makeSingleFieldMatchQuery: MakeSingleFieldMatchQuery = ({ values, searchByField }) => {
+/** makes a query that gets back any documents with the given `values` in the `searchByField` */
+export const makeSingleFieldMatchQuery: MakeSingleFieldMatchQuery = ({
+ values,
+ searchByField,
+ extraFilters,
+}) => {
const shouldClauses = values.map((value) => ({
match: {
[searchByField]: {
@@ -26,6 +31,7 @@ export const makeSingleFieldMatchQuery: MakeSingleFieldMatchQuery = ({ values, s
query: {
bool: {
should: shouldClauses,
+ filter: extraFilters ?? [],
minimum_should_match: 1,
},
},
diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts
index 227a7ea7e1439d..53141fc1751ceb 100644
--- a/x-pack/plugins/security_solution/server/plugin.ts
+++ b/x-pack/plugins/security_solution/server/plugin.ts
@@ -211,6 +211,7 @@ export class Plugin implements ISecuritySolutionPlugin {
this.endpointAppContextService.setup({
securitySolutionRequestContextFactory: requestContextFactory,
cloud: plugins.cloud,
+ loggerFactory: this.pluginContext.logger,
});
initUsageCollectors({
diff --git a/x-pack/plugins/security_solution/server/utils/custom_http_request_error.ts b/x-pack/plugins/security_solution/server/utils/custom_http_request_error.ts
index 0146912d78c6ca..9ce131a9182efa 100644
--- a/x-pack/plugins/security_solution/server/utils/custom_http_request_error.ts
+++ b/x-pack/plugins/security_solution/server/utils/custom_http_request_error.ts
@@ -5,10 +5,13 @@
* 2.0.
*/
export class CustomHttpRequestError extends Error {
- constructor(message: string, public readonly statusCode: number = 500, meta?: unknown) {
+ constructor(
+ message: string,
+ public readonly statusCode: number = 500,
+ public readonly meta?: unknown
+ ) {
super(message);
// For debugging - capture name of subclasses
this.name = this.constructor.name;
- this.message = message;
}
}
diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json
index 2158fe97996dc2..92f062103e7597 100644
--- a/x-pack/plugins/security_solution/tsconfig.json
+++ b/x-pack/plugins/security_solution/tsconfig.json
@@ -185,6 +185,8 @@
"@kbn/core-http-common",
"@kbn/search-errors",
"@kbn/stack-connectors-plugin",
- "@kbn/elastic-assistant-common"
+ "@kbn/elastic-assistant-common",
+ "@kbn/core-elasticsearch-server-mocks",
+ "@kbn/lens-embeddable-utils"
]
}
diff --git a/x-pack/plugins/session_view/public/test/index.tsx b/x-pack/plugins/session_view/public/test/index.tsx
index 277917e44ccafc..23f5fe8b25ce1c 100644
--- a/x-pack/plugins/session_view/public/test/index.tsx
+++ b/x-pack/plugins/session_view/public/test/index.tsx
@@ -11,7 +11,6 @@ import { render as reactRender, RenderOptions, RenderResult } from '@testing-lib
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Router } from '@kbn/shared-ux-router';
import { History } from 'history';
-import useObservable from 'react-use/lib/useObservable';
import { I18nProvider } from '@kbn/i18n-react';
import { CoreStart } from '@kbn/core/public';
import { coreMock } from '@kbn/core/public/mocks';
@@ -68,8 +67,8 @@ const AppRootProvider = memo<{
history: History;
coreStart: CoreStart;
children: ReactNode | ReactNode[];
-}>(({ history, coreStart: { http, notifications, uiSettings, application }, children }) => {
- const isDarkMode = useObservable(uiSettings.get$('theme:darkMode'));
+}>(({ history, coreStart: { http, notifications, theme, application }, children }) => {
+ const isDarkMode = theme.getTheme().darkMode;
const services = useMemo(
() => ({ http, notifications, application }),
[application, http, notifications]
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/_policy_form.scss b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/_policy_form.scss
index 389e925f392c27..0a5187908f8542 100644
--- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/_policy_form.scss
+++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/_policy_form.scss
@@ -4,7 +4,7 @@
.snapshotRestore__policyForm__stepSettings {
.euiFormRow--hasEmptyLabelSpace {
min-height: auto;
- margin-top: $euiFontSizeXS + $euiSizeS + calc($euiSizeXXL / 4);
+ margin-top: $euiFontSizeXS + $euiSizeS + ($euiSizeXXL / 4);
}
}
@@ -13,4 +13,4 @@
*/
.snapshotRestore__policyForm__stepSettings__indicesFieldWrapper .euiFormLabel {
width: 100%;
-}
+}
\ No newline at end of file
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/_restore_snapshot_form.scss b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/_restore_snapshot_form.scss
index ec680472edf465..6a8f0b951c99fe 100644
--- a/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/_restore_snapshot_form.scss
+++ b/x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/_restore_snapshot_form.scss
@@ -5,7 +5,7 @@
.snapshotRestore__restoreForm__stepSettings {
.euiFormRow--hasEmptyLabelSpace {
min-height: auto;
- margin-top: $euiFontSizeXS + $euiSizeS + calc($euiSizeXXL / 4);
+ margin-top: $euiFontSizeXS + $euiSizeS + ($euiSizeXXL / 4);
}
}
@@ -14,4 +14,4 @@
*/
.snapshotRestore__restoreForm__stepLogistics__indicesFieldWrapper .euiFormLabel {
width: 100%;
-}
+}
\ No newline at end of file
diff --git a/x-pack/plugins/stack_alerts/common/constants.ts b/x-pack/plugins/stack_alerts/common/constants.ts
index a2be12ee63867c..b378e097d883a2 100644
--- a/x-pack/plugins/stack_alerts/common/constants.ts
+++ b/x-pack/plugins/stack_alerts/common/constants.ts
@@ -6,3 +6,18 @@
*/
export const MAX_SELECTABLE_GROUP_BY_TERMS = 4;
+export const MAX_SELECTABLE_SOURCE_FIELDS = 5;
+
+const HOST_NAME = 'host.name';
+const HOST_HOSTNAME = 'host.hostname';
+const HOST_ID = 'host.id';
+const CONTAINER_ID = 'container.id';
+const KUBERNETES_POD_UID = 'kubernetes.pod.uid';
+
+export const validSourceFields = [
+ HOST_NAME,
+ HOST_HOSTNAME,
+ HOST_ID,
+ CONTAINER_ID,
+ KUBERNETES_POD_UID,
+];
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.test.tsx b/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.test.tsx
new file mode 100644
index 00000000000000..aed0f36b27c8eb
--- /dev/null
+++ b/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.test.tsx
@@ -0,0 +1,226 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { cleanup, render, screen, waitFor } from '@testing-library/react';
+import { I18nProvider } from '@kbn/i18n-react';
+import { SourceFields } from './source_fields_select';
+import { SourceField } from '../es_query/types';
+
+const AppWrapper: React.FC<{ children: React.ReactElement }> = React.memo(({ children }) => (
+ {children}
+));
+
+describe('SourceFields', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should render SourceFields component if there are valid sourceFields', () => {
+ const result = render(
+ {}}
+ esFields={[
+ {
+ name: 'host.name',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ ]}
+ sourceFields={[]}
+ errors={[]}
+ />,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ expect(result.getByTestId('sourceFields')).toBeInTheDocument();
+ });
+
+ it('should not render SourceFields component if there are not valid sourceFields', () => {
+ const result = render(
+ {}}
+ esFields={[
+ {
+ name: 'test',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ ]}
+ sourceFields={[]}
+ errors={[]}
+ />,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ expect(result.queryByTestId('sourceFields')).not.toBeInTheDocument();
+ });
+
+ it('should render sourceFields param', () => {
+ const result = render(
+ {}}
+ esFields={[
+ {
+ name: 'host.name',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ ]}
+ sourceFields={[{ label: 'host.name', searchPath: 'host.name' }]}
+ errors={[]}
+ />,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ expect(result.getByTestId('option-host.name')).toBeInTheDocument();
+ });
+
+ it('should auto select valid sourceFields if sourceFields param is not defined', async () => {
+ let sourceFields: SourceField[] | undefined;
+ const onChange = (fields: SourceField[]) => {
+ sourceFields = fields;
+ };
+ const { rerender } = render(
+ ,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ await waitFor(() => {
+ rerender(
+
+ );
+ expect(screen.getByTestId('option-host.name')).toBeInTheDocument();
+ });
+ });
+
+ it('should remove duplicate and non-aggregatable esFields and handle keyword esFields', async () => {
+ let sourceFields: SourceField[] | undefined;
+ const onChange = (fields: SourceField[]) => {
+ sourceFields = fields;
+ };
+ const esFields = [
+ {
+ name: 'host.name',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: false,
+ },
+ {
+ name: 'host.hostname',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ {
+ name: 'host.hostname.keyword',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ {
+ name: 'host.id.keyword',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ ];
+ const { rerender } = render(
+ ,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ await waitFor(() => {
+ rerender(
+
+ );
+ expect(screen.queryByTestId('option-host.name')).not.toBeInTheDocument();
+ expect(screen.getAllByTestId('option-host.hostname')).toHaveLength(1);
+ expect(screen.getByTestId('option-host.id')).toBeInTheDocument();
+ });
+ });
+
+ it('should render SourceFields component with errors', () => {
+ const result = render(
+ {}}
+ esFields={[
+ {
+ name: 'host.name',
+ type: 'type',
+ normalizedType: 'type',
+ searchable: true,
+ aggregatable: true,
+ },
+ ]}
+ sourceFields={[]}
+ errors={['test error']}
+ />,
+ {
+ wrapper: AppWrapper,
+ }
+ );
+
+ expect(result.getByText('test error')).toBeInTheDocument();
+ });
+});
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.tsx b/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.tsx
new file mode 100644
index 00000000000000..3cd34a3cd00155
--- /dev/null
+++ b/x-pack/plugins/stack_alerts/public/rule_types/components/source_fields_select.tsx
@@ -0,0 +1,120 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useEffect, useState } from 'react';
+import { uniqBy } from 'lodash';
+import { EuiComboBox, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { FieldOption } from '@kbn/triggers-actions-ui-plugin/public/common';
+import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { i18n } from '@kbn/i18n';
+import { MAX_SELECTABLE_SOURCE_FIELDS, validSourceFields } from '../../../common/constants';
+import { SourceField } from '../es_query/types';
+
+interface SourceFieldsOption {
+ label: string;
+ value: string;
+}
+
+interface SourceFieldsProps {
+ esFields: FieldOption[];
+ onChangeSourceFields: (selectedSourceFields: SourceField[]) => void;
+ errors: string | string[] | IErrorObject;
+ sourceFields?: SourceField[];
+}
+
+export const SourceFields: React.FC = ({
+ esFields,
+ onChangeSourceFields,
+ errors,
+ sourceFields,
+}) => {
+ const [sourceFieldsOptions, setSourceFieldsOptions] = useState([]);
+
+ useEffect(() => {
+ const options = uniqBy(
+ esFields
+ .filter((f) => f.aggregatable)
+ .flatMap((f) => {
+ const validSourceField = validSourceFields.find(
+ (validatedField) => f.name === validatedField || f.name === `${validatedField}.keyword`
+ );
+ if (validSourceField) {
+ return [
+ {
+ label: validSourceField,
+ value: f.name,
+ 'data-test-subj': `option-${validSourceField}`,
+ },
+ ];
+ }
+ return [];
+ }),
+ 'label'
+ );
+
+ setSourceFieldsOptions(options);
+
+ // if not sourceFields, auto select the current options
+ if (!sourceFields) {
+ const fields: SourceField[] = [];
+ options.forEach((f) => {
+ if (f.value && fields.length < MAX_SELECTABLE_SOURCE_FIELDS) {
+ fields.push({ label: f.label, searchPath: f.value });
+ }
+ });
+ onChangeSourceFields(fields);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [esFields]);
+
+ return sourceFieldsOptions.length > 0 ? (
+ 0 && sourceFields !== undefined}
+ error={errors}
+ >
+ <>
+
+
+
+
+
+
+ 0 && sourceFields !== undefined}
+ selectedOptions={(sourceFields || []).map((f) => ({
+ label: f.label,
+ value: f.searchPath,
+ 'data-test-subj': `option-${f.label}`,
+ }))}
+ onChange={(options) => {
+ const fields: SourceField[] = [];
+ options.forEach((f) => {
+ if (f.value) {
+ fields.push({ label: f.label, searchPath: f.value });
+ }
+ });
+ onChangeSourceFields(fields);
+ }}
+ options={sourceFieldsOptions}
+ />
+ >
+
+ ) : null;
+};
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts
index f99b97634fc5ad..690387d0549392 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts
@@ -23,6 +23,7 @@ export const DEFAULT_VALUES = {
GROUP_BY: 'all',
EXCLUDE_PREVIOUS_HITS: true,
CAN_SELECT_MULTI_TERMS: true,
+ SOURCE_FIELDS: [],
};
export const COMMON_EXPRESSION_ERRORS = {
@@ -36,6 +37,7 @@ export const COMMON_EXPRESSION_ERRORS = {
groupBy: new Array(),
termSize: new Array(),
termField: new Array(),
+ sourceFields: new Array(),
};
export const SEARCH_SOURCE_ONLY_EXPRESSION_ERRORS = {
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx
index f1e91ba63dc691..1cf901ecae8f90 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/es_query_expression.tsx
@@ -55,6 +55,7 @@ export const EsQueryExpression: React.FC<
termSize,
termField,
excludeHitsFromPreviousRun,
+ sourceFields,
} = ruleParams;
const [currentRuleParams, setCurrentRuleParams] = useState>(
@@ -72,6 +73,7 @@ export const EsQueryExpression: React.FC<
searchType: SearchType.esQuery,
excludeHitsFromPreviousRun:
excludeHitsFromPreviousRun ?? DEFAULT_VALUES.EXCLUDE_PREVIOUS_HITS,
+ sourceFields,
}
);
@@ -229,6 +231,7 @@ export const EsQueryExpression: React.FC<
termSize: DEFAULT_VALUES.TERM_SIZE,
searchType: SearchType.esQuery,
excludeHitsFromPreviousRun: DEFAULT_VALUES.EXCLUDE_PREVIOUS_HITS,
+ sourceFields: undefined,
});
} else {
await refreshEsFields(indices);
@@ -352,6 +355,11 @@ export const EsQueryExpression: React.FC<
[setParam]
)}
canSelectMultiTerms={DEFAULT_VALUES.CAN_SELECT_MULTI_TERMS}
+ onChangeSourceFields={useCallback(
+ (selectedSourceFields) => setParam('sourceFields', selectedSourceFields),
+ [setParam]
+ )}
+ sourceFields={sourceFields}
/>
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx
index 8f3edd45a1e283..30f34b0a15d057 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx
@@ -6,7 +6,7 @@
*/
import React, { useState, Fragment, useEffect, useCallback } from 'react';
-import { get } from 'lodash';
+import { debounce, get } from 'lodash';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiFieldNumber,
@@ -23,11 +23,13 @@ import { fetchFieldsFromESQL } from '@kbn/text-based-editor';
import { AggregateQuery, getIndexPatternFromESQLQuery } from '@kbn/es-query';
import { parseDuration } from '@kbn/alerting-plugin/common';
import {
+ FieldOption,
firstFieldOption,
getTimeFieldOptions,
getTimeOptions,
parseAggregationResults,
} from '@kbn/triggers-actions-ui-plugin/public/common';
+import { SourceFields } from '../../components/source_fields_select';
import { EsQueryRuleParams, EsQueryRuleMetaData, SearchType } from '../types';
import { DEFAULT_VALUES } from '../constants';
import { useTriggerUiActionServices } from '../util';
@@ -39,7 +41,7 @@ export const EsqlQueryExpression: React.FC<
RuleTypeParamsExpressionProps, EsQueryRuleMetaData>
> = ({ ruleParams, setRuleParams, setRuleProperty, errors }) => {
const { expressions, http } = useTriggerUiActionServices();
- const { esqlQuery, timeWindowSize, timeWindowUnit, timeField } = ruleParams;
+ const { esqlQuery, timeWindowSize, timeWindowUnit, timeField, sourceFields } = ruleParams;
const [currentRuleParams, setCurrentRuleParams] = useState<
EsQueryRuleParams
@@ -57,10 +59,12 @@ export const EsqlQueryExpression: React.FC<
groupBy: DEFAULT_VALUES.GROUP_BY,
termSize: DEFAULT_VALUES.TERM_SIZE,
searchType: SearchType.esqlQuery,
+ sourceFields: sourceFields ?? DEFAULT_VALUES.SOURCE_FIELDS,
});
const [query, setQuery] = useState({ esql: '' });
const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]);
const [detectTimestamp, setDetectTimestamp] = useState(false);
+ const [esFields, setEsFields] = useState([]);
const setParam = useCallback(
(paramField: string, paramValue: unknown) => {
@@ -79,6 +83,7 @@ export const EsqlQueryExpression: React.FC<
if (esqlQuery && 'esql' in esqlQuery) {
if (esqlQuery.esql) {
refreshTimeFields(esqlQuery);
+ refreshEsFields(esqlQuery, false);
}
}
if (timeField) {
@@ -138,6 +143,7 @@ export const EsqlQueryExpression: React.FC<
let hasTimestamp = false;
const indexPattern: string = getIndexPatternFromESQLQuery(get(q, 'esql'));
const currentEsFields = await getFields(http, [indexPattern]);
+
const timeFields = getTimeFieldOptions(currentEsFields);
setTimeFieldOptions([firstFieldOption, ...timeFields]);
@@ -149,6 +155,29 @@ export const EsqlQueryExpression: React.FC<
setDetectTimestamp(hasTimestamp);
};
+ const refreshEsFields = async (q: AggregateQuery, resetSourceFields: boolean = true) => {
+ let fields: FieldOption[] = [];
+ try {
+ const table = await fetchFieldsFromESQL({ esql: `${get(q, 'esql')} | limit 0` }, expressions);
+ if (table) {
+ fields = table.columns.map((c) => ({
+ name: c.id,
+ type: c.meta.type,
+ normalizedType: c.meta.type,
+ searchable: true,
+ aggregatable: true,
+ }));
+ }
+ } catch (error) {
+ /** ignore error */
+ }
+
+ if (resetSourceFields) {
+ setParam('sourceFields', undefined);
+ }
+ setEsFields(fields);
+ };
+
return (
@@ -163,11 +192,12 @@ export const EsqlQueryExpression: React.FC<
{
+ onTextLangQueryChange={debounce((q: AggregateQuery) => {
setQuery(q);
setParam('esqlQuery', q);
refreshTimeFields(q);
- }}
+ refreshEsFields(q);
+ }, 1000)}
expandCodeEditor={() => true}
isCodeEditorExpanded={true}
onTextLangQuerySubmit={() => {}}
@@ -176,6 +206,14 @@ export const EsqlQueryExpression: React.FC<
hideRunQueryText={true}
/>
+
+ setParam('sourceFields', selectedSourceFields)
+ }
+ esFields={esFields}
+ sourceFields={sourceFields}
+ errors={errors.sourceFields}
+ />
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression.tsx
index b8fd6d1ee086f0..dae9a67fd0c380 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression.tsx
@@ -43,6 +43,7 @@ export const SearchSourceExpression = ({
termField,
termSize,
excludeHitsFromPreviousRun,
+ sourceFields,
} = ruleParams;
const { data } = useTriggerUiActionServices();
@@ -92,6 +93,7 @@ export const SearchSourceExpression = ({
termSize: termSize ?? DEFAULT_VALUES.TERM_SIZE,
excludeHitsFromPreviousRun:
excludeHitsFromPreviousRun ?? DEFAULT_VALUES.EXCLUDE_PREVIOUS_HITS,
+ sourceFields,
});
setSearchSource(createdSearchSource);
} catch (error) {
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx
index c4419a780809df..c6ee0157e082ab 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/search_source_expression_form.tsx
@@ -27,7 +27,13 @@ import {
import { STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils';
import { getComparatorScript } from '../../../../common';
import { Comparator } from '../../../../common/comparator_types';
-import { CommonRuleParams, EsQueryRuleMetaData, EsQueryRuleParams, SearchType } from '../types';
+import {
+ CommonRuleParams,
+ EsQueryRuleMetaData,
+ EsQueryRuleParams,
+ SearchType,
+ SourceField,
+} from '../types';
import { DEFAULT_VALUES } from '../constants';
import { DataViewSelectPopover } from '../../components/data_view_select_popover';
import { RuleCommonExpressions } from '../rule_common_expressions';
@@ -49,7 +55,7 @@ interface LocalStateAction {
type: SearchSourceParamsAction['type'] | keyof CommonRuleParams;
payload:
| SearchSourceParamsAction['payload']
- | (number[] | number | string | string[] | boolean | undefined);
+ | (number[] | number | string | string[] | boolean | SourceField[] | undefined);
}
type LocalStateReducer = (prevState: LocalState, action: LocalStateAction) => LocalState;
@@ -112,6 +118,7 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp
size: ruleParams.size ?? DEFAULT_VALUES.SIZE,
excludeHitsFromPreviousRun:
ruleParams.excludeHitsFromPreviousRun ?? DEFAULT_VALUES.EXCLUDE_PREVIOUS_HITS,
+ sourceFields: ruleParams.sourceFields,
}
);
@@ -123,8 +130,9 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp
);
const onSelectDataView = useCallback((newDataView: DataView) => {
- setEsFields(convertFieldSpecToFieldOption(newDataView.fields.map((field) => field.toSpec())));
dispatch({ type: 'index', payload: newDataView });
+ dispatch({ type: 'sourceFields', payload: undefined });
+ setEsFields(convertFieldSpecToFieldOption(newDataView.fields.map((field) => field.toSpec())));
}, []);
const onUpdateFilters = useCallback((newFilters) => {
@@ -226,6 +234,12 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp
[]
);
+ const onChangeSourceFields = useCallback(
+ (selectedSourceFields: SourceField[]) =>
+ dispatch({ type: 'sourceFields', payload: selectedSourceFields }),
+ []
+ );
+
const timeWindow = `${ruleConfiguration.timeWindowSize}${ruleConfiguration.timeWindowUnit}`;
const createTestSearchSource = useCallback(() => {
@@ -372,6 +386,8 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp
excludeHitsFromPreviousRun={ruleConfiguration.excludeHitsFromPreviousRun}
onChangeExcludeHitsFromPreviousRun={onChangeExcludeHitsFromPreviousRun}
canSelectMultiTerms={DEFAULT_VALUES.CAN_SELECT_MULTI_TERMS}
+ onChangeSourceFields={onChangeSourceFields}
+ sourceFields={ruleConfiguration.sourceFields}
/>
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.test.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.test.tsx
index 267b289cf5ca85..c235c3fe8fc3e5 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.test.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.test.tsx
@@ -93,6 +93,7 @@ describe('RuleCommonExpressions', () => {
}}
excludeHitsFromPreviousRun={excludeHitsFromPreviousRun}
onChangeExcludeHitsFromPreviousRun={onChangeExcludeHitsFromPreviousRunFn}
+ onChangeSourceFields={() => {}}
/>
);
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.tsx
index cee08144f307e7..313d3f586c73ef 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.tsx
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/rule_common_expressions/rule_common_expressions.tsx
@@ -28,7 +28,8 @@ import {
WhenExpression,
} from '@kbn/triggers-actions-ui-plugin/public';
import { builtInGroupByTypes, FieldOption } from '@kbn/triggers-actions-ui-plugin/public/common';
-import { CommonRuleParams } from '../types';
+import { SourceFields } from '../../components/source_fields_select';
+import { CommonRuleParams, SourceField } from '../types';
import { DEFAULT_VALUES } from '../constants';
import { TestQueryRow, TestQueryRowProps } from '../test_query_row';
import { QueryThresholdHelpPopover } from './threshold_help_popover';
@@ -53,6 +54,7 @@ export interface RuleCommonExpressionsProps extends CommonRuleParams {
onCopyQuery?: TestQueryRowProps['copyQuery'];
onChangeExcludeHitsFromPreviousRun: (exclude: boolean) => void;
canSelectMultiTerms?: boolean;
+ onChangeSourceFields: (selectedSourceFields: SourceField[]) => void;
}
export const RuleCommonExpressions: React.FC = ({
@@ -67,6 +69,7 @@ export const RuleCommonExpressions: React.FC = ({
termField,
termSize,
size,
+ sourceFields,
errors,
hasValidationErrors,
onChangeSelectedAggField,
@@ -84,6 +87,7 @@ export const RuleCommonExpressions: React.FC = ({
excludeHitsFromPreviousRun,
onChangeExcludeHitsFromPreviousRun,
canSelectMultiTerms,
+ onChangeSourceFields,
}) => {
const [isExcludeHitsDisabled, setIsExcludeHitsDisabled] = useState(false);
@@ -205,6 +209,13 @@ export const RuleCommonExpressions: React.FC = ({
})}
/>
+
+
Promise.resolve({
testResults: {
- results: [{ group: 'all documents', hits: [], count: 42 }],
+ results: [{ group: 'all documents', hits: [], count: 42, sourceFields: [] }],
truncated: false,
},
isGrouped: false,
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/test_query_row/use_test_query.test.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/test_query_row/use_test_query.test.ts
index 44c2a1cf557177..f476d7d896b69d 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/test_query_row/use_test_query.test.ts
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/test_query_row/use_test_query.test.ts
@@ -15,7 +15,7 @@ describe('useTestQuery', () => {
initialProps: () =>
Promise.resolve({
testResults: {
- results: [{ group: 'all documents', hits: [], count: 1 }],
+ results: [{ group: 'all documents', hits: [], count: 1, sourceFields: [] }],
truncated: false,
},
isGrouped: false,
@@ -46,8 +46,8 @@ describe('useTestQuery', () => {
Promise.resolve({
testResults: {
results: [
- { group: 'a', count: 1, value: 10, hits: [] },
- { group: 'b', count: 2, value: 20, hits: [] },
+ { group: 'a', count: 1, value: 10, hits: [], sourceFields: [] },
+ { group: 'b', count: 2, value: 20, hits: [], sourceFields: [] },
],
truncated: false,
},
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/types.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/types.ts
index 93b184f8552326..4e1abc5b52a5cb 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/types.ts
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/types.ts
@@ -17,6 +17,11 @@ export enum SearchType {
esqlQuery = 'esqlQuery',
}
+export interface SourceField {
+ label: string;
+ searchPath: string;
+}
+
export interface CommonRuleParams {
size: number;
thresholdComparator?: string;
@@ -29,6 +34,7 @@ export interface CommonRuleParams {
termSize?: number;
termField?: string | string[];
excludeHitsFromPreviousRun: boolean;
+ sourceFields?: SourceField[];
}
export interface CommonEsQueryRuleParams extends RuleTypeParams, CommonRuleParams {}
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.test.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.test.ts
index 54363fe1010f3e..e665ac79088884 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.test.ts
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.test.ts
@@ -377,4 +377,27 @@ describe('expression params validation', () => {
'Threshold is required to be 0.'
);
});
+
+ test('if sourceFields property is an array but has more than 5 items, should return proper error message', () => {
+ const sourceField = { label: 'test', searchPath: 'test' };
+ const initialParams: EsQueryRuleParams = {
+ index: ['test'],
+ esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
+ size: 100,
+ timeWindowSize: 1,
+ timeWindowUnit: 's',
+ threshold: [0],
+ timeField: '',
+ excludeHitsFromPreviousRun: true,
+ aggType: 'count',
+ groupBy: 'top',
+ termSize: 10,
+ termField: ['term'],
+ sourceFields: [sourceField, sourceField, sourceField, sourceField, sourceField, sourceField],
+ };
+ expect(validateExpression(initialParams).errors.sourceFields.length).toBeGreaterThan(0);
+ expect(validateExpression(initialParams).errors.sourceFields[0]).toBe(
+ 'Cannot select more than 5 fields'
+ );
+ });
});
diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts
index 5830a506a00732..59c5a4ff55e814 100644
--- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts
+++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts
@@ -14,7 +14,10 @@ import {
builtInGroupByTypes,
COMPARATORS,
} from '@kbn/triggers-actions-ui-plugin/public';
-import { MAX_SELECTABLE_GROUP_BY_TERMS } from '../../../common/constants';
+import {
+ MAX_SELECTABLE_SOURCE_FIELDS,
+ MAX_SELECTABLE_GROUP_BY_TERMS,
+} from '../../../common/constants';
import { EsQueryRuleParams, SearchType } from './types';
import { isEsqlQueryRule, isSearchSourceRule } from './util';
import {
@@ -35,6 +38,7 @@ const validateCommonParams = (ruleParams: EsQueryRuleParams) => {
groupBy,
termSize,
termField,
+ sourceFields,
} = ruleParams;
const errors: typeof COMMON_EXPRESSION_ERRORS = defaultsDeep({}, COMMON_EXPRESSION_ERRORS);
@@ -149,6 +153,19 @@ const validateCommonParams = (ruleParams: EsQueryRuleParams) => {
);
}
+ if (
+ sourceFields &&
+ Array.isArray(sourceFields) &&
+ sourceFields.length > MAX_SELECTABLE_SOURCE_FIELDS
+ ) {
+ errors.sourceFields.push(
+ i18n.translate('xpack.stackAlerts.esqlQuery.ui.validation.error.sourceFields', {
+ defaultMessage: `Cannot select more than {max} fields`,
+ values: { max: MAX_SELECTABLE_SOURCE_FIELDS },
+ })
+ );
+ }
+
return errors;
};
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.test.ts
index 33d7babe6b1b9f..aa8c7d1dbc0def 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.test.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.test.ts
@@ -34,6 +34,7 @@ describe('addMessages', () => {
conditions: 'count greater than 4',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -67,6 +68,7 @@ describe('addMessages', () => {
conditions: 'count not greater than 4',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -101,6 +103,7 @@ describe('addMessages', () => {
conditions: 'count between 4 and 5',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -136,6 +139,7 @@ describe('addMessages', () => {
conditions: `count for group "host-1" not greater than 4`,
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -172,6 +176,7 @@ describe('addMessages', () => {
conditions: 'count greater than 4',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -205,6 +210,7 @@ describe('addMessages', () => {
conditions: 'count greater than 4',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
@@ -238,6 +244,7 @@ describe('addMessages', () => {
conditions: 'count greater than 4',
hits: [],
link: 'link-mock',
+ sourceFields: [],
};
const context = addMessages({
ruleName: '[rule-name]',
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.ts
index fe70914a9e7bd1..719d497a10236f 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/action_context.ts
@@ -34,6 +34,7 @@ export interface EsQueryRuleActionContext extends AlertInstanceContext {
// a link to see records that triggered the rule for Discover rule
// a link which navigates to stack management in case of Elastic query rule
link: string;
+ sourceFields: string[];
}
interface AddMessagesOpts {
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts
index 02ddc658b021f1..7ab974dc8d82bb 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.test.ts
@@ -645,6 +645,7 @@ describe('es_query executor', () => {
message: 'Document count is 0 in the last 5m. Alert when greater than or equal to 500.',
title: "rule 'test-rule-name' recovered",
value: 0,
+ sourceFields: [],
},
payload: {
'kibana.alert.evaluation.conditions':
@@ -705,6 +706,7 @@ describe('es_query executor', () => {
'Document count is 0 in the last 5m for host-1. Alert when greater than or equal to 200.',
title: "rule 'test-rule-name' recovered",
value: 0,
+ sourceFields: [],
},
payload: {
'kibana.alert.evaluation.conditions':
@@ -729,6 +731,7 @@ describe('es_query executor', () => {
'Document count is 0 in the last 5m for host-2. Alert when greater than or equal to 200.',
title: "rule 'test-rule-name' recovered",
value: 0,
+ sourceFields: [],
},
payload: {
'kibana.alert.evaluation.conditions':
@@ -783,6 +786,7 @@ describe('es_query executor', () => {
message: 'Document count is 0 in the last 5m. Alert when greater than 0.',
title: "rule 'test-rule-name' recovered",
value: 0,
+ sourceFields: [],
},
payload: {
'kibana.alert.evaluation.conditions': 'Query did NOT match documents',
@@ -797,6 +801,87 @@ describe('es_query executor', () => {
expect(mockSetLimitReached).toHaveBeenCalledTimes(1);
expect(mockSetLimitReached).toHaveBeenCalledWith(false);
});
+
+ it('should correctly handle alerts with sourceFields', async () => {
+ mockFetchEsQuery.mockResolvedValueOnce({
+ parsedResults: {
+ results: [
+ {
+ group: 'host-1',
+ count: 291,
+ hits: [],
+ sourceFields: {
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ },
+ ],
+ truncated: false,
+ },
+ link: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id',
+ });
+ await executor(coreMock, {
+ ...defaultExecutorOptions,
+ // @ts-expect-error
+ params: {
+ ...defaultProps,
+ threshold: [200],
+ thresholdComparator: '>=' as Comparator,
+ groupBy: 'top',
+ termSize: 10,
+ termField: 'host.name',
+ sourceFields: [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id.keyword' },
+ { label: 'host.name', searchPath: 'host.name.keyword' },
+ ],
+ },
+ });
+
+ expect(mockReport).toHaveBeenCalledTimes(1);
+ expect(mockReport).toHaveBeenNthCalledWith(1, {
+ actionGroup: 'query matched',
+ context: {
+ conditions:
+ 'Number of matching documents for group "host-1" is greater than or equal to 200',
+ date: new Date(mockNow).toISOString(),
+ hits: [],
+ link: 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id',
+ message:
+ 'Document count is 291 in the last 5m for host-1. Alert when greater than or equal to 200.',
+ title: "rule 'test-rule-name' matched query for group host-1",
+ value: 291,
+ sourceFields: {
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ },
+ id: 'host-1',
+ state: {
+ dateEnd: new Date(mockNow).toISOString(),
+ dateStart: new Date(mockNow).toISOString(),
+ latestTimestamp: undefined,
+ },
+ payload: {
+ 'kibana.alert.evaluation.conditions':
+ 'Number of matching documents for group "host-1" is greater than or equal to 200',
+ 'kibana.alert.evaluation.threshold': 200,
+ 'kibana.alert.evaluation.value': '291',
+ 'kibana.alert.reason':
+ 'Document count is 291 in the last 5m for host-1. Alert when greater than or equal to 200.',
+ 'kibana.alert.title': "rule 'test-rule-name' matched query for group host-1",
+ 'kibana.alert.url':
+ 'https://localhost:5601/app/management/insightsAndAlerting/triggersActions/rule/test-rule-id',
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ });
+ expect(mockSetLimitReached).toHaveBeenCalledTimes(1);
+ expect(mockSetLimitReached).toHaveBeenCalledWith(false);
+ });
});
describe('tryToParseAsDate', () => {
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts
index 7d8d2df355c16c..5e23bf9498ec5e 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/executor.ts
@@ -136,6 +136,7 @@ export async function executor(core: CoreSetup, options: ExecutorOptions {
expect(onValidate()).not.toThrow();
});
+ it('fails for invalid sourceFields', async () => {
+ // no array that has more than 5 elements
+ const sourceField = { label: 'test', searchPath: 'test' };
+ params.sourceFields = [
+ sourceField,
+ sourceField,
+ sourceField,
+ sourceField,
+ sourceField,
+ sourceField,
+ ];
+ expect(onValidate()).toThrow(
+ '[sourceFields]: array size is [6], but cannot be greater than [5]'
+ );
+ });
+
describe('esqlQuery search type', () => {
beforeEach(() => {
params = { ...DefaultParams, searchType: 'esqlQuery', esqlQuery: { esql: 'from test' } };
diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type_params.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type_params.ts
index d1f99b4e97b93d..73e8eae32cf9b2 100644
--- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type_params.ts
+++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type_params.ts
@@ -15,7 +15,10 @@ import {
} from '@kbn/triggers-actions-ui-plugin/server';
import { RuleTypeState } from '@kbn/alerting-plugin/server';
import { SerializedSearchSourceFields } from '@kbn/data-plugin/common';
-import { MAX_SELECTABLE_GROUP_BY_TERMS } from '../../../common/constants';
+import {
+ MAX_SELECTABLE_SOURCE_FIELDS,
+ MAX_SELECTABLE_GROUP_BY_TERMS,
+} from '../../../common/constants';
import { ComparatorFnNames } from '../../../common';
import { Comparator } from '../../../common/comparator_types';
import { getComparatorSchemaType } from '../lib/comparator';
@@ -96,6 +99,17 @@ const EsQueryRuleParamsSchemaProperties = {
schema.object({ esql: schema.string({ minLength: 1 }) }),
schema.never()
),
+ sourceFields: schema.maybe(
+ schema.arrayOf(
+ schema.object({
+ label: schema.string(),
+ searchPath: schema.string(),
+ }),
+ {
+ maxSize: MAX_SELECTABLE_SOURCE_FIELDS,
+ }
+ )
+ ),
};
export const EsQueryRuleParamsSchema = schema.object(EsQueryRuleParamsSchemaProperties, {
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx
index 23e82f6f2c6dc8..d7f3b79ef53cbc 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/render_app.tsx
@@ -11,11 +11,7 @@ import { i18n as i18nFormatter } from '@kbn/i18n';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
import { SyntheticsAppProps } from './contexts';
import { getIntegratedAppAvailability } from './utils/adapters';
-import {
- DEFAULT_DARK_MODE,
- DEFAULT_TIMEPICKER_QUICK_RANGES,
- INTEGRATED_SOLUTIONS,
-} from '../../../common/constants';
+import { DEFAULT_TIMEPICKER_QUICK_RANGES, INTEGRATED_SOLUTIONS } from '../../../common/constants';
import { SyntheticsApp } from './synthetics_app';
import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin';
@@ -32,6 +28,7 @@ export function renderApp(
docLinks,
http: { basePath },
i18n,
+ theme,
} = core;
const { apm, infrastructure, logs } = getIntegratedAppAvailability(
@@ -40,6 +37,7 @@ export function renderApp(
);
const canSave = (capabilities.uptime.save ?? false) as boolean; // TODO: Determine for synthetics
+ const darkMode = theme.getTheme().darkMode;
const props: SyntheticsAppProps = {
isDev,
@@ -49,7 +47,7 @@ export function renderApp(
i18n,
startPlugins,
basePath: basePath.get(),
- darkMode: core.uiSettings.get(DEFAULT_DARK_MODE),
+ darkMode,
commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES),
isApmAvailable: apm,
isInfraAvailable: infrastructure,
diff --git a/x-pack/plugins/synthetics/public/hooks/use_base_chart_theme.ts b/x-pack/plugins/synthetics/public/hooks/use_base_chart_theme.ts
index be10a050905e6a..413081b998aecf 100644
--- a/x-pack/plugins/synthetics/public/hooks/use_base_chart_theme.ts
+++ b/x-pack/plugins/synthetics/public/hooks/use_base_chart_theme.ts
@@ -7,10 +7,11 @@
import { DARK_THEME, LIGHT_THEME, Theme } from '@elastic/charts';
import { useMemo } from 'react';
-import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
+import { useDarkMode } from '@kbn/kibana-react-plugin/public';
export const useBaseChartTheme = (): Theme => {
- const [darkMode] = useUiSetting$('theme:darkMode');
+ const darkMode = useDarkMode(false);
+
return useMemo(() => {
return darkMode ? DARK_THEME : LIGHT_THEME;
}, [darkMode]);
diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/cell_renderer.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/cell_renderer.tsx
index 520a9d69c4f2a5..6ac03f591eb5fa 100644
--- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/cell_renderer.tsx
+++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/table/cell_renderer.tsx
@@ -8,8 +8,8 @@
import { EuiDataGridCellValueElementProps } from '@elastic/eui';
import React, { useContext, useEffect } from 'react';
import { euiDarkVars as themeDark, euiLightVars as themeLight } from '@kbn/ui-theme';
+import { useDarkMode } from '@kbn/kibana-react-plugin/public';
import { useStyles } from './styles';
-import { useKibana } from '../../../../hooks/use_kibana';
import { Indicator } from '../../../../../common/types/indicator';
import { IndicatorFieldValue } from '../common/field_value';
import { IndicatorsTableContext } from '../../hooks/use_table_context';
@@ -25,11 +25,7 @@ export const cellRendererFactory = (from: number) => {
throw new Error('this can only be used inside indicators table');
}
- const {
- services: { uiSettings },
- } = useKibana();
-
- const darkMode = uiSettings.get('theme:darkMode');
+ const darkMode = useDarkMode();
const { indicators, expanded } = indicatorsTableContext;
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 1f97750f40911b..d47d3add7fd8ba 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -34810,7 +34810,6 @@
"xpack.securitySolution.entityAnalytics.hostsRiskDashboard.title": "Scores de risque de l'hôte",
"xpack.securitySolution.entityAnalytics.riskDashboard.viewAllLabel": "Afficher tout",
"xpack.securitySolution.entityAnalytics.technicalPreviewLabel": "Version d'évaluation technique",
- "xpack.securitySolution.entityAnalytics.totalLabel": "Total",
"xpack.securitySolution.entityAnalytics.usersRiskDashboard.title": "Scores de risque de l'utilisateur",
"xpack.securitySolution.event.module.linkToElasticEndpointSecurityDescription": "Ouvrir dans Endpoint Security",
"xpack.securitySolution.event.summary.threat_indicator.modal.allMatches": "Toutes les correspondances d'indicateur",
@@ -35969,7 +35968,6 @@
"xpack.securitySolution.riskScore.errorPanel.errors": "Erreurs",
"xpack.securitySolution.riskScore.errorPanel.message": "Un problème est survenu. Réessayez plus tard.",
"xpack.securitySolution.riskScore.errorPanel.title": "Désolé, une erreur est survenue.",
- "xpack.securitySolution.riskScore.errorSearchDescription": "Une erreur s'est produite sur la recherche du score de risque",
"xpack.securitySolution.riskScore.failSearchDescription": "Impossible de lancer une recherche sur le score de risque",
"xpack.securitySolution.riskScore.hostRiskScoresEnabledTitle": "Scores de risque de l'hôte activés",
"xpack.securitySolution.riskScore.hostsDashboardWarningPanelBody": "Nous n’avons pas trouvé de données de score de risque de l’hôte. Vérifiez si vous avez des filtres globaux dans la barre de recherche KQL globale. Si vous venez d’activer le module de risque de l’hôte, le moteur de risque peut mettre une heure à générer les données de score de risque de l’hôte et les afficher dans ce panneau.",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 0bcc41300dc7b5..71ebe72b930256 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -34809,7 +34809,6 @@
"xpack.securitySolution.entityAnalytics.hostsRiskDashboard.title": "ホストリスクスコア",
"xpack.securitySolution.entityAnalytics.riskDashboard.viewAllLabel": "すべて表示",
"xpack.securitySolution.entityAnalytics.technicalPreviewLabel": "テクニカルプレビュー",
- "xpack.securitySolution.entityAnalytics.totalLabel": "合計",
"xpack.securitySolution.entityAnalytics.usersRiskDashboard.title": "ユーザーリスクスコア",
"xpack.securitySolution.event.module.linkToElasticEndpointSecurityDescription": "Endpoint Securityで開く",
"xpack.securitySolution.event.summary.threat_indicator.modal.allMatches": "すべてのインジケーター一致",
@@ -35968,7 +35967,6 @@
"xpack.securitySolution.riskScore.errorPanel.errors": "エラー",
"xpack.securitySolution.riskScore.errorPanel.message": "何か問題が発生しましたしばらくたってから再試行してください。",
"xpack.securitySolution.riskScore.errorPanel.title": "申し訳ございません、エラーが発生しました",
- "xpack.securitySolution.riskScore.errorSearchDescription": "リスクスコア検索でエラーが発生しました",
"xpack.securitySolution.riskScore.failSearchDescription": "リスクスコアで検索を実行できませんでした",
"xpack.securitySolution.riskScore.hostRiskScoresEnabledTitle": "ホストリスクスコア有効",
"xpack.securitySolution.riskScore.hostsDashboardWarningPanelBody": "ホストリスクスコアデータが見つかりません。グローバルKQL検索バーにグローバルフィルターがあるかどうかを確認してください。ホストリスクモジュールを有効にしたばかりの場合は、リスクエンジンがホストリスクスコアデータを生成し、このパネルに表示するまでに1時間かかることがあります。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 9e283e2a5fe0bb..ff583c718ddf8b 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -34804,7 +34804,6 @@
"xpack.securitySolution.entityAnalytics.hostsRiskDashboard.title": "主机风险分数",
"xpack.securitySolution.entityAnalytics.riskDashboard.viewAllLabel": "查看全部",
"xpack.securitySolution.entityAnalytics.technicalPreviewLabel": "技术预览",
- "xpack.securitySolution.entityAnalytics.totalLabel": "合计",
"xpack.securitySolution.entityAnalytics.usersRiskDashboard.title": "用户风险分数",
"xpack.securitySolution.event.module.linkToElasticEndpointSecurityDescription": "在 Endpoint Security 中打开",
"xpack.securitySolution.event.summary.threat_indicator.modal.allMatches": "所有指标匹配",
@@ -35963,7 +35962,6 @@
"xpack.securitySolution.riskScore.errorPanel.errors": "错误",
"xpack.securitySolution.riskScore.errorPanel.message": "出问题了。请稍后重试。",
"xpack.securitySolution.riskScore.errorPanel.title": "抱歉,有错误",
- "xpack.securitySolution.riskScore.errorSearchDescription": "搜索风险分数时发生错误",
"xpack.securitySolution.riskScore.failSearchDescription": "无法对风险分数执行搜索",
"xpack.securitySolution.riskScore.hostRiskScoresEnabledTitle": "已启用主机风险分数",
"xpack.securitySolution.riskScore.hostsDashboardWarningPanelBody": "找不到任何主机风险分数数据。检查全局 KQL 搜索栏中是否具有任何全局筛选。如果刚刚启用了主机风险模块,风险引擎可能需要一小时才能生成并在此面板中显示主机风险分数数据。",
diff --git a/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.test.ts b/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.test.ts
index 1925954ea7bb9d..08c43eb9f8c0fb 100644
--- a/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.test.ts
@@ -919,4 +919,142 @@ describe('buildAgg', () => {
},
});
});
+
+ it('should correctly apply the sourceFieldsParams if specified', async () => {
+ expect(
+ buildAggregation({
+ timeSeries: {
+ timeField: 'time-field',
+ timeWindowSize: 5,
+ timeWindowUnit: 'm',
+ dateStart: '2021-04-22T15:19:31Z',
+ dateEnd: '2021-04-22T15:20:31Z',
+ interval: '1m',
+ },
+ aggType: 'count',
+ aggField: undefined,
+ termField: 'the-term',
+ termSize: 100,
+ condition: {
+ conditionScript: `params.compareValue > 1`,
+ },
+ sourceFieldsParams: [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id.keyword' },
+ { label: 'host.name', searchPath: 'host.name.keyword' },
+ ],
+ })
+ ).toEqual({
+ groupAgg: {
+ terms: {
+ field: 'the-term',
+ size: 100,
+ },
+ aggs: {
+ conditionSelector: {
+ bucket_selector: {
+ buckets_path: {
+ compareValue: '_count',
+ },
+ script: `params.compareValue > 1`,
+ },
+ },
+ dateAgg: {
+ date_range: {
+ field: 'time-field',
+ format: 'strict_date_time',
+ ranges: [
+ {
+ from: '2021-04-22T15:14:31.000Z',
+ to: '2021-04-22T15:19:31.000Z',
+ },
+ {
+ from: '2021-04-22T15:15:31.000Z',
+ to: '2021-04-22T15:20:31.000Z',
+ },
+ ],
+ },
+ },
+ 'host.hostname': {
+ terms: {
+ field: 'host.hostname.keyword',
+ size: 10,
+ },
+ },
+ 'host.id': {
+ terms: {
+ field: 'host.id.keyword',
+ size: 10,
+ },
+ },
+ 'host.name': {
+ terms: {
+ field: 'host.name.keyword',
+ size: 10,
+ },
+ },
+ },
+ },
+ groupAggCount: {
+ stats_bucket: {
+ buckets_path: 'groupAgg._count',
+ },
+ },
+ });
+ });
+
+ it('should correctly apply the sourceFieldsParams if specified on a grouped query', async () => {
+ expect(
+ buildAggregation({
+ aggType: 'avg',
+ aggField: 'event.duration',
+ termField: 'event.action',
+ termSize: 10,
+ sourceFieldsParams: [{ label: 'event.provider', searchPath: 'event.provider' }],
+ condition: { resultLimit: 1000, conditionScript: 'params.compareValue > -1L' },
+ topHitsSize: 100,
+ })
+ ).toEqual({
+ groupAgg: {
+ aggs: {
+ conditionSelector: {
+ bucket_selector: {
+ buckets_path: {
+ compareValue: 'metricAgg',
+ },
+ script: 'params.compareValue > -1L',
+ },
+ },
+ 'event.provider': {
+ terms: {
+ field: 'event.provider',
+ size: 10,
+ },
+ },
+ metricAgg: {
+ avg: {
+ field: 'event.duration',
+ },
+ },
+ topHitsAgg: {
+ top_hits: {
+ size: 100,
+ },
+ },
+ },
+ terms: {
+ field: 'event.action',
+ order: {
+ metricAgg: 'desc',
+ },
+ size: 10,
+ },
+ },
+ groupAggCount: {
+ stats_bucket: {
+ buckets_path: 'groupAgg._count',
+ },
+ },
+ });
+ });
});
diff --git a/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.ts b/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.ts
index 2c6ccac04c638e..dfb141554a9fd8 100644
--- a/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.ts
+++ b/x-pack/plugins/triggers_actions_ui/common/data/lib/build_agg.ts
@@ -20,6 +20,7 @@ export interface BuildAggregationOpts {
aggField?: string;
termSize?: number;
termField?: string | string[];
+ sourceFieldsParams?: Array<{ label: string; searchPath: string }>;
topHitsSize?: number;
condition?: {
resultLimit?: number;
@@ -30,6 +31,7 @@ export interface BuildAggregationOpts {
export const BUCKET_SELECTOR_PATH_NAME = 'compareValue';
export const BUCKET_SELECTOR_FIELD = `params.${BUCKET_SELECTOR_PATH_NAME}`;
export const DEFAULT_GROUPS = 100;
+export const MAX_SOURCE_FIELDS_TO_COPY = 10;
export const isCountAggregation = (aggType: string) => aggType === 'count';
export const isGroupAggregation = (termField?: string | string[]) => !!termField;
@@ -40,6 +42,7 @@ export const buildAggregation = ({
aggField,
termField,
termSize,
+ sourceFieldsParams,
condition,
topHitsSize,
}: BuildAggregationOpts): Record => {
@@ -126,10 +129,21 @@ export const buildAggregation = ({
},
};
}
-
aggParent = aggParent.aggs.groupAgg;
}
+ // add sourceField aggregations
+ if (sourceFieldsParams && sourceFieldsParams.length > 0) {
+ sourceFieldsParams.forEach((field) => {
+ aggParent.aggs = {
+ ...aggParent.aggs,
+ [field.label]: {
+ terms: { field: field.searchPath, size: MAX_SOURCE_FIELDS_TO_COPY },
+ },
+ };
+ });
+ }
+
// next, add the time window aggregation
if (isDateAgg) {
aggParent.aggs = {
diff --git a/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.test.ts b/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.test.ts
index e0935326368f84..e22d4959c6093b 100644
--- a/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.test.ts
@@ -62,6 +62,50 @@ const sampleHit = {
sort: [1668020234918],
};
+const sampleSourceFieldsHit = {
+ _index: 'test-data',
+ _id: '6S04F4wBEMHmjvc_YPJ_',
+ _score: null,
+ _source: {
+ '@timestamp': '2023-11-27T10:00:00',
+ host: { id: '1', name: 'host-1', hostname: 'host-1' },
+ },
+ fields: { '@timestamp': ['2023-11-27T10:00:00.000Z'] },
+ sort: ['2023-11-27T10:00:00.000Z'],
+};
+
+const sampleAggregations = {
+ 'host.name': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: 'host-1', doc_count: 5 }],
+ },
+ 'host.hostname': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: 'host-1', doc_count: 5 }],
+ },
+ 'host.id': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: '1', doc_count: 5 }],
+ },
+};
+
+const sampleEsqlSourceFieldsHit = {
+ _id: 'esql_query_document',
+ _index: '',
+ _source: {
+ '@timestamp': '2023-11-27T10:00:00.000Z',
+ 'host.hostname': 'host-1',
+ 'host.hostname.keyword': 'host-1',
+ 'host.id': '1',
+ 'host.id.keyword': '1',
+ 'host.name': 'host-1',
+ 'host.name.keyword': 'host-1',
+ },
+};
+
describe('parseAggregationResults', () => {
it('correctly parses results for count over all', () => {
expect(
@@ -85,6 +129,7 @@ describe('parseAggregationResults', () => {
group: 'all documents',
count: 491,
hits: [sampleHit],
+ sourceFields: {},
},
],
truncated: false,
@@ -137,26 +182,31 @@ describe('parseAggregationResults', () => {
group: 'execute',
count: 120,
hits: [],
+ sourceFields: {},
},
{
group: 'execute-start',
count: 120,
hits: [],
+ sourceFields: {},
},
{
group: 'active-instance',
count: 100,
hits: [],
+ sourceFields: {},
},
{
group: 'execute-action',
count: 100,
hits: [],
+ sourceFields: {},
},
{
group: 'new-instance',
count: 100,
hits: [],
+ sourceFields: {},
},
],
truncated: false,
@@ -259,26 +309,31 @@ describe('parseAggregationResults', () => {
group: 'execute',
count: 120,
hits: [sampleHit],
+ sourceFields: {},
},
{
group: 'execute-start',
count: 120,
hits: [sampleHit],
+ sourceFields: {},
},
{
group: 'active-instance',
count: 100,
hits: [sampleHit],
+ sourceFields: {},
},
{
group: 'execute-action',
count: 100,
hits: [sampleHit],
+ sourceFields: {},
},
{
group: 'new-instance',
count: 100,
hits: [sampleHit],
+ sourceFields: {},
},
],
truncated: false,
@@ -309,6 +364,7 @@ describe('parseAggregationResults', () => {
hits: [sampleHit],
count: 643,
value: 3578195238.095238,
+ sourceFields: {},
},
],
truncated: false,
@@ -377,30 +433,35 @@ describe('parseAggregationResults', () => {
count: 120,
hits: [],
value: null,
+ sourceFields: {},
},
{
group: 'execute-start',
count: 139,
hits: [],
value: null,
+ sourceFields: {},
},
{
group: 'starting',
count: 1,
hits: [],
value: null,
+ sourceFields: {},
},
{
group: 'recovered-instance',
count: 120,
hits: [],
value: 12837500000,
+ sourceFields: {},
},
{
group: 'execute',
count: 139,
hits: [],
value: 137647482.0143885,
+ sourceFields: {},
},
],
truncated: false,
@@ -519,30 +580,35 @@ describe('parseAggregationResults', () => {
count: 120,
hits: [sampleHit],
value: null,
+ sourceFields: {},
},
{
group: 'execute-start',
count: 139,
hits: [sampleHit],
value: null,
+ sourceFields: {},
},
{
group: 'starting',
count: 1,
hits: [sampleHit],
value: null,
+ sourceFields: {},
},
{
group: 'recovered-instance',
count: 120,
hits: [sampleHit],
value: 12837500000,
+ sourceFields: {},
},
{
group: 'execute',
count: 139,
hits: [sampleHit],
value: 137647482.0143885,
+ sourceFields: {},
},
],
truncated: false,
@@ -599,19 +665,191 @@ describe('parseAggregationResults', () => {
group: 'execute',
count: 120,
hits: [],
+ sourceFields: {},
},
{
group: 'execute-start',
count: 120,
hits: [],
+ sourceFields: {},
},
{
group: 'active-instance',
count: 100,
hits: [],
+ sourceFields: {},
},
],
truncated: true,
});
});
+
+ it('correctly parses results for count with source fields', () => {
+ expect(
+ parseAggregationResults({
+ isCountAgg: true,
+ isGroupAgg: false,
+ esResult: {
+ took: 1,
+ timed_out: false,
+ _shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
+ hits: {
+ total: 491,
+ max_score: null,
+ hits: [sampleSourceFieldsHit],
+ },
+ aggregations: sampleAggregations,
+ },
+ resultLimit: 1000,
+ sourceFieldsParams: [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id.keyword' },
+ { label: 'host.name', searchPath: 'host.name.keyword' },
+ ],
+ })
+ ).toEqual({
+ results: [
+ {
+ group: 'all documents',
+ count: 491,
+ hits: [sampleSourceFieldsHit],
+ sourceFields: {
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ },
+ ],
+ truncated: false,
+ });
+ });
+
+ it('correctly parses results for aggregate metric with source fields', () => {
+ expect(
+ parseAggregationResults({
+ isCountAgg: true,
+ isGroupAgg: true,
+ esResult: {
+ took: 7,
+ timed_out: false,
+ _shards: { total: 4, successful: 1, skipped: 0, failed: 0 },
+ hits: { total: { value: 4, relation: 'eq' }, max_score: null, hits: [] },
+ aggregations: {
+ groupAgg: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [
+ {
+ key: 'host-1',
+ doc_count: 4,
+ topHitsAgg: {
+ hits: {
+ total: { value: 4, relation: 'eq' },
+ max_score: 0,
+ hits: [
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ ],
+ },
+ },
+ 'host.name': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: 'host-1', doc_count: 4 }],
+ },
+ 'host.hostname': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: 'host-1', doc_count: 4 }],
+ },
+ 'host.id': {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [{ key: '1', doc_count: 4 }],
+ },
+ },
+ ],
+ },
+ groupAggCount: { count: 1, min: 4, max: 4, avg: 4, sum: 4 },
+ },
+ },
+ resultLimit: 1000,
+ sourceFieldsParams: [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id.keyword' },
+ { label: 'host.name', searchPath: 'host.name.keyword' },
+ ],
+ })
+ ).toEqual({
+ results: [
+ {
+ group: 'host-1',
+ hits: [
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ sampleSourceFieldsHit,
+ ],
+ count: 4,
+ sourceFields: {
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ },
+ ],
+ truncated: false,
+ });
+ });
+
+ it('correctly parses results for count with source fields and generateSourceFieldsFromHits = true', () => {
+ expect(
+ parseAggregationResults({
+ isCountAgg: true,
+ isGroupAgg: false,
+ esResult: {
+ took: 0,
+ timed_out: false,
+ _shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
+ hits: {
+ total: 4,
+ hits: [
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ ],
+ },
+ },
+ resultLimit: 1000,
+ sourceFieldsParams: [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id.keyword' },
+ { label: 'host.name', searchPath: 'host.name.keyword' },
+ ],
+ generateSourceFieldsFromHits: true,
+ })
+ ).toEqual({
+ results: [
+ {
+ group: 'all documents',
+ count: 4,
+ hits: [
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ sampleEsqlSourceFieldsHit,
+ ],
+ sourceFields: {
+ 'host.hostname': ['host-1'],
+ 'host.id': ['1'],
+ 'host.name': ['host-1'],
+ },
+ },
+ ],
+ truncated: false,
+ });
+ });
});
diff --git a/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.ts b/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.ts
index 5c1f5ecba90c52..6fe7040abacc34 100644
--- a/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.ts
+++ b/x-pack/plugins/triggers_actions_ui/common/data/lib/parse_aggregation_results.ts
@@ -17,6 +17,7 @@ export interface ParsedAggregationGroup {
group: string;
count: number;
hits: Array>;
+ sourceFields: string[];
value?: number;
}
@@ -30,12 +31,16 @@ interface ParseAggregationResultsOpts {
isGroupAgg: boolean;
esResult: SearchResponse;
resultLimit?: number;
+ sourceFieldsParams?: Array<{ label: string; searchPath: string }>;
+ generateSourceFieldsFromHits?: boolean;
}
export const parseAggregationResults = ({
isCountAgg,
isGroupAgg,
esResult,
resultLimit,
+ sourceFieldsParams = [],
+ generateSourceFieldsFromHits = false,
}: ParseAggregationResultsOpts): ParsedAggregationResults => {
const aggregations = esResult?.aggregations || {};
@@ -51,6 +56,7 @@ export const parseAggregationResults = ({
hits: esResult.hits.hits ?? [],
},
},
+ ...aggregations, // sourceFields
...(!isCountAgg
? {
metricAgg: {
@@ -77,11 +83,32 @@ export const parseAggregationResults = ({
if (resultLimit && results.results.length === resultLimit) break;
const groupName: string = `${groupBucket?.key}`;
+ const sourceFields: { [key: string]: string[] } = {};
+
+ sourceFieldsParams.forEach((field) => {
+ if (generateSourceFieldsFromHits) {
+ const fieldsSet = new Set();
+ groupBucket.topHitsAgg.hits.hits.forEach((hit: SearchHit<{ [key: string]: string }>) => {
+ if (hit._source && hit._source[field.label]) {
+ fieldsSet.add(hit._source[field.label]);
+ }
+ });
+ sourceFields[field.label] = Array.from(fieldsSet);
+ } else {
+ if (groupBucket[field.label]?.buckets && groupBucket[field.label].buckets.length > 0) {
+ sourceFields[field.label] = groupBucket[field.label].buckets.map(
+ (bucket: { doc_count: number; key: string | number }) => bucket.key
+ );
+ }
+ }
+ });
+
const groupResult: any = {
group: groupName,
count: groupBucket?.doc_count,
hits: groupBucket?.topHitsAgg?.hits?.hits ?? [],
...(!isCountAgg ? { value: groupBucket?.metricAgg?.value } : {}),
+ sourceFields,
};
results.results.push(groupResult);
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
index 62eaf684e97dad..585f65954254ae 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
@@ -11,7 +11,6 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { ChromeBreadcrumb, CoreStart, CoreTheme, ScopedHistory } from '@kbn/core/public';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n-react';
-import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs';
import { KibanaFeature } from '@kbn/features-plugin/common';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
@@ -86,17 +85,17 @@ export const renderApp = (deps: TriggersAndActionsUiServices) => {
};
export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
- const { dataViews, uiSettings, theme$ } = deps;
+ const { dataViews, theme } = deps;
const sections: Section[] = ['rules', 'logs', 'alerts'];
- const isDarkMode = useObservable(uiSettings.get$('theme:darkMode'));
+ const isDarkMode = theme.getTheme().darkMode;
const sectionsRegex = sections.join('|');
setDataViewsService(dataViews);
return (
-
-
+
+
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx
index 876c8a26b00ec7..089908dcbbdbcc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/connectors_app.tsx
@@ -11,7 +11,6 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { ChromeBreadcrumb, CoreStart, CoreTheme, ScopedHistory } from '@kbn/core/public';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n-react';
-import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs';
import { KibanaFeature } from '@kbn/features-plugin/common';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
@@ -74,8 +73,8 @@ export const renderApp = (deps: TriggersAndActionsUiServices) => {
};
export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
- const { dataViews, uiSettings, theme$ } = deps;
- const isDarkMode = useObservable(uiSettings.get$('theme:darkMode'));
+ const { dataViews, theme } = deps;
+ const isDarkMode = theme.getTheme().darkMode;
const sections: Section[] = ['connectors', 'logs'];
const sectionsRegex = sections.join('|');
@@ -83,7 +82,7 @@ export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
return (
-
+
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts
index bf4cafcf63da96..649af1c9fa07a8 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts
@@ -6,7 +6,11 @@
*/
import { i18n } from '@kbn/i18n';
-import { ES_QUERY_ID, OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils';
+import {
+ ES_QUERY_ID,
+ OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
+} from '@kbn/rule-data-utils';
export {
BASE_ALERTING_API_PATH,
INTERNAL_BASE_ALERTING_API_PATH,
@@ -120,4 +124,8 @@ export const GLOBAL_CONNECTOR_EXECUTION_DEFAULT_INITIAL_VISIBLE_COLUMNS = [
...CONNECTOR_LOCKED_COLUMNS,
];
-export const MULTI_CONSUMER_RULE_TYPE_IDS = [OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, ES_QUERY_ID];
+export const MULTI_CONSUMER_RULE_TYPE_IDS = [
+ OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
+ ES_QUERY_ID,
+ ML_ANOMALY_DETECTION_RULE_TYPE_ID,
+];
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx
index 7f6a45c7d99926..07264709dd5448 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx
@@ -315,6 +315,7 @@ const RuleAdd = ({
onChangeMetaData={onChangeMetaData}
setConsumer={setSelectedConsumer}
useRuleProducer={useRuleProducer}
+ initialSelectedConsumer={initialSelectedConsumer}
/>
> {
validConsumers?: RuleCreationValidConsumer[];
onChangeMetaData: (metadata: MetaData) => void;
useRuleProducer?: boolean;
+ initialSelectedConsumer?: RuleCreationValidConsumer | null;
}
const EMPTY_ARRAY: string[] = [];
@@ -183,6 +184,7 @@ export const RuleForm = ({
validConsumers,
onChangeMetaData,
useRuleProducer,
+ initialSelectedConsumer,
}: RuleFormProps) => {
const {
notifications: { toasts },
@@ -818,6 +820,7 @@ export const RuleForm = ({
onChange={setConsumer}
errors={errors}
selectedConsumer={selectedConsumer}
+ initialSelectedConsumer={initialSelectedConsumer}
/>
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form_consumer_selection.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form_consumer_selection.test.tsx
index 324e9a290e8316..bec46c682919b8 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form_consumer_selection.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form_consumer_selection.test.tsx
@@ -41,6 +41,71 @@ describe('RuleFormConsumerSelectionModal', () => {
expect(screen.getByText('Stack Rules')).toBeInTheDocument();
});
+ it('should be able to initialize to the prop initialSelectedConsumer', () => {
+ render(
+
+ );
+ expect(mockOnChange).toHaveBeenLastCalledWith('logs');
+ });
+
+ it('should NOT initialize if initialSelectedConsumer is equal to null', () => {
+ render(
+
+ );
+ expect(mockOnChange).not.toBeCalled();
+ });
+
+ it('should initialize to the first valid consumers if initialSelectedConsumer is not valid', () => {
+ render(
+
+ );
+ expect(mockOnChange).toHaveBeenLastCalledWith('logs');
+ });
+
+ it('should initialize to stackAlerts if the initialSelectedConsumer is not a valid and consumers has stackAlerts', () => {
+ render(
+
+ );
+ expect(mockOnChange).toHaveBeenLastCalledWith('stackAlerts');
+ });
+
+ it('should initialize to stackAlerts if the initialSelectedConsumer is undefined and consumers has stackAlerts', () => {
+ render(
+
+ );
+ expect(mockOnChange).toHaveBeenLastCalledWith('stackAlerts');
+ });
+
it('should be able to select infrastructure and call onChange', () => {
render(
void;
errors: IErrorObject;
- selectedConsumer: RuleCreationValidConsumer | null | undefined;
+ selectedConsumer?: RuleCreationValidConsumer | null;
+ /* FUTURE ENGINEER
+ * if this prop is set to null then we wont initialize the value and the user will have to set it
+ * if this prop is set to a valid consumers then we will set it up to what was passed
+ * if this prop is not valid or undefined but the valid consumers has stackAlerts then we will default it to stackAlerts
+ */
+ initialSelectedConsumer?: RuleCreationValidConsumer | null;
}
const SINGLE_SELECTION = { asPlainText: true };
export const RuleFormConsumerSelection = (props: RuleFormConsumerSelectionProps) => {
- const { consumers, errors, onChange, selectedConsumer } = props;
+ const { consumers, errors, onChange, selectedConsumer, initialSelectedConsumer } = props;
const isInvalid = errors?.consumer?.length > 0;
const handleOnChange = useCallback(
(selected: Array>) => {
@@ -123,13 +130,18 @@ export const RuleFormConsumerSelection = (props: RuleFormConsumerSelectionProps)
}, [consumers]);
useEffect(() => {
- // At initialization, select Stack Alerts, or the first value
+ // At initialization, select initialSelectedConsumer or the first value
if (!validatedSelectedConsumer) {
- if (consumers.includes(STACK_ALERTS_FEATURE_ID)) {
+ if (initialSelectedConsumer === null) {
+ return;
+ } else if (initialSelectedConsumer && consumers.includes(initialSelectedConsumer)) {
+ onChange(initialSelectedConsumer);
+ return;
+ } else if (consumers.includes(STACK_ALERTS_FEATURE_ID)) {
onChange(STACK_ALERTS_FEATURE_ID);
return;
}
- onChange(consumers[0] as RuleCreationValidConsumer);
+ onChange(consumers[0]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx
index fe6ba4b9ab91aa..61d9fb7133f65f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx
@@ -41,6 +41,7 @@ import {
RuleLastRunOutcomeValues,
} from '@kbn/alerting-plugin/common';
import {
+ RuleCreationValidConsumer,
ruleDetailsRoute as commonRuleDetailsRoute,
STACK_ALERTS_FEATURE_ID,
} from '@kbn/rule-data-utils';
@@ -136,6 +137,7 @@ export interface RulesListProps {
onTypeFilterChange?: (type: string[]) => void;
onRefresh?: (refresh: Date) => void;
setHeaderActions?: (components?: React.ReactNode[]) => void;
+ initialSelectedConsumer?: RuleCreationValidConsumer | null;
}
export const percentileFields = {
@@ -176,6 +178,7 @@ export const RulesList = ({
onTypeFilterChange,
onRefresh,
setHeaderActions,
+ initialSelectedConsumer = STACK_ALERTS_FEATURE_ID,
}: RulesListProps) => {
const history = useHistory();
const kibanaServices = useKibana().services;
@@ -1007,7 +1010,7 @@ export const RulesList = ({
ruleTypeRegistry={ruleTypeRegistry}
ruleTypeIndex={ruleTypesState.data}
onSave={refreshRules}
- initialSelectedConsumer={STACK_ALERTS_FEATURE_ID}
+ initialSelectedConsumer={initialSelectedConsumer}
/>
)}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss
index abcc87b75cd081..e8b6ec06ed7c99 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecation_logs/fix_deprecation_logs/deprecation_logging_toggle/_deprecation_logging_toggle.scss
@@ -3,5 +3,5 @@
// them. With this selector we offset the difference so that the content
// of the page doesnt jump when toggling between states.
.upgToggleLoading > .upgLoadingItem {
- margin: calc($euiSizeM / 2);
+ margin: $euiSizeM / 2;
}
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/app/render_app.tsx b/x-pack/plugins/uptime/public/legacy_uptime/app/render_app.tsx
index d0c6a2f550e4eb..1a9d5aac398abe 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/app/render_app.tsx
+++ b/x-pack/plugins/uptime/public/legacy_uptime/app/render_app.tsx
@@ -10,11 +10,7 @@ import ReactDOM from 'react-dom';
import { i18n as i18nFormatter } from '@kbn/i18n';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
import { getIntegratedAppAvailability } from '../lib/adapters/framework/capabilities_adapter';
-import {
- DEFAULT_DARK_MODE,
- DEFAULT_TIMEPICKER_QUICK_RANGES,
- INTEGRATED_SOLUTIONS,
-} from '../../../common/constants';
+import { DEFAULT_TIMEPICKER_QUICK_RANGES, INTEGRATED_SOLUTIONS } from '../../../common/constants';
import { UptimeApp, UptimeAppProps } from './uptime_app';
import { ClientPluginsSetup, ClientPluginsStart } from '../../plugin';
@@ -31,6 +27,7 @@ export function renderApp(
docLinks,
http: { basePath },
i18n,
+ theme,
} = core;
const { apm, infrastructure, logs } = getIntegratedAppAvailability(
@@ -39,6 +36,7 @@ export function renderApp(
);
const canSave = (capabilities.uptime.save ?? false) as boolean;
+ const darkMode = theme.getTheme().darkMode;
const props: UptimeAppProps = {
isDev,
@@ -48,7 +46,7 @@ export function renderApp(
i18n,
startPlugins,
basePath: basePath.get(),
- darkMode: core.uiSettings.get(DEFAULT_DARK_MODE),
+ darkMode,
commonlyUsedRanges: core.uiSettings.get(DEFAULT_TIMEPICKER_QUICK_RANGES),
isApmAvailable: apm,
isInfraAvailable: infrastructure,
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
index acf41bdf93448f..6f9a8644bb706c 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
+++ b/x-pack/plugins/uptime/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
@@ -204,6 +204,19 @@ exports[`DonutChart component passes correct props without errors for valid prop
"visible": true,
},
},
+ "bulletGraph": Object {
+ "angularTickLabelPadding": 10,
+ "barBackground": "#343741",
+ "border": "#EDF0F5",
+ "colorBands": Array [
+ "#D9C6EF",
+ "#AA87D1",
+ ],
+ "fallbackBandColor": "#98A2B3",
+ "minHeight": 64,
+ "nonFiniteText": "N/A",
+ "textColor": "#343741",
+ },
"chartMargins": Object {
"bottom": 0,
"left": 0,
@@ -523,7 +536,7 @@ exports[`DonutChart component passes correct props without errors for valid prop
"tooltip": Object {
"defaultDotColor": "black",
"maxTableHeight": 120,
- "maxWidth": 260,
+ "maxWidth": 500,
},
}
}
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/hooks/use_base_chart_theme.ts b/x-pack/plugins/uptime/public/legacy_uptime/hooks/use_base_chart_theme.ts
index f8cb8dfd4134f9..007d463c63e1b0 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/hooks/use_base_chart_theme.ts
+++ b/x-pack/plugins/uptime/public/legacy_uptime/hooks/use_base_chart_theme.ts
@@ -6,11 +6,11 @@
*/
import { useMemo } from 'react';
-import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
+import { useDarkMode } from '@kbn/kibana-react-plugin/public';
import { DARK_THEME, LIGHT_THEME, Theme } from '@elastic/charts';
export const useBaseChartTheme = (): Theme => {
- const [darkMode] = useUiSetting$('theme:darkMode');
+ const darkMode = useDarkMode();
return useMemo(() => {
return darkMode ? DARK_THEME : LIGHT_THEME;
}, [darkMode]);
diff --git a/x-pack/plugins/ux/public/application/ux_app.tsx b/x-pack/plugins/ux/public/application/ux_app.tsx
index aae803dec162a5..e8fdbb96b5e7ba 100644
--- a/x-pack/plugins/ux/public/application/ux_app.tsx
+++ b/x-pack/plugins/ux/public/application/ux_app.tsx
@@ -23,7 +23,7 @@ import {
import {
KibanaContextProvider,
KibanaThemeProvider,
- useUiSetting$,
+ useDarkMode,
} from '@kbn/kibana-react-plugin/public';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
@@ -66,10 +66,9 @@ export const uxRoutes: RouteDefinition[] = [
];
function UxApp() {
- const [darkMode] = useUiSetting$('theme:darkMode');
-
const { http } = useKibanaServices();
const basePath = http.basePath.get();
+ const darkMode = useDarkMode(false);
useBreadcrumbs([
{
diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts
index 90998bc55623b2..d2f29896575833 100644
--- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts
+++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts
@@ -6,6 +6,7 @@
*/
import moment from 'moment';
+import { omit } from 'lodash';
import { cleanup, generate } from '@kbn/infra-forge';
import {
Aggregators,
@@ -14,6 +15,7 @@ import {
import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants';
import expect from '@kbn/expect';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils';
+import { parseSearchParams } from '@kbn/share-plugin/common/url_service';
import { createIndexConnector, createRule } from '../helpers/alerting_api_helper';
import {
waitForAlertInIndex,
@@ -21,7 +23,8 @@ import {
waitForRuleStatus,
} from '../helpers/alerting_wait_for_helpers';
import { FtrProviderContext } from '../../common/ftr_provider_context';
-import { ActionDocument } from './typings';
+import { ActionDocument, LogExplorerLocatorParsedParams } from './typings';
+import { ISO_DATE_REGEX } from './constants';
// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
@@ -35,12 +38,12 @@ export default function ({ getService }: FtrProviderContext) {
const ALERT_ACTION_INDEX = 'alert-action-threshold';
// DATE_VIEW should match the index template:
// x-pack/packages/kbn-infra-forge/src/data_sources/composable/template.json
- const DATE_VIEW = 'kbn-data-forge-fake_hosts';
+ const DATE_VIEW_TITLE = 'kbn-data-forge-fake_hosts';
const DATE_VIEW_NAME = 'ad-hoc-data-view-name';
const DATA_VIEW_ID = 'data-view-id';
const MOCKED_AD_HOC_DATA_VIEW = {
id: DATA_VIEW_ID,
- title: DATE_VIEW,
+ title: DATE_VIEW_TITLE,
timeFieldName: '@timestamp',
sourceFilters: [],
fieldFormats: {},
@@ -122,6 +125,7 @@ export default function ({ getService }: FtrProviderContext) {
reason: '{{context.reason}}',
value: '{{context.value}}',
host: '{{context.host}}',
+ viewInAppUrl: '{{context.viewInAppUrl}}',
},
],
},
@@ -218,6 +222,18 @@ export default function ({ getService }: FtrProviderContext) {
`Average system.cpu.user.pct is 250%, above the threshold of 50%. (duration: 5 mins, data view: ${DATE_VIEW_NAME})`
);
expect(resp.hits.hits[0]._source?.value).eql('250%');
+
+ const parsedViewInAppUrl = parseSearchParams(
+ new URL(resp.hits.hits[0]._source?.viewInAppUrl || '').search
+ );
+
+ expect(resp.hits.hits[0]._source?.viewInAppUrl).contain('LOG_EXPLORER_LOCATOR');
+ expect(omit(parsedViewInAppUrl.params, 'timeRange.from')).eql({
+ dataset: DATE_VIEW_TITLE,
+ timeRange: { to: 'now' },
+ query: { query: '', language: 'kuery' },
+ });
+ expect(parsedViewInAppUrl.params.timeRange.from).match(ISO_DATE_REGEX);
});
});
});
diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts
index ae3689fbb49e3b..e1d029f375c92e 100644
--- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts
+++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts
@@ -5,12 +5,14 @@
* 2.0.
*/
+import { omit } from 'lodash';
import moment from 'moment';
import {
Aggregators,
Comparator,
} from '@kbn/observability-plugin/common/custom_threshold_rule/types';
import { NO_DATA_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants';
+import { parseSearchParams } from '@kbn/share-plugin/common/url_service';
import expect from '@kbn/expect';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils';
@@ -22,7 +24,8 @@ import {
waitForRuleStatus,
} from '../helpers/alerting_wait_for_helpers';
import { FtrProviderContext } from '../../common/ftr_provider_context';
-import { ActionDocument } from './typings';
+import { ISO_DATE_REGEX } from './constants';
+import { ActionDocument, LogExplorerLocatorParsedParams } from './typings';
// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
@@ -114,6 +117,7 @@ export default function ({ getService }: FtrProviderContext) {
alertDetailsUrl: '{{context.alertDetailsUrl}}',
reason: '{{context.reason}}',
value: '{{context.value}}',
+ viewInAppUrl: '{{context.viewInAppUrl}}',
},
],
},
@@ -210,6 +214,18 @@ export default function ({ getService }: FtrProviderContext) {
'Average system.cpu.user.pct reported no data in the last 5m'
);
expect(resp.hits.hits[0]._source?.value).eql('[NO DATA]');
+
+ const parsedViewInAppUrl = parseSearchParams(
+ new URL(resp.hits.hits[0]._source?.viewInAppUrl || '').search
+ );
+
+ expect(resp.hits.hits[0]._source?.viewInAppUrl).contain('LOG_EXPLORER_LOCATOR');
+ expect(omit(parsedViewInAppUrl.params, 'timeRange.from')).eql({
+ dataset: DATA_VIEW_ID,
+ timeRange: { to: 'now' },
+ query: { query: '', language: 'kuery' },
+ });
+ expect(parsedViewInAppUrl.params.timeRange.from).match(ISO_DATE_REGEX);
});
});
});
diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/constants.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/constants.ts
new file mode 100644
index 00000000000000..5cf1e0b4d66145
--- /dev/null
+++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/constants.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts
index 361ca1d05b225d..5bad9470f5d75c 100644
--- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts
+++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts
@@ -5,15 +5,17 @@
* 2.0.
*/
+import { omit } from 'lodash';
import moment from 'moment';
+import expect from '@kbn/expect';
import { cleanup, generate } from '@kbn/infra-forge';
import {
Aggregators,
Comparator,
} from '@kbn/observability-plugin/common/custom_threshold_rule/types';
import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants';
-import expect from '@kbn/expect';
import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils';
+import { parseSearchParams } from '@kbn/share-plugin/common/url_service';
import { createIndexConnector, createRule } from '../helpers/alerting_api_helper';
import { createDataView, deleteDataView } from '../helpers/data_view';
import {
@@ -22,7 +24,8 @@ import {
waitForRuleStatus,
} from '../helpers/alerting_wait_for_helpers';
import { FtrProviderContext } from '../../common/ftr_provider_context';
-import { ActionDocument } from './typings';
+import { ISO_DATE_REGEX } from './constants';
+import { ActionDocument, LogExplorerLocatorParsedParams } from './typings';
// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
@@ -95,14 +98,14 @@ export default function ({ getService }: FtrProviderContext) {
threshold: [1, 2],
timeSize: 1,
timeUnit: 'm',
- metrics: [{ name: 'A', filter: '', aggType: Aggregators.COUNT }],
+ metrics: [{ name: 'A', filter: 'container.id:*', aggType: Aggregators.COUNT }],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
searchConfiguration: {
query: {
- query: '',
+ query: 'host.name:*',
language: 'kuery',
},
index: DATA_VIEW_ID,
@@ -119,6 +122,7 @@ export default function ({ getService }: FtrProviderContext) {
alertDetailsUrl: '{{context.alertDetailsUrl}}',
reason: '{{context.reason}}',
value: '{{context.value}}',
+ viewInAppUrl: '{{context.viewInAppUrl}}',
},
],
},
@@ -190,12 +194,15 @@ export default function ({ getService }: FtrProviderContext) {
threshold: [1, 2],
timeSize: 1,
timeUnit: 'm',
- metrics: [{ name: 'A', filter: '', aggType: 'count' }],
+ metrics: [{ name: 'A', filter: 'container.id:*', aggType: 'count' }],
},
],
alertOnNoData: true,
alertOnGroupDisappear: true,
- searchConfiguration: { index: 'data-view-id', query: { query: '', language: 'kuery' } },
+ searchConfiguration: {
+ index: 'data-view-id',
+ query: { query: 'host.name:*', language: 'kuery' },
+ },
});
});
@@ -214,6 +221,18 @@ export default function ({ getService }: FtrProviderContext) {
`Document count is 3, not between the threshold of 1 and 2. (duration: 1 min, data view: ${DATE_VIEW_NAME})`
);
expect(resp.hits.hits[0]._source?.value).eql('3');
+
+ const parsedViewInAppUrl = parseSearchParams(
+ new URL(resp.hits.hits[0]._source?.viewInAppUrl || '').search
+ );
+
+ expect(resp.hits.hits[0]._source?.viewInAppUrl).contain('LOG_EXPLORER_LOCATOR');
+ expect(omit(parsedViewInAppUrl.params, 'timeRange.from')).eql({
+ dataset: DATA_VIEW_ID,
+ timeRange: { to: 'now' },
+ query: { query: 'host.name:* and container.id:*', language: 'kuery' },
+ });
+ expect(parsedViewInAppUrl.params.timeRange.from).match(ISO_DATE_REGEX);
});
});
});
diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/typings.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/typings.ts
index 83894e6cf24a2f..a0cfb54fff1577 100644
--- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/typings.ts
+++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/typings.ts
@@ -5,11 +5,21 @@
* 2.0.
*/
+import { Query, TimeRange } from '@kbn/es-query';
+import { SerializableRecord } from '@kbn/utility-types';
+
export interface ActionDocument {
ruleType: string;
alertDetailsUrl: string;
reason: string;
value: string;
+ viewInAppUrl: string;
host?: string;
group?: string;
}
+
+export interface LogExplorerLocatorParsedParams extends SerializableRecord {
+ dataset: string;
+ timeRange: TimeRange;
+ query: Query;
+}
diff --git a/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts b/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts
index 7607cecc3e2f29..1a84915a5c935e 100644
--- a/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts
+++ b/x-pack/test/alerting_api_integration/packages/helpers/es_test_index_tool.ts
@@ -61,6 +61,25 @@ export class ESTestIndexTool {
group: {
type: 'keyword',
},
+ host: {
+ properties: {
+ hostname: {
+ type: 'text',
+ fields: {
+ keyword: {
+ type: 'keyword',
+ ignore_above: 256,
+ },
+ },
+ },
+ id: {
+ type: 'keyword',
+ },
+ name: {
+ type: 'keyword',
+ },
+ },
+ },
},
},
},
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create_test_data.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create_test_data.ts
index ee8811cf98f28a..d79fdd086cab9d 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create_test_data.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create_test_data.ts
@@ -109,6 +109,11 @@ async function createEsDocument(
testedValueFloat: 234.2534643,
testedValueUnsigned: '18446744073709551615',
'@timestamp': new Date(epochMillis).toISOString(),
+ host: {
+ hostname: 'host-1',
+ id: '1',
+ name: 'host-1',
+ },
...(group ? { group } : {}),
};
@@ -147,6 +152,25 @@ export async function createDataStream(es: Client, name: string) {
enabled: false,
type: 'object',
},
+ host: {
+ properties: {
+ hostname: {
+ type: 'text',
+ fields: {
+ keyword: {
+ type: 'keyword',
+ ignore_above: 256,
+ },
+ },
+ },
+ id: {
+ type: 'keyword',
+ },
+ name: {
+ type: 'keyword',
+ },
+ },
+ },
},
},
},
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/common.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/common.ts
index fc7a65978aaa0f..26d8c64a302969 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/common.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/common.ts
@@ -24,6 +24,11 @@ export const RULE_INTERVAL_SECONDS = 4;
export const RULE_INTERVAL_MILLIS = RULE_INTERVAL_SECONDS * 1000;
export const ES_GROUPS_TO_WRITE = 3;
+export interface SourceField {
+ label: string;
+ searchPath: string;
+}
+
export async function createConnector(
supertest: any,
objectRemover: ObjectRemover,
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts
index b5b2d41e9a4044..f193af1b81703c 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/esql_only.ts
@@ -22,6 +22,7 @@ import {
RULE_INTERVAL_MILLIS,
RULE_INTERVAL_SECONDS,
RULE_TYPE_ID,
+ SourceField,
} from './common';
import { createDataStream, deleteDataStream } from '../../../create_test_data';
@@ -38,6 +39,12 @@ export default function ruleTests({ getService }: FtrProviderContext) {
getAllAADDocs,
} = getRuleServices(getService);
+ const sourceFields = [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id' },
+ { label: 'host.name', searchPath: 'host.name' },
+ ];
+
describe('rule', async () => {
let endDate: string;
let connectorId: string;
@@ -72,11 +79,15 @@ export default function ruleTests({ getService }: FtrProviderContext) {
await createEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate);
await createRule({
name: 'never fire',
- esqlQuery: 'from .kibana-alerting-test-data | stats c = count(date) | where c < 0',
+ esqlQuery:
+ 'from .kibana-alerting-test-data | stats c = count(date) by host.hostname, host.name, host.id | where c < 0',
+ sourceFields,
});
await createRule({
name: 'always fire',
- esqlQuery: 'from .kibana-alerting-test-data | stats c = count(date) | where c > -1',
+ esqlQuery:
+ 'from .kibana-alerting-test-data | stats c = count(date) by host.hostname, host.name, host.id | where c > -1',
+ sourceFields,
});
const docs = await waitForDocs(2);
@@ -103,6 +114,9 @@ export default function ruleTests({ getService }: FtrProviderContext) {
const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
expect(value).greaterThan(0);
expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
});
it('runs correctly: use epoch millis - threshold on hit count < >', async () => {
@@ -209,13 +223,19 @@ export default function ruleTests({ getService }: FtrProviderContext) {
);
await createRule({
name: 'never fire',
- esqlQuery: 'from test-data-stream | stats c = count(@timestamp) | where c < 0',
+ esqlQuery:
+ 'from test-data-stream | stats c = count(@timestamp) by host.hostname, host.name, host.id | where c < 0',
+ sourceFields,
});
await createRule({
name: 'always fire',
- esqlQuery: 'from test-data-stream | stats c = count(@timestamp) | where c > -1',
+ esqlQuery:
+ 'from test-data-stream | stats c = count(@timestamp) by host.hostname, host.name, host.id | where c > -1',
+ sourceFields,
});
+ const messagePattern = /Document count is \d+ in the last 20s. Alert when greater than 0./;
+
const docs = await waitForDocs(2);
for (let i = 0; i < docs.length; i++) {
const doc = docs[i];
@@ -224,10 +244,22 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(name).to.be('always fire');
expect(title).to.be(`rule 'always fire' matched query`);
- const messagePattern = /Document count is \d+ in the last 20s. Alert when greater than 0./;
expect(message).to.match(messagePattern);
expect(hits).not.to.be.empty();
}
+
+ const aadDocs = await getAllAADDocs(1);
+
+ const alertDoc = aadDocs.body.hits.hits[0]._source;
+ expect(alertDoc[ALERT_REASON]).to.match(messagePattern);
+ expect(alertDoc['kibana.alert.title']).to.be("rule 'always fire' matched query");
+ expect(alertDoc['kibana.alert.evaluation.conditions']).to.be('Query matched documents');
+ const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
+ expect(value).greaterThan(0);
+ expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
});
it('throws an error if the thresholdComparator is not >', async () => {
@@ -365,6 +397,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy?: string;
termField?: string;
termSize?: number;
+ sourceFields?: SourceField[];
}
async function createRule(params: CreateRuleParams): Promise {
@@ -436,6 +469,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
termSize: params.termSize,
timeField: params.timeField || 'date',
esqlQuery: { esql: params.esqlQuery },
+ sourceFields: params.sourceFields,
},
})
.expect(200);
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts
index 8a558c8e27299b..c339bb7f24604d 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/query_dsl_only.ts
@@ -6,9 +6,8 @@
*/
import expect from '@kbn/expect';
-
import { ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers';
-
+import { pull } from 'lodash';
import { Spaces } from '../../../../../scenarios';
import { FtrProviderContext } from '../../../../../../common/ftr_provider_context';
import { getUrlPrefix, ObjectRemover } from '../../../../../../common/lib';
@@ -144,7 +143,16 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(hits).not.to.be.empty();
hits.forEach((hit: any) => {
expect(hit.fields).not.to.be.empty();
- expect(Object.keys(hit.fields).sort()).to.eql(Object.keys(hit._source).sort());
+ expect(
+ pull(
+ // remove nested fields
+ Object.keys(hit.fields),
+ 'host.hostname',
+ 'host.hostname.keyword',
+ 'host.id',
+ 'host.name'
+ ).sort()
+ ).to.eql(Object.keys(hit._source).sort());
});
}
});
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts
index 948eccc893e18e..58d439a0aab4e3 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/builtin_alert_types/es_query/rule.ts
@@ -25,6 +25,7 @@ import {
RULE_INTERVAL_MILLIS,
RULE_INTERVAL_SECONDS,
RULE_TYPE_ID,
+ SourceField,
} from './common';
import { createDataStream, deleteDataStream } from '../../../create_test_data';
@@ -43,6 +44,12 @@ export default function ruleTests({ getService }: FtrProviderContext) {
getAllAADDocs,
} = getRuleServices(getService);
+ const sourceFields = [
+ { label: 'host.hostname', searchPath: 'host.hostname.keyword' },
+ { label: 'host.id', searchPath: 'host.id' },
+ { label: 'host.name', searchPath: 'host.name' },
+ ];
+
describe('rule', async () => {
let endDate: string;
let connectorId: string;
@@ -82,6 +89,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
size: 100,
thresholdComparator: '<',
threshold: [0],
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -89,6 +97,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
size: 100,
thresholdComparator: '>',
threshold: [-1],
+ sourceFields,
});
},
] as const,
@@ -114,6 +123,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
index: esTestDataView.id,
filter: [],
},
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -129,6 +139,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
index: esTestDataView.id,
filter: [],
},
+ sourceFields,
});
},
] as const,
@@ -173,6 +184,9 @@ export default function ruleTests({ getService }: FtrProviderContext) {
const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
expect(value).greaterThan(0);
expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
})
);
@@ -188,6 +202,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
threshold: [0],
aggType: 'avg',
aggField: 'testedValue',
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -197,6 +212,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
threshold: [-1],
aggType: 'avg',
aggField: 'testedValue',
+ sourceFields,
});
},
] as const,
@@ -224,6 +240,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
},
aggType: 'avg',
aggField: 'testedValue',
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -241,6 +258,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
},
aggType: 'avg',
aggField: 'testedValue',
+ sourceFields,
});
},
] as const,
@@ -250,6 +268,9 @@ export default function ruleTests({ getService }: FtrProviderContext) {
await createEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate);
await initData();
+ const messagePattern =
+ /Document count is \d+.?\d* in the last 20s in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
+
const docs = await waitForDocs(2);
for (let i = 0; i < docs.length; i++) {
const doc = docs[i];
@@ -258,8 +279,6 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(name).to.be('always fire');
expect(title).to.be(`rule 'always fire' matched query`);
- const messagePattern =
- /Document count is \d+.?\d* in the last 20s in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
expect(message).to.match(messagePattern);
expect(hits).not.to.be.empty();
@@ -271,6 +290,21 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(previousTimestamp).not.to.be.empty();
}
}
+
+ const aadDocs = await getAllAADDocs(1);
+
+ const alertDoc = aadDocs.body.hits.hits[0]._source;
+ expect(alertDoc[ALERT_REASON]).to.match(messagePattern);
+ expect(alertDoc['kibana.alert.title']).to.be("rule 'always fire' matched query");
+ expect(alertDoc['kibana.alert.evaluation.conditions']).to.be(
+ 'Number of matching documents where avg of testedValue is greater than -1'
+ );
+ const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
+ expect(value).greaterThan(0);
+ expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
})
);
@@ -287,6 +321,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: 'group',
termSize: 2,
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -297,6 +332,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: 'group',
termSize: 2,
+ sourceFields,
});
},
] as const,
@@ -325,6 +361,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: 'group',
termSize: 2,
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -343,6 +380,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: 'group',
termSize: 2,
+ sourceFields,
});
},
] as const,
@@ -352,6 +390,12 @@ export default function ruleTests({ getService }: FtrProviderContext) {
await createGroupedEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate);
await initData();
+ const messagePattern =
+ /Document count is \d+.?\d* in the last 20s for group-\d+ in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
+ const titlePattern = /rule 'always fire' matched query for group group-\d/;
+ const conditionPattern =
+ /Number of matching documents for group "group-\d" is greater than -1/;
+
const docs = await waitForDocs(2);
for (let i = 0; i < docs.length; i++) {
const doc = docs[i];
@@ -359,15 +403,25 @@ export default function ruleTests({ getService }: FtrProviderContext) {
const { name, title, message } = doc._source.params;
expect(name).to.be('always fire');
- const titlePattern = /rule 'always fire' matched query for group group-\d/;
expect(title).to.match(titlePattern);
- const messagePattern =
- /Document count is \d+.?\d* in the last 20s for group-\d+ in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
expect(message).to.match(messagePattern);
expect(hits).not.to.be.empty();
expect(previousTimestamp).to.be.empty();
}
+
+ const aadDocs = await getAllAADDocs(1);
+
+ const alertDoc = aadDocs.body.hits.hits[0]._source;
+ expect(alertDoc[ALERT_REASON]).to.match(messagePattern);
+ expect(alertDoc['kibana.alert.title']).to.match(titlePattern);
+ expect(alertDoc['kibana.alert.evaluation.conditions']).to.match(conditionPattern);
+ const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
+ expect(value).greaterThan(0);
+ expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
})
);
@@ -384,6 +438,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: ['group', 'testedValue'],
termSize: 2,
+ sourceFields,
});
},
] as const,
@@ -412,6 +467,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy: 'top',
termField: ['group', 'testedValue'],
termSize: 2,
+ sourceFields,
});
},
] as const,
@@ -421,6 +477,12 @@ export default function ruleTests({ getService }: FtrProviderContext) {
await createGroupedEsDocumentsInGroups(ES_GROUPS_TO_WRITE, endDate);
await initData();
+ const messagePattern =
+ /Document count is \d+.?\d* in the last 20s for group-\d+,\d+ in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
+ const titlePattern = /rule 'always fire' matched query for group group-\d+,\d+/;
+ const conditionPattern =
+ /Number of matching documents for group "group-\d+,\d+" is greater than -1/;
+
const docs = await waitForDocs(2);
for (let i = 0; i < docs.length; i++) {
const doc = docs[i];
@@ -428,15 +490,25 @@ export default function ruleTests({ getService }: FtrProviderContext) {
const { name, title, message } = doc._source.params;
expect(name).to.be('always fire');
- const titlePattern = /rule 'always fire' matched query for group group-\d/;
expect(title).to.match(titlePattern);
- const messagePattern =
- /Document count is \d+.?\d* in the last 20s for group-\d+,\d+ in .kibana-alerting-test-data (?:index|data view). Alert when greater than -1./;
expect(message).to.match(messagePattern);
expect(hits).not.to.be.empty();
expect(previousTimestamp).to.be.empty();
}
+
+ const aadDocs = await getAllAADDocs(1);
+
+ const alertDoc = aadDocs.body.hits.hits[0]._source;
+ expect(alertDoc[ALERT_REASON]).to.match(messagePattern);
+ expect(alertDoc['kibana.alert.title']).to.match(titlePattern);
+ expect(alertDoc['kibana.alert.evaluation.conditions']).to.match(conditionPattern);
+ const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
+ expect(value).greaterThan(0);
+ expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
})
);
@@ -904,6 +976,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
threshold: [0],
indexName: ES_TEST_DATA_STREAM_NAME,
timeField: '@timestamp',
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -913,6 +986,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
threshold: [-1],
indexName: ES_TEST_DATA_STREAM_NAME,
timeField: '@timestamp',
+ sourceFields,
});
},
] as const,
@@ -938,6 +1012,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
index: esTestDataView.id,
filter: [],
},
+ sourceFields,
});
await createRule({
name: 'always fire',
@@ -953,6 +1028,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
index: esTestDataView.id,
filter: [],
},
+ sourceFields,
});
},
] as const,
@@ -967,6 +1043,9 @@ export default function ruleTests({ getService }: FtrProviderContext) {
);
await initData();
+ const messagePattern =
+ /Document count is \d+.?\d* in the last 20s in test-data-stream (?:index|data view). Alert when greater than -1./;
+
const docs = await waitForDocs(2);
for (let i = 0; i < docs.length; i++) {
const doc = docs[i];
@@ -975,8 +1054,6 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(name).to.be('always fire');
expect(title).to.be(`rule 'always fire' matched query`);
- const messagePattern =
- /Document count is \d+.?\d* in the last 20s in test-data-stream (?:index|data view). Alert when greater than -1./;
expect(message).to.match(messagePattern);
expect(hits).not.to.be.empty();
@@ -988,6 +1065,21 @@ export default function ruleTests({ getService }: FtrProviderContext) {
expect(previousTimestamp).not.to.be.empty();
}
}
+
+ const aadDocs = await getAllAADDocs(1);
+
+ const alertDoc = aadDocs.body.hits.hits[0]._source;
+ expect(alertDoc[ALERT_REASON]).to.match(messagePattern);
+ expect(alertDoc['kibana.alert.title']).to.be("rule 'always fire' matched query");
+ expect(alertDoc['kibana.alert.evaluation.conditions']).to.be(
+ 'Number of matching documents is greater than -1'
+ );
+ const value = parseInt(alertDoc['kibana.alert.evaluation.value'], 10);
+ expect(value).greaterThan(0);
+ expect(alertDoc[ALERT_URL]).to.contain('/s/space1/app/');
+ expect(alertDoc['host.name']).to.eql(['host-1']);
+ expect(alertDoc['host.hostname']).to.eql(['host-1']);
+ expect(alertDoc['host.id']).to.eql(['1']);
})
);
@@ -1121,6 +1213,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
groupBy?: string;
termField?: string | string[];
termSize?: number;
+ sourceFields?: SourceField[];
}
async function createRule(params: CreateRuleParams): Promise {
@@ -1201,6 +1294,7 @@ export default function ruleTests({ getService }: FtrProviderContext) {
aggField: params.aggField,
termField: params.termField,
termSize: params.termSize,
+ sourceFields: params.sourceFields,
...(params.excludeHitsFromPreviousRun !== undefined && {
excludeHitsFromPreviousRun: params.excludeHitsFromPreviousRun,
}),
diff --git a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts
index dad7845e60e317..fd08e1cae3f3a6 100644
--- a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts
+++ b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts
@@ -12,14 +12,16 @@ import {
ELASTIC_HTTP_VERSION_HEADER,
X_ELASTIC_INTERNAL_ORIGIN_REQUEST,
} from '@kbn/core-http-common';
+import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants';
+import {
+ CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE,
+ DETECTION_RULE_RULES_API_CURRENT_VERSION,
+} from '@kbn/cloud-security-posture-plugin/common/constants';
+import type { CspBenchmarkRule } from '@kbn/cloud-security-posture-plugin/common/types/latest';
+// eslint-disable @kbn/imports/no_boundary_crossing
+import { generateBenchmarkRuleTags } from '@kbn/cloud-security-posture-plugin/common/utils/detection_rules';
import type { FtrProviderContext } from '../ftr_provider_context';
-interface RuleIdentifier {
- benchmarkId: string;
- benchmarkVersion: string;
- ruleNumber: string;
-}
-
// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
const retry = getService('retry');
@@ -27,24 +29,52 @@ export default function ({ getService }: FtrProviderContext) {
const log = getService('log');
const kibanaServer = getService('kibanaServer');
- const generateRuleKey = (ruleParams: RuleIdentifier): string => {
- return `${ruleParams.benchmarkId};${ruleParams.benchmarkVersion};${ruleParams.ruleNumber}`;
+ const generateRuleKey = (rule: CspBenchmarkRule): string => {
+ return `${rule.metadata.benchmark.id};${rule.metadata.benchmark.version};${rule.metadata.benchmark.rule_number}`;
+ };
+
+ const getRandomCspBenchmarkRule = async () => {
+ const cspBenchmarkRules = await kibanaServer.savedObjects.find({
+ type: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE,
+ });
+
+ expect(cspBenchmarkRules.saved_objects.length).greaterThan(0);
+
+ const randomIndex = Math.floor(Math.random() * cspBenchmarkRules.saved_objects.length);
+ return cspBenchmarkRules.saved_objects[randomIndex].attributes;
};
- const generateRandomRule = (): RuleIdentifier => {
- const majorVersionNumber = Math.floor(Math.random() * 10); // Random major number between 0 and 9
- const minorVersionNumber = Math.floor(Math.random() * 10);
- const benchmarksIds = ['cis_aws', 'cis_k8s', 'cis_k8s'];
- const benchmarksVersions = ['v2.0.0', 'v2.0.1', 'v2.0.3', 'v3.0.0'];
- const randomBenchmarkId = benchmarksIds[Math.floor(Math.random() * benchmarksIds.length)];
- const randomBenchmarkVersion =
- benchmarksVersions[Math.floor(Math.random() * benchmarksVersions.length)];
-
- return {
- benchmarkId: randomBenchmarkId,
- benchmarkVersion: randomBenchmarkVersion,
- ruleNumber: `${majorVersionNumber}.${minorVersionNumber}`,
- };
+ const createDetectionRule = async (rule: CspBenchmarkRule) => {
+ await supertest
+ .post(DETECTION_ENGINE_RULES_URL)
+ .set('version', DETECTION_RULE_RULES_API_CURRENT_VERSION)
+ .set('kbn-xsrf', 'xxxx')
+ .send({
+ type: 'query',
+ language: 'kuery',
+ license: 'Elastic',
+ author: ['Elastic License v2'],
+ filters: [],
+ false_positives: [],
+ risk_score: 0,
+ risk_score_mapping: [],
+ severity: 'low',
+ severity_mapping: [],
+ threat: [],
+ interval: '1h',
+ from: 'now-26h',
+ to: 'now',
+ max_signals: 100,
+ timestamp_override: 'event.ingested',
+ timestamp_override_fallback_disabled: false,
+ actions: [],
+ enabled: true,
+ index: ['logs-cloud_security_posture.findings-default*'],
+ query: 'rule.benchmark.rule_number: foo',
+ name: rule.metadata.name,
+ description: rule.metadata.rationale,
+ tags: generateBenchmarkRuleTags(rule.metadata),
+ });
};
/**
@@ -66,15 +96,15 @@ export default function ({ getService }: FtrProviderContext) {
await waitForPluginInitialized();
});
- afterEach(async () => {
+ beforeEach(async () => {
await kibanaServer.savedObjects.clean({
types: ['cloud-security-posture-settings'],
});
});
- it('mute rules successfully', async () => {
- const rule1 = generateRandomRule();
- const rule2 = generateRandomRule();
+ it('mute benchmark rules successfully', async () => {
+ const rule1 = await getRandomCspBenchmarkRule();
+ const rule2 = await getRandomCspBenchmarkRule();
const { body } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
@@ -85,14 +115,10 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
- benchmark_id: rule1.benchmarkId,
- benchmark_version: rule1.benchmarkVersion,
- rule_number: rule1.ruleNumber,
+ rule_id: rule1.metadata.id,
},
{
- benchmark_id: rule2.benchmarkId,
- benchmark_version: rule2.benchmarkVersion,
- rule_number: rule2.ruleNumber,
+ rule_id: rule2.metadata.id,
},
],
})
@@ -100,16 +126,33 @@ export default function ({ getService }: FtrProviderContext) {
expectExpect(body.updated_benchmark_rules).toEqual(
expectExpect.objectContaining({
- [generateRuleKey(rule1)]: { muted: true },
- [generateRuleKey(rule2)]: { muted: true },
+ [generateRuleKey(rule1)]: {
+ muted: true,
+ benchmark_id: rule1.metadata.benchmark.id,
+ benchmark_version: rule1.metadata.benchmark.version,
+ rule_number: rule1.metadata.benchmark.rule_number
+ ? rule1.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule1.metadata.id,
+ },
+ [generateRuleKey(rule2)]: {
+ muted: true,
+ benchmark_id: rule2.metadata.benchmark.id,
+ benchmark_version: rule2.metadata.benchmark.version,
+ rule_number: rule2.metadata.benchmark.rule_number
+ ? rule2.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule2.metadata.id,
+ },
})
);
+ expectExpect(body.detection_rules).toEqual('disabled 0 detections rules.');
});
it('unmute rules successfully', async () => {
- const rule1 = generateRandomRule();
- const rule2 = generateRandomRule();
-
+ const rule1 = await getRandomCspBenchmarkRule();
+ const rule2 = await getRandomCspBenchmarkRule();
+ // getRandomCspBenchmarkRule();
const { body } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
@@ -119,14 +162,10 @@ export default function ({ getService }: FtrProviderContext) {
action: 'unmute',
rules: [
{
- benchmark_id: rule1.benchmarkId,
- benchmark_version: rule1.benchmarkVersion,
- rule_number: rule1.ruleNumber,
+ rule_id: rule1.metadata.id,
},
{
- benchmark_id: rule2.benchmarkId,
- benchmark_version: rule2.benchmarkVersion,
- rule_number: rule2.ruleNumber,
+ rule_id: rule2.metadata.id,
},
],
})
@@ -134,19 +173,35 @@ export default function ({ getService }: FtrProviderContext) {
expectExpect(body.updated_benchmark_rules).toEqual(
expectExpect.objectContaining({
- [generateRuleKey(rule1)]: { muted: false },
- [generateRuleKey(rule2)]: { muted: false },
+ [generateRuleKey(rule1)]: {
+ muted: false,
+ benchmark_id: rule1.metadata.benchmark.id,
+ benchmark_version: rule1.metadata.benchmark.version,
+ rule_number: rule1.metadata.benchmark.rule_number
+ ? rule1.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule1.metadata.id,
+ },
+ [generateRuleKey(rule2)]: {
+ muted: false,
+ benchmark_id: rule2.metadata.benchmark.id,
+ benchmark_version: rule2.metadata.benchmark.version,
+ rule_number: rule2.metadata.benchmark.rule_number
+ ? rule2.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule2.metadata.id,
+ },
})
);
});
it('verify new rules are added and existing rules are set.', async () => {
- const rule1 = generateRandomRule();
- const rule2 = generateRandomRule();
- const rule3 = generateRandomRule();
+ const rule1 = await getRandomCspBenchmarkRule();
+ const rule2 = await getRandomCspBenchmarkRule();
+ const rule3 = await getRandomCspBenchmarkRule();
// unmute rule1 and rule2
- const cspSettingsResponse = await supertest
+ const { body } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
@@ -155,28 +210,40 @@ export default function ({ getService }: FtrProviderContext) {
action: 'unmute',
rules: [
{
- benchmark_id: rule1.benchmarkId,
- benchmark_version: rule1.benchmarkVersion,
- rule_number: rule1.ruleNumber,
+ rule_id: rule1.metadata.id,
},
{
- benchmark_id: rule2.benchmarkId,
- benchmark_version: rule2.benchmarkVersion,
- rule_number: rule2.ruleNumber,
+ rule_id: rule2.metadata.id,
},
],
})
.expect(200);
- expectExpect(cspSettingsResponse.body.updated_benchmark_rules).toEqual(
+ expectExpect(body.updated_benchmark_rules).toEqual(
expectExpect.objectContaining({
- [generateRuleKey(rule1)]: { muted: false },
- [generateRuleKey(rule2)]: { muted: false },
+ [generateRuleKey(rule1)]: {
+ muted: false,
+ benchmark_id: rule1.metadata.benchmark.id,
+ benchmark_version: rule1.metadata.benchmark.version,
+ rule_number: rule1.metadata.benchmark.rule_number
+ ? rule1.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule1.metadata.id,
+ },
+ [generateRuleKey(rule2)]: {
+ muted: false,
+ benchmark_id: rule2.metadata.benchmark.id,
+ benchmark_version: rule2.metadata.benchmark.version,
+ rule_number: rule2.metadata.benchmark.rule_number
+ ? rule2.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule2.metadata.id,
+ },
})
);
// mute rule1 and rule3
- const updatedCspSettingsResponse = await supertest
+ const { body: body2 } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
@@ -185,29 +252,43 @@ export default function ({ getService }: FtrProviderContext) {
action: 'mute',
rules: [
{
- benchmark_id: rule1.benchmarkId,
- benchmark_version: rule1.benchmarkVersion,
- rule_number: rule1.ruleNumber,
+ rule_id: rule1.metadata.id,
},
{
- benchmark_id: rule3.benchmarkId,
- benchmark_version: rule3.benchmarkVersion,
- rule_number: rule3.ruleNumber,
+ rule_id: rule3.metadata.id,
},
],
})
.expect(200);
- expectExpect(updatedCspSettingsResponse.body.updated_benchmark_rules).toEqual(
+ expectExpect(body2.updated_benchmark_rules).toEqual(
expectExpect.objectContaining({
- [generateRuleKey(rule1)]: { muted: true },
- [generateRuleKey(rule3)]: { muted: true },
+ [generateRuleKey(rule1)]: {
+ muted: true,
+ benchmark_id: rule1.metadata.benchmark.id,
+ benchmark_version: rule1.metadata.benchmark.version,
+ rule_number: rule1.metadata.benchmark.rule_number
+ ? rule1.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule1.metadata.id,
+ },
+ [generateRuleKey(rule3)]: {
+ muted: true,
+ benchmark_id: rule3.metadata.benchmark.id,
+ benchmark_version: rule3.metadata.benchmark.version,
+ rule_number: rule3.metadata.benchmark.rule_number
+ ? rule3.metadata.benchmark.rule_number
+ : '',
+ rule_id: rule3.metadata.id,
+ },
})
);
});
- it('set wrong action input', async () => {
- const rule1 = generateRandomRule();
+ it('mute detection rule successfully', async () => {
+ const rule1 = await getRandomCspBenchmarkRule();
+
+ await createDetectionRule(rule1);
const { body } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
@@ -215,21 +296,24 @@ export default function ({ getService }: FtrProviderContext) {
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.send({
- action: 'foo',
+ action: 'mute',
rules: [
{
- benchmark_id: rule1.benchmarkId,
- benchmark_version: rule1.benchmarkVersion,
- rule_number: rule1.ruleNumber,
+ rule_id: rule1.metadata.id,
},
],
- });
+ })
+ .expect(200);
- expect(body.error).to.eql('Bad Request');
- expect(body.statusCode).to.eql(400);
+ expectExpect(body.detection_rules).toEqual('disabled 1 detections rules.');
});
- it('set wrong rule ids input', async () => {
+ it('Expect to two benchmark rules and one detection rule', async () => {
+ const rule1 = await getRandomCspBenchmarkRule();
+ const rule2 = await getRandomCspBenchmarkRule();
+
+ await createDetectionRule(rule1);
+
const { body } = await supertest
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
@@ -237,7 +321,35 @@ export default function ({ getService }: FtrProviderContext) {
.set('kbn-xsrf', 'xxxx')
.send({
action: 'mute',
- rule_ids: ['invalid_rule_structure'],
+ rules: [
+ {
+ rule_id: rule1.metadata.id,
+ },
+ {
+ rule_id: rule2.metadata.id,
+ },
+ ],
+ })
+ .expect(200);
+
+ expectExpect(body.detection_rules).toEqual('disabled 1 detections rules.');
+ });
+
+ it('set wrong action input', async () => {
+ const rule1 = await getRandomCspBenchmarkRule();
+
+ const { body } = await supertest
+ .post(`/internal/cloud_security_posture/rules/_bulk_action`)
+ .set(ELASTIC_HTTP_VERSION_HEADER, '1')
+ .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
+ .set('kbn-xsrf', 'xxxx')
+ .send({
+ action: 'foo',
+ rules: [
+ {
+ rule_id: rule1.metadata.id,
+ },
+ ],
});
expect(body.error).to.eql('Bad Request');
diff --git a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts
index 7f15daf653478f..892d31e4bc04bb 100644
--- a/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts
+++ b/x-pack/test/cloud_security_posture_api/routes/vulnerabilities_dashboard.ts
@@ -19,7 +19,7 @@ export interface CnvmStatistics {
highCount?: number;
mediumCount?: number;
resourcesScanned?: number;
- cloudRegions?: number;
+ cloudAccounts?: number;
}
export interface AccountVulnStats {
@@ -211,7 +211,7 @@ export default function ({ getService }: FtrProviderContext) {
highCount: 1,
mediumCount: 1,
resourcesScanned: 2,
- cloudRegions: 1,
+ cloudAccounts: 1,
},
vulnTrends: [
{
diff --git a/x-pack/test/functional/apps/maps/group1/sample_data.js b/x-pack/test/functional/apps/maps/group1/sample_data.js
index 09b29f5e529c31..377d80a63999bc 100644
--- a/x-pack/test/functional/apps/maps/group1/sample_data.js
+++ b/x-pack/test/functional/apps/maps/group1/sample_data.js
@@ -15,7 +15,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) {
const kibanaServer = getService('kibanaServer');
const security = getService('security');
- // Only update the baseline images from Jenkins session images after comparing them
+ // Only update the baseline images from CI session images after comparing them
// These tests might fail locally because of scaling factors and resolution.
describe('maps loaded from sample data', () => {
diff --git a/x-pack/test/functional/es_archives/asset_criticality/data.json b/x-pack/test/functional/es_archives/asset_criticality/data.json
new file mode 100644
index 00000000000000..dae5bd12006a80
--- /dev/null
+++ b/x-pack/test/functional/es_archives/asset_criticality/data.json
@@ -0,0 +1,143 @@
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "1",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "suricata-zeek-sensor-toronto",
+ "criticality_level": "important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "2",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "host-0",
+ "criticality_level": "very_important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "20",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "zeek-newyork-sha-aa8df15",
+ "criticality_level": "normal",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "21",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "zeek-sensor-amsterdam",
+ "criticality_level": "low",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "22",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "suricata-sensor-london",
+ "criticality_level": "important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "3",
+ "source": {
+ "id_field": "user.name",
+ "id_value": "root",
+ "criticality_level": "very_important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "4",
+ "source": {
+ "id_field": "user.name",
+ "id_value": "User 2",
+ "criticality_level": "important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "5",
+ "source": {
+ "id_field": "host.name",
+ "id_value": "abc",
+ "criticality_level": "normal",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
+
+{
+ "type": "doc",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "id": "6",
+ "source": {
+ "id_field": "user.name",
+ "id_value": "abc",
+ "criticality_level": "not_important",
+ "@timestamp": "2022-08-12T14:45:36.171Z",
+ "updated_at": "2022-08-12T14:45:36.171Z"
+ },
+ "type": "_doc"
+ }
+}
\ No newline at end of file
diff --git a/x-pack/test/functional/es_archives/asset_criticality/mappings.json b/x-pack/test/functional/es_archives/asset_criticality/mappings.json
new file mode 100644
index 00000000000000..94fe5389706b20
--- /dev/null
+++ b/x-pack/test/functional/es_archives/asset_criticality/mappings.json
@@ -0,0 +1,32 @@
+{
+ "type": "index",
+ "value": {
+ "index": ".asset-criticality.asset-criticality-default",
+ "mappings": {
+ "properties": {
+ "id_value": {
+ "type": "keyword"
+ },
+ "id_field": {
+ "type": "keyword"
+ },
+ "criticality_level": {
+ "type": "keyword"
+ },
+ "@timestamp": {
+ "type": "date"
+ },
+ "updated_at": {
+ "type": "date"
+ }
+ }
+ },
+ "settings": {
+ "index": {
+ "auto_expand_replicas": "0-1",
+ "number_of_replicas": "0",
+ "number_of_shards": "1"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/x-pack/test/security_api_integration/tests/api_keys/has_active_key.ts b/x-pack/test/security_api_integration/tests/api_keys/has_active_key.ts
new file mode 100644
index 00000000000000..11b118abb1d67b
--- /dev/null
+++ b/x-pack/test/security_api_integration/tests/api_keys/has_active_key.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+import type { ApiKey } from '@kbn/security-plugin/common/model';
+import type { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService }: FtrProviderContext) {
+ const supertest = getService('supertest');
+
+ const createKey = async () => {
+ const { body: apiKey } = await supertest
+ .post('/api_keys/_grant')
+ .set('kbn-xsrf', 'xxx')
+ .send({ name: 'an-actual-api-key' })
+ .expect(200);
+ expect(apiKey.name).to.eql('an-actual-api-key');
+ return apiKey;
+ };
+
+ const cleanup = async () => {
+ // get existing keys which would affect test results
+ const { body: getResponseBody } = await supertest.get('/internal/security/api_key').expect(200);
+ const apiKeys: ApiKey[] = getResponseBody.apiKeys;
+ const existing = apiKeys.map(({ id, name }) => ({ id, name }));
+
+ // invalidate the keys
+ await supertest
+ .post(`/internal/security/api_key/invalidate`)
+ .set('kbn-xsrf', 'xxx')
+ .send({ apiKeys: existing, isAdmin: false })
+ .expect(200, { itemsInvalidated: existing, errors: [] });
+ };
+
+ describe('Has Active API Keys: _has_active', () => {
+ before(cleanup);
+ after(cleanup);
+
+ it('detects when user has no API Keys', async () => {
+ await supertest
+ .get('/internal/security/api_key/_has_active')
+ .set('kbn-xsrf', 'xxx')
+ .expect(200, { hasApiKeys: false });
+ });
+
+ it('detects when user has some API Keys', async () => {
+ await createKey();
+
+ await supertest
+ .get('/internal/security/api_key/_has_active')
+ .set('kbn-xsrf', 'xxx')
+ .expect(200, { hasApiKeys: true });
+ });
+ });
+}
diff --git a/x-pack/test/security_api_integration/tests/api_keys/index.ts b/x-pack/test/security_api_integration/tests/api_keys/index.ts
index a20f0a30181ff2..a36a76c0c95663 100644
--- a/x-pack/test/security_api_integration/tests/api_keys/index.ts
+++ b/x-pack/test/security_api_integration/tests/api_keys/index.ts
@@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('security APIs - Api Keys', function () {
loadTestFile(require.resolve('./grant_api_key'));
+ loadTestFile(require.resolve('./has_active_key'));
});
}
diff --git a/x-pack/test/security_api_integration/tests/session_concurrent_limit/cleanup.ts b/x-pack/test/security_api_integration/tests/session_concurrent_limit/cleanup.ts
index 45d3bfc5b12eae..32f5351b4e4609 100644
--- a/x-pack/test/security_api_integration/tests/session_concurrent_limit/cleanup.ts
+++ b/x-pack/test/security_api_integration/tests/session_concurrent_limit/cleanup.ts
@@ -150,8 +150,7 @@ export default function ({ getService }: FtrProviderContext) {
});
}
- // Failing: See https://github.com/elastic/kibana/issues/149091
- describe.skip('Session Concurrent Limit cleanup', () => {
+ describe('Session Concurrent Limit cleanup', () => {
before(async () => {
await security.user.create('anonymous_user', {
password: 'changeme',
@@ -181,7 +180,10 @@ export default function ({ getService }: FtrProviderContext) {
await setTimeoutAsync(500);
const basicSessionCookieThree = await loginWithBasic(testUser);
- expect(await getNumberOfSessionDocuments()).to.be(3);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(3);
+ });
// Poke the background task to run
await runCleanupTaskSoon();
@@ -210,7 +212,10 @@ export default function ({ getService }: FtrProviderContext) {
const basicSessionCookieThree = await loginWithBasic(testUser);
const samlSessionCookieThree = await loginWithSAML();
- expect(await getNumberOfSessionDocuments()).to.be(6);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(6);
+ });
// Poke the background task to run
await runCleanupTaskSoon();
@@ -243,7 +248,10 @@ export default function ({ getService }: FtrProviderContext) {
const basicSessionCookieThree = await loginWithBasic(testUser);
const samlSessionCookieThree = await loginWithSAML();
- expect(await getNumberOfSessionDocuments()).to.be(6);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(6);
+ });
// Remove `createdAt` field from the most recent sessions to emulate legacy sessions.
// 1. Get the latest session for every unique credentials.
@@ -305,7 +313,10 @@ export default function ({ getService }: FtrProviderContext) {
await setTimeoutAsync(500);
const basicSessionCookieTwo = await loginWithBasic(testUser);
- expect(await getNumberOfSessionDocuments()).to.be(2);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(2);
+ });
// Poke the background task to run
await runCleanupTaskSoon();
@@ -328,7 +339,10 @@ export default function ({ getService }: FtrProviderContext) {
const anonymousSessionCookieTwo = await loginWithAnonymous();
const anonymousSessionCookieThree = await loginWithAnonymous();
- expect(await getNumberOfSessionDocuments()).to.be(3);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(3);
+ });
// Poke the background task to run
await runCleanupTaskSoon();
@@ -357,7 +371,10 @@ export default function ({ getService }: FtrProviderContext) {
const unauthenticatedSessionTwo = await startSAMLHandshake();
const unauthenticatedSessionThree = await startSAMLHandshake();
- expect(await getNumberOfSessionDocuments()).to.be(3);
+ log.debug('Waiting for all sessions to be persisted...');
+ await retry.tryForTime(20000, async () => {
+ expect(await getNumberOfSessionDocuments()).to.be(3);
+ });
// Poke the background task to run
await runCleanupTaskSoon();
diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts
index b7f08d5180bbe8..89e6df3c68cd25 100644
--- a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts
+++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts
@@ -81,7 +81,7 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s
'previewTelemetryUrlEnabled',
'riskScoringPersistence',
'riskScoringRoutesEnabled',
- 'alertSuppressionForThresholdRuleEnabled',
+ 'entityAnalyticsAssetCriticalityEnabled',
])}`,
'--xpack.task_manager.poll_interval=1000',
`--xpack.actions.preconfigured=${JSON.stringify({
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/serverless.config.ts
index c01c3a74e61cfd..1f43395efcd902 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/serverless.config.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/serverless.config.ts
@@ -17,7 +17,7 @@ export default createTestConfig({
'/testing_regex*/',
])}`, // See tests within the file "ignore_fields.ts" which use these values in "alertIgnoreFields"
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'alertSuppressionForThresholdRuleEnabled',
+ 'entityAnalyticsAssetCriticalityEnabled',
])}`,
],
});
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/eql.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/eql.ts
index db5a924b48a05f..03af11e239c68b 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/eql.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/eql.ts
@@ -41,6 +41,13 @@ import {
import { FtrProviderContext } from '../../../../../ftr_provider_context';
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
+/**
+ * Specific AGENT_ID to use for some of the tests. If the archiver changes and you see errors
+ * here, update this to a new value of a chosen auditbeat record and update the tests values.
+ */
+const AGENT_ID = 'a1d7b39c-f898-4dbe-a761-efb61939302d';
+const specificQueryForTests = `configuration where agent.id=="${AGENT_ID}"`;
+
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
@@ -73,7 +80,7 @@ export default ({ getService }: FtrProviderContext) => {
it('generates a correctly formatted alert from EQL non-sequence queries', async () => {
const rule: EqlRuleCreateProps = {
...getEqlRuleForAlertTesting(['auditbeat-*']),
- query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
+ query: specificQueryForTests,
};
const createdRule = await createRule(supertest, log, rule);
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
@@ -88,7 +95,7 @@ export default ({ getService }: FtrProviderContext) => {
agent: {
ephemeral_id: '0010d67a-14f7-41da-be30-489fea735967',
hostname: 'suricata-zeek-sensor-toronto',
- id: 'a1d7b39c-f898-4dbe-a761-efb61939302d',
+ id: AGENT_ID,
type: 'auditbeat',
version: '8.0.0',
},
@@ -196,7 +203,7 @@ export default ({ getService }: FtrProviderContext) => {
it('uses the provided event_category_override', async () => {
const rule: EqlRuleCreateProps = {
...getEqlRuleForAlertTesting(['auditbeat-*']),
- query: 'config_change where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
+ query: `config_change where agent.id=="${AGENT_ID}"`,
event_category_override: 'auditd.message_type',
};
const { previewId } = await previewRule({ supertest, rule });
@@ -542,7 +549,7 @@ export default ({ getService }: FtrProviderContext) => {
it('generates alerts when an index name contains special characters to encode', async () => {
const rule: EqlRuleCreateProps = {
...getEqlRuleForAlertTesting(['auditbeat-*', '']),
- query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
+ query: specificQueryForTests,
};
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@@ -607,7 +614,7 @@ export default ({ getService }: FtrProviderContext) => {
it('should be enriched with host risk score', async () => {
const rule: EqlRuleCreateProps = {
...getEqlRuleForAlertTesting(['auditbeat-*']),
- query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
+ query: specificQueryForTests,
};
const { previewId } = await previewRule({ supertest, rule });
const previewAlerts = await getPreviewAlerts({ es, previewId });
@@ -620,5 +627,27 @@ export default ({ getService }: FtrProviderContext) => {
expect(fullAlert?.host?.risk?.calculated_score_norm).to.eql(96);
});
});
+
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const rule: EqlRuleCreateProps = {
+ ...getEqlRuleForAlertTesting(['auditbeat-*']),
+ query: specificQueryForTests,
+ };
+
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId });
+ const fullAlert = previewAlerts[0]._source;
+ expect(fullAlert?.['kibana.alert.host.criticality_level']).to.eql('important');
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts
index cb0f31ad254600..caf649896abf3a 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/esql.ts
@@ -859,6 +859,45 @@ export default ({ getService }: FtrProviderContext) => {
});
});
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const id = uuidv4();
+ const interval: [string, string] = ['2020-10-28T06:00:00.000Z', '2020-10-28T06:10:00.000Z'];
+ const doc1 = { host: { name: 'host-0' } };
+
+ await indexEnhancedDocuments({ documents: [doc1], interval, id });
+
+ const rule: EsqlRuleCreateProps = {
+ ...getCreateEsqlRulesSchemaMock('rule-1', true),
+ query: `from ecs_compliant ${internalIdPipe(id)} | where host.name=="host-0"`,
+ from: 'now-1h',
+ interval: '1h',
+ };
+
+ const { previewId } = await previewRule({
+ supertest,
+ rule,
+ timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
+ });
+
+ const previewAlerts = await getPreviewAlerts({ es, previewId });
+
+ expect(previewAlerts.length).toBe(1);
+
+ expect(previewAlerts[0]?._source?.['kibana.alert.host.criticality_level']).toBe(
+ 'very_important'
+ );
+ });
+ });
+
describe('ECS fields validation', () => {
it('creates alert if ECS field has multifields', async () => {
const id = uuidv4();
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts
index d58227377f116d..8787a51871125a 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/machine_learning.ts
@@ -273,5 +273,25 @@ export default ({ getService }: FtrProviderContext) => {
expect(fullAlert?.host?.risk?.calculated_score_norm).toBe(1);
});
});
+
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId });
+ expect(previewAlerts.length).toBe(1);
+ const fullAlert = previewAlerts[0]._source;
+
+ expect(fullAlert?.['kibana.alert.host.criticality_level']).toBe('normal');
+ expect(fullAlert?.['kibana.alert.user.criticality_level']).toBe('very_important');
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts
index 8a47aeaa89bdcd..9aea83afb95d00 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/new_terms.ts
@@ -1040,5 +1040,31 @@ export default ({ getService }: FtrProviderContext) => {
expect(previewAlerts[0]?._source?.host?.risk?.calculated_score_norm).to.eql(23);
});
});
+
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const rule: NewTermsRuleCreateProps = {
+ ...getCreateNewTermsRulesSchemaMock('rule-1', true),
+ new_terms_fields: ['host.name'],
+ from: '2019-02-19T20:42:00.000Z',
+ history_window_start: '2019-01-19T20:42:00.000Z',
+ };
+
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId });
+ const fullAlert = previewAlerts[0]._source;
+
+ expect(fullAlert?.['kibana.alert.host.criticality_level']).to.eql('normal');
+ expect(fullAlert?.['kibana.alert.user.criticality_level']).to.eql('very_important');
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts
index 19c02fe389fe4c..38930bafa564ea 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/query.ts
@@ -280,6 +280,31 @@ export default ({ getService }: FtrProviderContext) => {
});
});
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const rule: QueryRuleCreateProps = {
+ ...getRuleForAlertTesting(['auditbeat-*']),
+ query: `_id:${ID}`,
+ };
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId });
+ expect(previewAlerts[0]?._source?.['kibana.alert.host.criticality_level']).to.eql(
+ 'important'
+ );
+ expect(previewAlerts[0]?._source?.['kibana.alert.user.criticality_level']).to.eql(
+ 'very_important'
+ );
+ });
+ });
+
/**
* Here we test the functionality of Severity and Risk Score overrides (also called "mappings"
* in the code). If the rule specifies a mapping, then the final Severity or Risk Score
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threat_match.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threat_match.ts
index 9b6c525b5e3516..734583d009ca38 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threat_match.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threat_match.ts
@@ -1623,5 +1623,49 @@ export default ({ getService }: FtrProviderContext) => {
expect(fullAlert?.host?.risk?.calculated_score_norm).to.eql(70);
});
});
+
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
+ query: '*:*',
+ threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
+ threat_mapping: [
+ // We match host.name against host.name
+ {
+ entries: [
+ {
+ field: 'host.name',
+ value: 'host.name',
+ type: 'mapping',
+ },
+ ],
+ },
+ ],
+ });
+
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId, size: 100 });
+ expect(previewAlerts.length).equal(88);
+ const fullSource = previewAlerts.find(
+ (alert) =>
+ (alert._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
+ );
+ const fullAlert = fullSource?._source;
+ if (!fullAlert) {
+ return expect(fullAlert).to.be.ok();
+ }
+
+ expect(fullAlert?.['kibana.alert.host.criticality_level']).to.eql('low');
+ expect(fullAlert?.['kibana.alert.user.criticality_level']).to.eql('very_important');
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threshold.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threshold.ts
index 5edee29c02dc6d..dce4886bc1ba57 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threshold.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/execution_logic/threshold.ts
@@ -430,5 +430,30 @@ export default ({ getService }: FtrProviderContext) => {
expect(previewAlerts[1]?._source?.host?.risk?.calculated_score_norm).toEqual(96);
});
});
+
+ describe('with asset criticality', async () => {
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/asset_criticality');
+ });
+
+ it('should be enriched alert with criticality_level', async () => {
+ const rule: ThresholdRuleCreateProps = {
+ ...getThresholdRuleForAlertTesting(['auditbeat-*']),
+ threshold: {
+ field: 'host.name',
+ value: 100,
+ },
+ };
+ const { previewId } = await previewRule({ supertest, rule });
+ const previewAlerts = await getPreviewAlerts({ es, previewId, sort: ['host.name'] });
+ const fullAlert = previewAlerts[0]?._source;
+
+ expect(fullAlert?.['kibana.alert.host.criticality_level']).toEqual('important');
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts
index 4fe61b660f1a4f..fb34362f7fb9b5 100644
--- a/x-pack/test/security_solution_cypress/config.ts
+++ b/x-pack/test/security_solution_cypress/config.ts
@@ -46,7 +46,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true',
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'chartEmbeddablesEnabled',
- 'alertSuppressionForThresholdRuleEnabled',
])}`,
// mock cloud to enable the guided onboarding tour in e2e tests
'--xpack.cloud.id=test',
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts
index fa925131892e15..3a09073b71f388 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule.cy.ts
@@ -72,15 +72,6 @@ describe(
'Threshold rules',
{
tags: ['@ess', '@serverless'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'alertSuppressionForThresholdRuleEnabled',
- ])}`,
- ],
- },
- },
},
() => {
const rule = getNewThresholdRule();
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule_serverless_essentials.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule_serverless_essentials.cy.ts
index 5b8ec27e2c8910..c26954ea7e37b3 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule_serverless_essentials.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/threshold_rule_serverless_essentials.cy.ts
@@ -23,11 +23,6 @@ describe(
{ product_line: 'security', product_tier: 'essentials' },
{ product_line: 'endpoint', product_tier: 'essentials' },
],
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'alertSuppressionForThresholdRuleEnabled',
- ])}`,
- ],
},
},
},
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/threshold_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/threshold_rule.cy.ts
index 5eed825f99ada0..8d4bdf2d349766 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/threshold_rule.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_edit/threshold_rule.cy.ts
@@ -41,15 +41,6 @@ describe(
'Detection threshold rules, edit',
{
tags: ['@ess', '@serverless'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'alertSuppressionForThresholdRuleEnabled',
- ])}`,
- ],
- },
- },
},
() => {
describe('without suppression', () => {
diff --git a/x-pack/test/security_solution_cypress/serverless_config.ts b/x-pack/test/security_solution_cypress/serverless_config.ts
index 8eb8d2efdefdc9..d0ee1613f6e4cc 100644
--- a/x-pack/test/security_solution_cypress/serverless_config.ts
+++ b/x-pack/test/security_solution_cypress/serverless_config.ts
@@ -34,9 +34,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
{ product_line: 'endpoint', product_tier: 'complete' },
{ product_line: 'cloud', product_tier: 'complete' },
])}`,
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'alertSuppressionForThresholdRuleEnabled',
- ])}`,
],
},
testRunner: SecuritySolutionConfigurableCypressTestRunner,
diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts
new file mode 100644
index 00000000000000..696c48183624a4
--- /dev/null
+++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_response_actions/agent_type_support.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { ISOLATE_HOST_ROUTE_V2 } from '@kbn/security-solution-plugin/common/endpoint/constants';
+import { FtrProviderContext } from '../../ftr_provider_context';
+import { targetTags } from '../../../security_solution_endpoint/target_tags';
+
+export default function ({ getService }: FtrProviderContext) {
+ const supertest = getService('supertest');
+
+ describe('Response Actions support for agentType', function () {
+ targetTags(this, ['@ess', '@serverless']);
+
+ describe('and the "responseActionsSentinelOneV1Enabled" feature flag is disabled', () => {
+ // When feature flag is enabled, this entire `describe()` block should be removed
+ it('should return an error', async () => {
+ await supertest
+ .post(ISOLATE_HOST_ROUTE_V2)
+ .set('kbn-xsrf', 'true')
+ .set('Elastic-Api-Version', '2023-10-31')
+ .send({ endpoint_ids: ['test'], agent_type: 'endpoint' })
+ .expect(400, {
+ statusCode: 400,
+ error: 'Bad Request',
+ message: '[request body.agent_type]: feature is disabled',
+ });
+ });
+ });
+ });
+}
diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
index c0668612907a76..b4f4bb4b46063a 100644
--- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
+++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts
@@ -57,6 +57,7 @@ export default function endpointAPIIntegrationTests(providerContext: FtrProvider
loadTestFile(require.resolve('./package'));
loadTestFile(require.resolve('./endpoint_authz'));
loadTestFile(require.resolve('./endpoint_response_actions/execute'));
+ loadTestFile(require.resolve('./endpoint_response_actions/agent_type_support'));
loadTestFile(require.resolve('./endpoint_artifacts/trusted_apps'));
loadTestFile(require.resolve('./endpoint_artifacts/event_filters'));
loadTestFile(require.resolve('./endpoint_artifacts/host_isolation_exceptions'));
diff --git a/yarn.lock b/yarn.lock
index e7bf86c9eb972c..bf07d32acc0c74 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1321,11 +1321,6 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
-"@bufbuild/protobuf@^1.0.0":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.2.1.tgz#f8b1fbbe79726a4eafa9772ddde147b57f85d177"
- integrity sha512-cwwGvLGqvoaOZmoP5+i4v/rbW+rHkguvTehuZyM2p/xpmaNSdT2h3B7kHw33aiffv35t1XrYHIkdJSEkSEMJuA==
-
"@cbor-extract/cbor-extract-darwin-arm64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.0.0.tgz#cf0667e4c22111c9d45e16c29964892b12460a76"
@@ -1549,14 +1544,14 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@61.0.3":
- version "61.0.3"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-61.0.3.tgz#77a2e1f18a39dd4b421a91edfd30f19cde740594"
- integrity sha512-TY7hUieULTchNFgvpi6Rt7wMxrYMCmuZ4bbS6szOGBIY4WKJvZCgMfgZ2kUdC5MVG/jEzd8Qu+Xixce7GDpRxw==
+"@elastic/charts@61.2.0":
+ version "61.2.0"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-61.2.0.tgz#fa065b85324d5660e4b6355390cca8699ef0d6ff"
+ integrity sha512-LBmZ+6wSR9/BCR+go5eIBy51Jpxr0cbK/a7vslZNYIGCdpHsWxGZgcqG3KgshFxOmUvcP7kj9LEEmeCTEGhbUQ==
dependencies:
"@popperjs/core" "^2.11.8"
bezier-easing "^2.1.0"
- chroma-js "^2.1.0"
+ chroma-js "^2.4.2"
classnames "^2.2.6"
d3-array "^1.2.4"
d3-cloud "^1.2.5"
@@ -2248,7 +2243,7 @@
pngjs "7.0.0"
sharp "0.32.1"
-"@gar/promisify@^1.0.1":
+"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
@@ -6791,6 +6786,14 @@
"@gar/promisify" "^1.0.1"
semver "^7.3.5"
+"@npmcli/fs@^2.1.0":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865"
+ integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==
+ dependencies:
+ "@gar/promisify" "^1.1.3"
+ semver "^7.3.5"
+
"@npmcli/fs@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e"
@@ -6805,6 +6808,14 @@
dependencies:
mkdirp "^1.0.4"
+"@npmcli/move-file@^2.0.0":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4"
+ integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==
+ dependencies:
+ mkdirp "^1.0.4"
+ rimraf "^3.0.2"
+
"@octokit/auth-token@^2.4.0":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56"
@@ -8542,6 +8553,11 @@
dependencies:
"@babel/runtime" "^7.12.5"
+"@tootallnate/once@1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
+ integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
+
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
@@ -10873,7 +10889,7 @@ after-all-results@^2.0.0:
resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0"
integrity sha1-asL8ICtQD4jaj09VMM+hAPTGotA=
-agent-base@6:
+agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
@@ -11234,6 +11250,14 @@ are-we-there-yet@^2.0.0:
delegates "^1.0.0"
readable-stream "^3.6.0"
+are-we-there-yet@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d"
+ integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^3.6.0"
+
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@@ -11535,6 +11559,11 @@ async-cache@^1.1.0:
dependencies:
lru-cache "^4.0.0"
+async-foreach@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
+ integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
+
async-value-promise@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378"
@@ -12374,11 +12403,6 @@ btoa-lite@^1.0.0:
resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337"
integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc=
-buffer-builder@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/buffer-builder/-/buffer-builder-0.2.0.tgz#3322cd307d8296dab1f604618593b261a3fade8f"
- integrity sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==
-
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@@ -12505,7 +12529,7 @@ cacache@^12.0.2:
unique-filename "^1.1.1"
y18n "^4.0.0"
-cacache@^15.0.4, cacache@^15.0.5:
+cacache@^15.0.4, cacache@^15.0.5, cacache@^15.2.0:
version "15.3.0"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb"
integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==
@@ -12529,6 +12553,30 @@ cacache@^15.0.4, cacache@^15.0.5:
tar "^6.0.2"
unique-filename "^1.1.1"
+cacache@^16.1.0:
+ version "16.1.3"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e"
+ integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==
+ dependencies:
+ "@npmcli/fs" "^2.1.0"
+ "@npmcli/move-file" "^2.0.0"
+ chownr "^2.0.0"
+ fs-minipass "^2.1.0"
+ glob "^8.0.1"
+ infer-owner "^1.0.4"
+ lru-cache "^7.7.1"
+ minipass "^3.1.6"
+ minipass-collect "^1.0.2"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ mkdirp "^1.0.4"
+ p-map "^4.0.0"
+ promise-inflight "^1.0.1"
+ rimraf "^3.0.2"
+ ssri "^9.0.0"
+ tar "^6.1.11"
+ unique-filename "^2.0.0"
+
cacache@^18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.0.tgz#17a9ecd6e1be2564ebe6cdca5f7cfed2bfeb6ddc"
@@ -12948,9 +12996,9 @@ ci-info@^2.0.0:
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
ci-info@^3.2.0:
- version "3.8.0"
- resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91"
- integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32"
+ integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
@@ -14461,7 +14509,7 @@ debug@3.X, debug@^3.0.0, debug@^3.1.0, debug@^3.2.7:
dependencies:
ms "^2.1.1"
-debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
+debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -15485,7 +15533,7 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
-encoding@^0.1.13:
+encoding@^0.1.12, encoding@^0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
@@ -17263,7 +17311,7 @@ fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
-fs-minipass@^2.0.0:
+fs-minipass@^2.0.0, fs-minipass@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
@@ -17365,6 +17413,28 @@ gauge@^3.0.0:
strip-ansi "^6.0.1"
wide-align "^1.1.2"
+gauge@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.2.tgz#c3777652f542b6ef62797246e8c7caddecb32cc7"
+ integrity sha512-aSPRm2CvA9R8QyU5eXMFPd+cYkyxLsXHd2l5/FOH2V/eml//M04G6KZOmTap07O1PvEwNcl2NndyLfK8g3QrKA==
+ dependencies:
+ ansi-regex "^5.0.1"
+ aproba "^1.0.3 || ^2.0.0"
+ color-support "^1.1.3"
+ console-control-strings "^1.1.0"
+ has-unicode "^2.0.1"
+ signal-exit "^3.0.7"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+ wide-align "^1.1.5"
+
+gaze@^1.0.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
+ integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
+ dependencies:
+ globule "^1.0.0"
+
geckodriver@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.3.0.tgz#8586e80ddd23e5d5cd47382d9f6897051ca12ea3"
@@ -17618,7 +17688,7 @@ glob@7.2.0:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@8.1.0, glob@^8.0.3:
+glob@8.1.0, glob@^8.0.1, glob@^8.0.3:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
@@ -17652,6 +17722,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@~7.1.1:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
global-dirs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
@@ -17784,6 +17866,15 @@ globjoin@^0.1.4:
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
+globule@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
+ integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==
+ dependencies:
+ glob "~7.1.1"
+ lodash "~4.17.10"
+ minimatch "~3.0.2"
+
gonzales-pe@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
@@ -18441,7 +18532,7 @@ htmlparser2@^8.0.1:
domutils "^3.0.1"
entities "^4.4.0"
-http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1:
+http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
@@ -18489,6 +18580,15 @@ http-parser-js@>=0.5.1:
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
+http-proxy-agent@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
+ integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
+ dependencies:
+ "@tootallnate/once" "1"
+ agent-base "6"
+ debug "4"
+
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
@@ -18548,7 +18648,7 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
-https-proxy-agent@^5.0.1:
+https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
@@ -18659,11 +18759,6 @@ immer@^9.0.15, immer@^9.0.7:
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==
-immutable@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
- integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
-
import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@@ -20319,6 +20414,11 @@ jquery@^3.5.0:
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
+js-base64@^2.4.9:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"
+ integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==
+
js-beautify@1.10.3:
version "1.10.3"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.3.tgz#c73fa10cf69d3dfa52d8ed624f23c64c0a6a94c1"
@@ -21253,7 +21353,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@>4.17.4, lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21:
+lodash@>4.17.4, lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -21422,7 +21522,7 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
-lru-cache@^7.14.1:
+lru-cache@^7.14.1, lru-cache@^7.7.1:
version "7.18.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
@@ -21481,6 +21581,28 @@ make-error@^1.1.1:
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+make-fetch-happen@^10.0.4:
+ version "10.2.1"
+ resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164"
+ integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==
+ dependencies:
+ agentkeepalive "^4.2.1"
+ cacache "^16.1.0"
+ http-cache-semantics "^4.1.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.0"
+ is-lambda "^1.0.1"
+ lru-cache "^7.7.1"
+ minipass "^3.1.6"
+ minipass-collect "^1.0.2"
+ minipass-fetch "^2.0.3"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ negotiator "^0.6.3"
+ promise-retry "^2.0.1"
+ socks-proxy-agent "^7.0.0"
+ ssri "^9.0.0"
+
make-fetch-happen@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz#705d6f6cbd7faecb8eac2432f551e49475bfedf0"
@@ -21498,6 +21620,28 @@ make-fetch-happen@^13.0.0:
promise-retry "^2.0.1"
ssri "^10.0.0"
+make-fetch-happen@^9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968"
+ integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==
+ dependencies:
+ agentkeepalive "^4.1.3"
+ cacache "^15.2.0"
+ http-cache-semantics "^4.1.0"
+ http-proxy-agent "^4.0.1"
+ https-proxy-agent "^5.0.0"
+ is-lambda "^1.0.1"
+ lru-cache "^6.0.0"
+ minipass "^3.1.3"
+ minipass-collect "^1.0.2"
+ minipass-fetch "^1.3.2"
+ minipass-flush "^1.0.5"
+ minipass-pipeline "^1.2.4"
+ negotiator "^0.6.2"
+ promise-retry "^2.0.1"
+ socks-proxy-agent "^6.0.0"
+ ssri "^8.0.0"
+
makeerror@1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@@ -22104,7 +22248,7 @@ minimatch@5.0.1:
dependencies:
brace-expansion "^2.0.1"
-minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@~3.0.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -22146,6 +22290,28 @@ minipass-collect@^1.0.2:
dependencies:
minipass "^3.0.0"
+minipass-fetch@^1.3.2:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.4.tgz#63f5af868a38746ca7b33b03393ddf8c291244fe"
+ integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ==
+ dependencies:
+ minipass "^3.1.0"
+ minipass-sized "^1.0.3"
+ minizlib "^2.0.0"
+ optionalDependencies:
+ encoding "^0.1.12"
+
+minipass-fetch@^2.0.3:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add"
+ integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==
+ dependencies:
+ minipass "^3.1.6"
+ minipass-sized "^1.0.3"
+ minizlib "^2.1.2"
+ optionalDependencies:
+ encoding "^0.1.13"
+
minipass-fetch@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce"
@@ -22178,7 +22344,7 @@ minipass-sized@^1.0.3:
dependencies:
minipass "^3.0.0"
-minipass@^3.0.0, minipass@^3.1.1:
+minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
@@ -22202,7 +22368,7 @@ minipass@^5.0.0:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
-minizlib@^2.1.1, minizlib@^2.1.2:
+minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
@@ -22577,7 +22743,7 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
-nan@^2.18.0:
+nan@^2.17.0, nan@^2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554"
integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==
@@ -22658,7 +22824,7 @@ nearley@^2.7.10:
randexp "0.4.6"
semver "^5.4.1"
-negotiator@0.6.3, negotiator@^0.6.3:
+negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
@@ -22825,6 +22991,22 @@ node-gyp@^10.0.1:
tar "^6.1.2"
which "^4.0.0"
+node-gyp@^8.4.1:
+ version "8.4.1"
+ resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
+ integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==
+ dependencies:
+ env-paths "^2.2.0"
+ glob "^7.1.4"
+ graceful-fs "^4.2.6"
+ make-fetch-happen "^9.1.0"
+ nopt "^5.0.0"
+ npmlog "^6.0.0"
+ rimraf "^3.0.2"
+ semver "^7.3.5"
+ tar "^6.1.2"
+ which "^2.0.2"
+
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -22886,6 +23068,26 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
+node-sass@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-8.0.0.tgz#c80d52148db0ce88610bcf1e1d112027393c13e1"
+ integrity sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A==
+ dependencies:
+ async-foreach "^0.1.3"
+ chalk "^4.1.2"
+ cross-spawn "^7.0.3"
+ gaze "^1.0.0"
+ get-stdin "^4.0.1"
+ glob "^7.0.3"
+ lodash "^4.17.15"
+ make-fetch-happen "^10.0.4"
+ meow "^9.0.0"
+ nan "^2.17.0"
+ node-gyp "^8.4.1"
+ sass-graph "^4.0.1"
+ stdout-stream "^1.4.0"
+ "true-case-path" "^2.2.1"
+
node-source-walk@^6.0.0, node-source-walk@^6.0.1, node-source-walk@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-6.0.2.tgz#ba81bc4bc0f6f05559b084bea10be84c3f87f211"
@@ -22906,6 +23108,13 @@ nopt@^4.0.1, nopt@~4.0.1:
abbrev "1"
osenv "^0.1.4"
+nopt@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
+ integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
+ dependencies:
+ abbrev "1"
+
nopt@^7.0.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7"
@@ -22996,6 +23205,16 @@ npmlog@^5.0.1:
gauge "^3.0.0"
set-blocking "^2.0.0"
+npmlog@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17"
+ integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==
+ dependencies:
+ are-we-there-yet "^3.0.0"
+ console-control-strings "^1.1.0"
+ gauge "^4.0.0"
+ set-blocking "^2.0.0"
+
nth-check@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
@@ -23275,9 +23494,9 @@ onetime@^5.1.0, onetime@^5.1.2:
mimic-fn "^2.1.0"
open@^7.0.3:
- version "7.4.2"
- resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
- integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-7.1.0.tgz#68865f7d3cb238520fa1225a63cf28bcf8368a1c"
+ integrity sha512-lLPI5KgOwEYCDKXf4np7y1PBEkj7HYIyP2DY8mVDRnx0VIIu6bNrRB0R66TuO7Mack6EnTNLm4uvcl1UoklTpA==
dependencies:
is-docker "^2.0.0"
is-wsl "^2.1.1"
@@ -26680,10 +26899,10 @@ rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.6.0, rxjs@^6.6.7:
dependencies:
tslib "^1.9.0"
-rxjs@^7.0.0, rxjs@^7.4.0, rxjs@^7.5.5:
- version "7.8.0"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4"
- integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==
+rxjs@^7.0.0, rxjs@^7.5.5:
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f"
+ integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
dependencies:
tslib "^2.1.0"
@@ -26765,71 +26984,20 @@ sane@^4.0.3:
minimist "^1.1.1"
walker "~1.0.5"
-sass-embedded-darwin-arm64@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.69.5.tgz#69e246d4a6875184a593906dfd7b84a21eb6eeb2"
- integrity sha512-zVuXJzgT54t24E4QPP/iteHsw/cawZE8gAXGEm20cP2DKsIQBF7bvSTk0zzY0bS01YFtJviYM13HcGUe4q7/7w==
-
-sass-embedded-darwin-x64@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.69.5.tgz#82c2a659dafa93b54d2690f08f7aac9b2447c43f"
- integrity sha512-HcA9YER3Ax7lMnHouxnIY462gnst5lNL56QXkZaTQmg9nH7oqR2bMfWbckEQL+mHIXGSM/QfX0aD59VOm5iKZw==
-
-sass-embedded-linux-arm64@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.69.5.tgz#8585bbcc6996ba04d8aa4216a2bf65de2e6592ea"
- integrity sha512-HWCjdFSLGh0dMUNLNh+slc2j9koSZnfTEO9qQR6WEIuC+We6vYKJugGPo1V9pFbBeoW6VAJGYdlqsRPquteCZw==
-
-sass-embedded-linux-arm@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.69.5.tgz#ded653fe37d6b07d778c5f414cba5f28107dc438"
- integrity sha512-m0NxVkrfcS3UsF33q0FgItMWIz/F1FZdfVZpjp+dP6qd0KLeTuoPUCh2GSize0DAU5T0Zj24b2mXeowDKv463g==
-
-sass-embedded-linux-ia32@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.69.5.tgz#61ad8b3b43be563f6fcd6ff04d0e121b195ecc2a"
- integrity sha512-0taR6AJDb+eLOBTEMc1nfX2fI1hgRF9M+Hmv+wwGrxfBu/MkASk6fmR9B8MDw9hPHIqGVUkTVizjOh50O7nIKg==
-
-sass-embedded-linux-x64@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.69.5.tgz#3d490f520200d596b2b6072d6d3f4460b7114241"
- integrity sha512-gN9yLTbKC0hUHukx4mdRs4V39WD719PM2GhWQBUA+3Z8qr9ywywi7LiU2atWrKESRF34V+eqF9lYiYVQxtTHZw==
-
-sass-embedded-win32-ia32@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.69.5.tgz#e84a053d25aec1176fac485bee65a384f39dfa9e"
- integrity sha512-9OgSaufHP53b33gaH1Y5NZ/Im3druCHIQvLUEqJBCFuOzly47g/hZGrO+dBDiWgYGYKbSYI7Z4/PBtQoK5Vkxg==
-
-sass-embedded-win32-x64@1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.69.5.tgz#8d803e99cccc0e8105dde50e49a2fd7839e360ec"
- integrity sha512-p1PsOJnpwXdPfiRbX6QdRa4PnL2QXPpIRy8fkeAZpQFYZ278ZIlwemC2MukKMVLcE3iQ5lBulbC8IYm91rod6Q==
-
-sass-embedded@^1.69.5:
- version "1.69.5"
- resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.69.5.tgz#ae217d4b17b0fb07e5ed146917c9c9de0c4383c6"
- integrity sha512-0YNcRcbSpgGd4AnE+mm3a3g4S97puFLIZ0cYJgbwdD4iGz/hiOzE+yh72XS+u1LMhE+pQfNeC9MNnRsc8n1yRg==
- dependencies:
- "@bufbuild/protobuf" "^1.0.0"
- buffer-builder "^0.2.0"
- immutable "^4.0.0"
- rxjs "^7.4.0"
- supports-color "^8.1.1"
- varint "^6.0.0"
- optionalDependencies:
- sass-embedded-darwin-arm64 "1.69.5"
- sass-embedded-darwin-x64 "1.69.5"
- sass-embedded-linux-arm "1.69.5"
- sass-embedded-linux-arm64 "1.69.5"
- sass-embedded-linux-ia32 "1.69.5"
- sass-embedded-linux-x64 "1.69.5"
- sass-embedded-win32-ia32 "1.69.5"
- sass-embedded-win32-x64 "1.69.5"
-
-sass-loader@^10.5.0:
- version "10.5.0"
- resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.5.0.tgz#011c92ea529029e296aea37508e034b94f7dd2dc"
- integrity sha512-VsU71W7VR6SChMJZUqtrfLeMSA8ns7QTHbnA7cfevtjb3c392mX93lr0Dmr4uU1ch5uIbEmfmHjdrDYcXXkQ7w==
+sass-graph@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-4.0.1.tgz#2ff8ca477224d694055bf4093f414cf6cfad1d2e"
+ integrity sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==
+ dependencies:
+ glob "^7.0.0"
+ lodash "^4.17.11"
+ scss-tokenizer "^0.4.3"
+ yargs "^17.2.1"
+
+sass-loader@^10.4.1:
+ version "10.4.1"
+ resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.4.1.tgz#bea4e173ddf512c9d7f53e9ec686186146807cbf"
+ integrity sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==
dependencies:
klona "^2.0.4"
loader-utils "^2.0.0"
@@ -26927,6 +27095,14 @@ screenfull@^5.0.0:
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6"
integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA==
+scss-tokenizer@^0.4.3:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz#1058400ee7d814d71049c29923d2b25e61dc026c"
+ integrity sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==
+ dependencies:
+ js-base64 "^2.4.9"
+ source-map "^0.7.3"
+
secure-json-parse@^2.4.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.6.0.tgz#95d89f84adf32d76ff7800e68a673b129fe918b0"
@@ -27427,6 +27603,24 @@ sockjs@^0.3.24:
uuid "^8.3.2"
websocket-driver "^0.7.4"
+socks-proxy-agent@^6.0.0:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
+ integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
+ dependencies:
+ agent-base "^6.0.2"
+ debug "^4.3.1"
+ socks "^2.6.1"
+
+socks-proxy-agent@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6"
+ integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==
+ dependencies:
+ agent-base "^6.0.2"
+ debug "^4.3.3"
+ socks "^2.6.2"
+
socks-proxy-agent@^8.0.1, socks-proxy-agent@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad"
@@ -27436,7 +27630,7 @@ socks-proxy-agent@^8.0.1, socks-proxy-agent@^8.0.2:
debug "^4.3.4"
socks "^2.7.1"
-socks@^2.7.1:
+socks@^2.6.1, socks@^2.6.2, socks@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
@@ -27770,13 +27964,20 @@ ssri@^6.0.1:
dependencies:
figgy-pudding "^3.5.1"
-ssri@^8.0.1:
+ssri@^8.0.0, ssri@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
dependencies:
minipass "^3.1.1"
+ssri@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057"
+ integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==
+ dependencies:
+ minipass "^3.1.1"
+
stable@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
@@ -27885,6 +28086,13 @@ stats-lite@^2.2.0:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+stdout-stream@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
+ integrity sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=
+ dependencies:
+ readable-stream "^2.0.1"
+
store2@^2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/store2/-/store2-2.12.0.tgz#e1f1b7e1a59b6083b2596a8d067f6ee88fd4d3cf"
@@ -29014,6 +29222,11 @@ trough@^1.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.1.tgz#a9fd8b0394b0ae8fff82e0633a0a36ccad5b5f86"
integrity sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=
+"true-case-path@^2.2.1":
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf"
+ integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==
+
ts-algebra@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-1.2.0.tgz#f91c481207a770f0d14d055c376cbee040afdfc9"
@@ -29458,6 +29671,13 @@ unique-filename@^1.1.1:
dependencies:
unique-slug "^2.0.0"
+unique-filename@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2"
+ integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==
+ dependencies:
+ unique-slug "^3.0.0"
+
unique-filename@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea"
@@ -29472,6 +29692,13 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
+unique-slug@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9"
+ integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==
+ dependencies:
+ imurmurhash "^0.1.4"
+
unique-slug@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3"
@@ -29912,11 +30139,6 @@ variable-diff@1.1.0:
chalk "^1.1.1"
object-assign "^4.0.1"
-varint@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0"
- integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==
-
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -30825,7 +31047,7 @@ which@^1.2.9, which@^1.3.1:
dependencies:
isexe "^2.0.0"
-which@^2.0.1:
+which@^2.0.1, which@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
@@ -30839,7 +31061,7 @@ which@^4.0.0:
dependencies:
isexe "^3.1.1"
-wide-align@^1.1.2:
+wide-align@^1.1.2, wide-align@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==