= ({
[alertViewSelection, setAlertViewSelection]
);
const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled');
+
return (
{alertViewSelection === 'trend' && (
@@ -151,21 +183,22 @@ const ChartPanelsComponent: React.FC
= ({
chartOptionsContextMenu={chartOptionsContextMenu}
comboboxRef={stackByField0ComboboxRef}
defaultStackByOption={trendChartStackBy}
+ extraActions={resetGroupByFieldAction}
filters={alertsHistogramDefaultFilters}
inspectTitle={i18n.TREND}
- setComboboxInputRef={setStackByField0ComboboxInputRef}
onFieldSelected={updateCommonStackBy0}
panelHeight={TREND_CHART_PANEL_HEIGHT}
query={query}
+ runtimeMappings={runtimeMappings}
+ setComboboxInputRef={setStackByField0ComboboxInputRef}
showCountsInLegend={true}
- showGroupByPlaceholder={true}
+ showGroupByPlaceholder={false}
showTotalAlertsCount={false}
+ signalIndexName={signalIndexName}
stackByLabel={GROUP_BY_LABEL}
title={title}
titleSize={'s'}
- signalIndexName={signalIndexName}
updateDateRange={updateDateRangeCallback}
- runtimeMappings={runtimeMappings}
/>
)}
@@ -179,6 +212,7 @@ const ChartPanelsComponent: React.FC = ({
= ({
runtimeMappings={runtimeMappings}
setStackByField0={updateCommonStackBy0}
setStackByField0ComboboxInputRef={setStackByField0ComboboxInputRef}
- stackByField0ComboboxRef={stackByField0ComboboxRef}
setStackByField1={updateCommonStackBy1}
setStackByField1ComboboxInputRef={setStackByField1ComboboxInputRef}
- stackByField1ComboboxRef={stackByField1ComboboxRef}
signalIndexName={signalIndexName}
stackByField0={countTableStackBy0}
+ stackByField0ComboboxRef={stackByField0ComboboxRef}
stackByField1={countTableStackBy1}
+ stackByField1ComboboxRef={stackByField1ComboboxRef}
title={title}
/>
)}
@@ -208,23 +242,23 @@ const ChartPanelsComponent: React.FC = ({
addFilter={addFilter}
alignHeader="flexStart"
chartOptionsContextMenu={chartOptionsContextMenu}
+ filters={alertsHistogramDefaultFilters}
inspectTitle={i18n.TREEMAP}
isPanelExpanded={isTreemapPanelExpanded}
- filters={alertsHistogramDefaultFilters}
query={query}
+ riskSubAggregationField="kibana.alert.risk_score"
+ runtimeMappings={runtimeMappings}
setIsPanelExpanded={setIsTreemapPanelExpanded}
setStackByField0={updateCommonStackBy0}
setStackByField0ComboboxInputRef={setStackByField0ComboboxInputRef}
- stackByField0ComboboxRef={stackByField0ComboboxRef}
setStackByField1={updateCommonStackBy1}
setStackByField1ComboboxInputRef={setStackByField1ComboboxInputRef}
- stackByField1ComboboxRef={stackByField1ComboboxRef}
signalIndexName={signalIndexName}
stackByField0={riskChartStackBy0}
+ stackByField0ComboboxRef={stackByField0ComboboxRef}
stackByField1={riskChartStackBy1}
+ stackByField1ComboboxRef={stackByField1ComboboxRef}
title={title}
- riskSubAggregationField="kibana.alert.risk_score"
- runtimeMappings={runtimeMappings}
/>
)}
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx
index a64ff6ecd3d4..f5a044a0b287 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx
@@ -139,6 +139,9 @@ jest.mock('../../components/alerts_table/timeline_actions/use_bulk_add_to_case_a
useBulkAddToCaseActions: jest.fn(() => []),
}));
+jest.mock('../../../common/components/visualization_actions/lens_embeddable');
+jest.mock('../../../common/components/page/use_refetch_by_session');
+
describe('DetectionEnginePageComponent', () => {
beforeAll(() => {
(useParams as jest.Mock).mockReturnValue({});
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.test.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.test.tsx
index ee2d86faca6c..7d860e5a9961 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.test.tsx
@@ -17,10 +17,8 @@ jest.mock('../../../containers/authentications');
jest.mock('../../../../common/containers/query_toggle');
jest.mock('../../../../common/lib/kibana');
-jest.mock('react-router-dom', () => {
- const actual = jest.requireActual('react-router-dom');
- return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) };
-});
+jest.mock('../../../../common/components/visualization_actions');
+jest.mock('../../../../common/components/visualization_actions/lens_embeddable');
describe('Authentications query tab body', () => {
const mockUseAuthentications = useAuthentications as jest.Mock;
diff --git a/x-pack/plugins/security_solution/public/helpers.tsx b/x-pack/plugins/security_solution/public/helpers.tsx
index 713ea0876ade..7a0f918d4e16 100644
--- a/x-pack/plugins/security_solution/public/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/helpers.tsx
@@ -20,13 +20,10 @@ import {
CASES_FEATURE_ID,
CASES_PATH,
EXCEPTIONS_PATH,
- HOSTS_PATH,
LANDING_PATH,
- NETWORK_PATH,
RULES_PATH,
SERVER_APP_ID,
THREAT_INTELLIGENCE_PATH,
- USERS_PATH,
} from '../common/constants';
import type {
FactoryQueryTypes,
@@ -195,13 +192,6 @@ export const isThreatIntelligencePath = (pathname: string): boolean => {
});
};
-export const isExplorePage = (pathname: string): boolean => {
- return !!matchPath(pathname, {
- path: `(${HOSTS_PATH}|${USERS_PATH}|${NETWORK_PATH})`,
- strict: false,
- });
-};
-
export const getSubPluginRoutesByCapabilities = (
subPlugins: StartedSubPlugins,
capabilities: Capabilities
diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx
index cc960b4130ff..e3ab3b292aab 100644
--- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx
@@ -242,7 +242,6 @@ export const AlertsByStatus = ({
label={STATUS_OPEN}
title={}
totalCount={openCount}
- isChartEmbeddablesEnabled={isChartEmbeddablesEnabled}
/>
)}
@@ -272,7 +271,6 @@ export const AlertsByStatus = ({
label={STATUS_ACKNOWLEDGED}
title={}
totalCount={acknowledgedCount}
- isChartEmbeddablesEnabled={isChartEmbeddablesEnabled}
/>
)}
@@ -299,7 +297,6 @@ export const AlertsByStatus = ({
label={STATUS_CLOSED}
title={}
totalCount={closedCount}
- isChartEmbeddablesEnabled={isChartEmbeddablesEnabled}
/>
)}
From d1194d488a6626731e1ee9cc43e599e1c7f48e8f Mon Sep 17 00:00:00 2001
From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
Date: Thu, 2 Feb 2023 10:20:46 -0500
Subject: [PATCH 14/35] [Security Solution][Endpoint] CLI dev tool to run
Elastic Agent/Endpoint against an Elastic stack (#149903)
## Summary
New CLI tool to assist in development with connecting a host VM with
Elastic Agent installed and running to an Elastic stack. Feature
include:
- Works against any elastic stack, including `localhost` (developer
setup)
- Can be run multiple times against the same stack (get multiple hosts
communicating with the stack)
- Will run a Fleet Server instance (via Docker) if one is not already
running on the targeted stack
- Creates a new VM using [`multipass` from
Canonical](https://multipass.run) and installs and runs Elastic agent
- All supported arguments are optional and have defaults assigned them
(defaults target a `localhost` developer setup). See below for output of
`--help`.
- Can defined the specific version of the Elastic Agent to run. If none
is defined, the version of the stack will be used. `SNAPSHOT` version
are also supported.
- Can define the policy ID to use for Enrollment. If none is defined,
tool will create a new Agent Policy with Endpoint integration for use by
the tool. Subsequent runs of this tool will also use this one agent
policy if it is already present in the system.
- Tools requires (dependencies) `docker` and `multipass` to be installed
(both are checked to be present when the tool runs
---
.../index_fleet_endpoint_policy.ts | 6 +-
.../scripts/endpoint/common/fleet_services.ts | 176 +++++++-
.../endpoint/common/localhost_services.ts | 29 ++
.../scripts/endpoint/common/stack_services.ts | 30 ++
.../endpoint_agent_runner/elastic_endpoint.ts | 250 ++++++++++++
.../endpoint_agent_runner/fleet_server.ts | 384 ++++++++++++++++++
.../endpoint/endpoint_agent_runner/index.ts | 69 ++++
.../endpoint_agent_runner/pre_check.ts | 49 +++
.../endpoint/endpoint_agent_runner/runtime.ts | 61 +++
.../endpoint/endpoint_agent_runner/setup.ts | 24 ++
.../endpoint/endpoint_agent_runner/types.ts | 18 +
.../scripts/endpoint/run_endpoint_agent.js | 9 +
12 files changed, 1101 insertions(+), 4 deletions(-)
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/pre_check.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts
create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/run_endpoint_agent.js
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 c7ccc728525d..1c2326832402 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
@@ -33,7 +33,8 @@ export interface IndexedFleetEndpointPolicyResponse {
export const indexFleetEndpointPolicy = async (
kbnClient: KbnClient,
policyName: string,
- endpointPackageVersion: string = '8.0.0'
+ endpointPackageVersion: string = '8.0.0',
+ agentPolicyName?: string
): Promise => {
const response: IndexedFleetEndpointPolicyResponse = {
integrationPolicies: [],
@@ -42,7 +43,8 @@ export const indexFleetEndpointPolicy = async (
// Create Agent Policy first
const newAgentPolicyData: CreateAgentPolicyRequest['body'] = {
- name: `Policy for ${policyName} (${Math.random().toString(36).substr(2, 5)})`,
+ name:
+ agentPolicyName || `Policy for ${policyName} (${Math.random().toString(36).substr(2, 5)})`,
description: `Policy created with endpoint data generator (${policyName})`,
namespace: 'default',
};
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 a8bc5fb04a8d..effa09db0c38 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
@@ -6,10 +6,27 @@
*/
import type { Client, estypes } from '@elastic/elasticsearch';
-import { AGENTS_INDEX } from '@kbn/fleet-plugin/common';
-import type { AgentStatus } from '@kbn/fleet-plugin/common';
+import type {
+ Agent,
+ AgentStatus,
+ GetAgentPoliciesRequest,
+ GetAgentPoliciesResponse,
+ GetAgentsResponse,
+} from '@kbn/fleet-plugin/common';
+import { AGENT_API_ROUTES, agentPolicyRouteService, AGENTS_INDEX } from '@kbn/fleet-plugin/common';
import { pick } from 'lodash';
import { ToolingLog } from '@kbn/tooling-log';
+import type { KbnClient } from '@kbn/test';
+import type { GetFleetServerHostsResponse } from '@kbn/fleet-plugin/common/types/rest_spec/fleet_server_hosts';
+import {
+ enrollmentAPIKeyRouteService,
+ fleetServerHostsRoutesService,
+} from '@kbn/fleet-plugin/common/services';
+import type {
+ EnrollmentAPIKey,
+ GetAgentsRequest,
+ GetEnrollmentAPIKeysResponse,
+} from '@kbn/fleet-plugin/common/types';
import { FleetAgentGenerator } from '../../../common/endpoint/data_generators/fleet_agent_generator';
const fleetGenerator = new FleetAgentGenerator();
@@ -64,3 +81,158 @@ export const checkInFleetAgent = async (
},
});
};
+
+/**
+ * Query Fleet Agents API
+ *
+ * @param kbnClient
+ * @param options
+ */
+export const fetchFleetAgents = async (
+ kbnClient: KbnClient,
+ options: GetAgentsRequest['query']
+): Promise => {
+ return kbnClient
+ .request({
+ method: 'GET',
+ path: AGENT_API_ROUTES.LIST_PATTERN,
+ query: options,
+ })
+ .then((response) => response.data);
+};
+
+/**
+ * Will keep querying Fleet list of agents until the given `hostname` shows up as healthy
+ *
+ * @param kbnClient
+ * @param hostname
+ * @param timeoutMs
+ */
+export const waitForHostToEnroll = async (
+ kbnClient: KbnClient,
+ hostname: string,
+ timeoutMs: number = 30000
+): Promise => {
+ const started = new Date();
+ const hasTimedOut = (): boolean => {
+ const elapsedTime = Date.now() - started.getTime();
+ return elapsedTime > timeoutMs;
+ };
+ let found: Agent | undefined;
+
+ while (!found && !hasTimedOut()) {
+ found = await fetchFleetAgents(kbnClient, {
+ perPage: 1,
+ kuery: `(local_metadata.host.hostname.keyword : "${hostname}") and (status:online)`,
+ showInactive: false,
+ }).then((response) => response.items[0]);
+
+ if (!found) {
+ // sleep and check again
+ await new Promise((r) => setTimeout(r, 2000));
+ }
+ }
+
+ if (!found) {
+ throw new Error(`Timed out waiting for host [${hostname}] to show up in Fleet`);
+ }
+
+ return found;
+};
+
+/**
+ * Returns the URL for the default Fleet Server connected to the stack
+ * @param kbnClient
+ */
+export const fetchFleetServerUrl = async (kbnClient: KbnClient): Promise => {
+ const fleetServerListResponse = await kbnClient
+ .request({
+ method: 'GET',
+ path: fleetServerHostsRoutesService.getListPath(),
+ query: {
+ perPage: 100,
+ },
+ })
+ .then((response) => response.data);
+
+ // TODO:PT need to also pull in the Proxies and use that instead if defiend for url
+
+ let url: string | undefined;
+
+ for (const fleetServer of fleetServerListResponse.items) {
+ if (!url || fleetServer.is_default) {
+ url = fleetServer.host_urls[0];
+
+ if (fleetServer.is_default) {
+ break;
+ }
+ }
+ }
+
+ return url;
+};
+
+/**
+ * Retrieve the API enrollment key for a given FLeet Agent Policy
+ * @param kbnClient
+ * @param agentPolicyId
+ */
+export const fetchAgentPolicyEnrollmentKey = async (
+ kbnClient: KbnClient,
+ agentPolicyId: string
+): Promise => {
+ const apiKey: EnrollmentAPIKey | undefined = await kbnClient
+ .request({
+ method: 'GET',
+ path: enrollmentAPIKeyRouteService.getListPath(),
+ query: { kuery: `policy_id: "${agentPolicyId}"` },
+ })
+ .then((response) => response.data.items[0]);
+
+ if (!apiKey) {
+ return;
+ }
+
+ return apiKey.api_key;
+};
+
+/**
+ * Retrieves a list of Fleet Agent policies
+ * @param kbnClient
+ * @param options
+ */
+export const fetchAgentPolicyList = async (
+ kbnClient: KbnClient,
+ options: GetAgentPoliciesRequest['query'] = {}
+) => {
+ return kbnClient
+ .request({
+ method: 'GET',
+ path: agentPolicyRouteService.getListPath(),
+ query: options,
+ })
+ .then((response) => response.data);
+};
+
+/**
+ * Returns the Agent Version that matches the current stack version. Will use `SNAPSHOT` if
+ * appropriate too.
+ * @param kbnClient
+ */
+export const getAgentVersionMatchingCurrentStack = async (
+ kbnClient: KbnClient
+): Promise => {
+ const kbnStatus = await kbnClient.status.get();
+ let version = kbnStatus.version.number;
+
+ // Add `-SNAPSHOT` if version indicates it was from a snapshot or the build hash starts
+ // with `xxxxxxxxx` (value that seems to be present when running kibana from source)
+ if (
+ kbnStatus.version.build_snapshot ||
+ kbnStatus.version.build_hash.startsWith('XXXXXXXXXXXXXXX')
+ ) {
+ version += '-SNAPSHOT';
+ }
+
+ return version;
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts
new file mode 100644
index 000000000000..3b985f76dcd8
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/common/localhost_services.ts
@@ -0,0 +1,29 @@
+/*
+ * 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 execa from 'execa';
+
+const POSSIBLE_LOCALHOST_VALUES: readonly string[] = [
+ 'localhost',
+ '127.0.0.1',
+ '0.0.0.0',
+ '::1',
+ '0000:0000:0000:0000:0000:0000:0000:0000',
+];
+
+export const getLocalhostRealIp = async (): Promise => {
+ // TODO:PT find better way to get host machine public IP. Command below is not x-platform
+
+ return execa.commandSync(
+ "ipconfig getifaddr `scutil --dns |awk -F'[()]' '$1~/if_index/ {print $2;exit;}'`",
+ { shell: true }
+ ).stdout;
+};
+
+export const isLocalhost = (hostname: string): boolean => {
+ return POSSIBLE_LOCALHOST_VALUES.includes(hostname.toLowerCase());
+};
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 213f839421a7..424f451c3fdc 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
@@ -9,6 +9,7 @@ import { Client } from '@elastic/elasticsearch';
import { ToolingLog } from '@kbn/tooling-log';
import { KbnClient } from '@kbn/test';
import type { StatusResponse } from '@kbn/core-status-common-internal';
+import { getLocalhostRealIp, isLocalhost } from './localhost_services';
import { createSecuritySuperuser } from './security_user_services';
export interface RuntimeServices {
@@ -19,6 +20,19 @@ export interface RuntimeServices {
username: string;
password: string;
}>;
+ localhostRealIp: string;
+ kibana: {
+ url: string;
+ hostname: string;
+ port: string;
+ isLocalhost: boolean;
+ };
+ elastic: {
+ url: string;
+ hostname: string;
+ port: string;
+ isLocalhost: boolean;
+ };
}
interface CreateRuntimeServicesOptions {
@@ -58,14 +72,30 @@ export const createRuntimeServices = async ({
}
}
+ const kbnURL = new URL(kibanaUrl);
+ const esURL = new URL(elasticsearchUrl);
+
return {
kbnClient: createKbnClient({ log, url: kibanaUrl, username, password }),
esClient: createEsClient({ log, url: elasticsearchUrl, username, password }),
log,
+ localhostRealIp: await getLocalhostRealIp(),
user: {
username,
password,
},
+ kibana: {
+ url: kibanaUrl,
+ hostname: kbnURL.hostname,
+ port: kbnURL.port,
+ isLocalhost: isLocalhost(kbnURL.hostname),
+ },
+ elastic: {
+ url: elasticsearchUrl,
+ hostname: esURL.hostname,
+ port: esURL.port,
+ isLocalhost: isLocalhost(esURL.hostname),
+ },
};
};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts
new file mode 100644
index 000000000000..7df5030a7009
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts
@@ -0,0 +1,250 @@
+/*
+ * 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 { userInfo } from 'os';
+import execa from 'execa';
+import nodeFetch from 'node-fetch';
+import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common';
+import chalk from 'chalk';
+import { getEndpointPackageInfo } from '../../../common/endpoint/index_data';
+import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
+import {
+ fetchAgentPolicyEnrollmentKey,
+ fetchAgentPolicyList,
+ fetchFleetServerUrl,
+ waitForHostToEnroll,
+} from '../common/fleet_services';
+import { getRuntimeServices } from './runtime';
+
+interface ElasticArtifactSearchResponse {
+ manifest: {
+ 'last-update-time': string;
+ 'seconds-since-last-update': number;
+ };
+ packages: {
+ [packageFileName: string]: {
+ architecture: string;
+ os: string[];
+ type: string;
+ asc_url: string;
+ sha_url: string;
+ url: string;
+ };
+ };
+}
+
+export const enrollEndpointHost = async () => {
+ const {
+ log,
+ kbnClient,
+ options: { version, policy },
+ } = getRuntimeServices();
+
+ log.info(`Creating VM and enrolling Elastic Agent`);
+ log.indent(4);
+
+ try {
+ const uniqueId = Math.random().toString(32).substring(2).substring(0, 4);
+ const username = userInfo().username.toLowerCase();
+ const policyId: string = policy || (await getOrCreateAgentPolicyId());
+
+ if (!policyId) {
+ throw new Error(`No valid policy id provide or unable to create it`);
+ }
+
+ if (!version) {
+ throw new Error(`No 'version' specified`);
+ }
+
+ const [fleetServerHostUrl, enrollmentToken] = await Promise.all([
+ fetchFleetServerUrl(kbnClient),
+ fetchAgentPolicyEnrollmentKey(kbnClient, policyId),
+ ]);
+
+ if (!fleetServerHostUrl) {
+ throw new Error(`Fleet setting does not have a Fleet Server host defined!`);
+ }
+
+ if (!enrollmentToken) {
+ throw new Error(`No API enrollment key found for policy id [${policyId}]`);
+ }
+
+ const vmName = `${username}-dev-${uniqueId}`;
+
+ log.info(`Creating VM named: ${vmName}`);
+
+ await execa.command(`multipass launch --name ${vmName}`);
+
+ log.verbose(await execa('multipass', ['info', vmName]));
+
+ const agentDownloadUrl = await getAgentDownloadUrl(version);
+ const agentDownloadedFile = agentDownloadUrl.substring(agentDownloadUrl.lastIndexOf('/') + 1);
+ const vmDirName = agentDownloadedFile.replace(/\.tar\.gz$/, '');
+
+ log.info(`Downloading and installing agent`);
+ log.verbose(`Agent download:\n ${agentDownloadUrl}`);
+
+ await execa.command(
+ `multipass exec ${vmName} -- curl -L ${agentDownloadUrl} -o ${agentDownloadedFile}`
+ );
+ await execa.command(`multipass exec ${vmName} -- tar -zxf ${agentDownloadedFile}`);
+ await execa.command(`multipass exec ${vmName} -- rm -f ${agentDownloadedFile}`);
+
+ const agentEnrollArgs = [
+ 'exec',
+
+ vmName,
+
+ '--working-directory',
+ `/home/ubuntu/${vmDirName}`,
+
+ '--',
+
+ 'sudo',
+
+ './elastic-agent',
+
+ 'enroll',
+
+ '--insecure',
+
+ '--force',
+
+ '--url',
+ fleetServerHostUrl,
+
+ '--enrollment-token',
+ enrollmentToken,
+ ];
+
+ log.info(`Enrolling elastic agent with Fleet`);
+ log.verbose(`Command: multipass ${agentEnrollArgs.join(' ')}`);
+
+ await execa(`multipass`, agentEnrollArgs);
+
+ const runAgentCommand = `multipass exec ${vmName} --working-directory /home/ubuntu/${vmDirName} -- sudo ./elastic-agent \&>/dev/null`;
+
+ log.info(`Running elastic agent`);
+ log.verbose(`Command: ${runAgentCommand}`);
+
+ // About `timeout` option below
+ // The `multipass exec` command seems to have some issues when a command pass to it redirects output,
+ // as is with the command that runs endpoint. See https://github.com/canonical/multipass/issues/667
+ // To get around it, `timeout` is set to 5s, which should be enough time for the command to be executed
+ // in the VM.
+ await execa.command(runAgentCommand, { timeout: 5000 }).catch((error) => {
+ if (error.originalMessage !== 'Timed out') {
+ throw error;
+ }
+ });
+
+ log.info(`Waiting for Agent to check-in with Fleet`);
+ await waitForHostToEnroll(kbnClient, vmName);
+
+ log.info(`VM created using Multipass.
+ VM Name: ${vmName}
+ Elastic Agent Version: ${version}
+
+ Shell access: ${chalk.bold(`multipass shell ${vmName}`)}
+ Delete VM: ${chalk.bold(`multipass delete -p ${vmName}${await getVmCountNotice()}`)}
+`);
+ } catch (error) {
+ log.error(error);
+ log.indent(-4);
+ throw error;
+ }
+
+ log.indent(-4);
+};
+
+const getAgentDownloadUrl = async (version: string): Promise => {
+ const { log } = getRuntimeServices();
+ // TODO:PT use arch and platform of VM to build download file name below (will be needed if tools ever supports different types of VMs)
+ const agentFile = `elastic-agent-${version}-linux-arm64.tar.gz`;
+ const artifactSearchUrl = `https://artifacts-api.elastic.co/v1/search/${version}/${agentFile}`;
+
+ log.verbose(`Retrieving elastic agent download URL from:\n ${artifactSearchUrl}`);
+
+ const searchResult: ElasticArtifactSearchResponse = await nodeFetch(artifactSearchUrl).then(
+ (response) => {
+ if (!response.ok) {
+ throw new Error(
+ `Failed to search elastic's artifact repository: ${response.statusText} (HTTP ${response.status})`
+ );
+ }
+
+ return response.json();
+ }
+ );
+
+ log.verbose(searchResult);
+
+ if (!searchResult.packages[agentFile]) {
+ throw new Error(`Unable to find an Agent download URL for version [${version}]`);
+ }
+
+ return searchResult.packages[agentFile].url;
+};
+
+const getOrCreateAgentPolicyId = async (): Promise => {
+ const { kbnClient, log } = getRuntimeServices();
+ const username = userInfo().username.toLowerCase();
+ const endpointPolicyName = `${username} test integration`;
+ const agentPolicyName = `${username} test policy`;
+
+ const existingPolicy = await fetchAgentPolicyList(kbnClient, {
+ kuery: `${AGENT_POLICY_SAVED_OBJECT_TYPE}.name: "${agentPolicyName}"`,
+ });
+
+ if (existingPolicy.items[0]) {
+ log.info(`Using existing Fleet test agent policy`);
+ log.verbose(existingPolicy.items[0]);
+
+ return existingPolicy.items[0].id;
+ }
+
+ // Create new policy
+ const endpointPackageVersion = (await getEndpointPackageInfo(kbnClient)).version;
+ const response = await indexFleetEndpointPolicy(
+ kbnClient,
+ endpointPolicyName,
+ endpointPackageVersion,
+ agentPolicyName
+ );
+
+ const agentPolicy = response.agentPolicies[0];
+
+ log.info(`New agent policy with Endpoint integration created:
+ Name: ${agentPolicy.name}
+ Id: ${agentPolicy.id}`);
+
+ log.verbose(JSON.stringify(response, null, 2));
+
+ return agentPolicy.id ?? '';
+};
+
+const getVmCountNotice = async (threshold: number = 1): Promise => {
+ const response = await execa.command(`multipass list --format=json`);
+
+ const output: { list: Array<{ ipv4: string; name: string; release: string; state: string }> } =
+ JSON.parse(response.stdout);
+
+ if (output.list.length > threshold) {
+ return `
+
+-----------------------------------------------------------------
+${chalk.red('NOTE:')} ${chalk.bold(
+ `You currently have ${output.list.length} VMs running.`
+ )} Remember to delete those
+ no longer being used.
+ View running VMs: ${chalk.bold('multipass list')}
+ -----------------------------------------------------------------
+`;
+ }
+
+ return '';
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts
new file mode 100644
index 000000000000..e8891f04aa6e
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/fleet_server.ts
@@ -0,0 +1,384 @@
+/*
+ * 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 {
+ AgentPolicy,
+ CreateAgentPolicyResponse,
+ GetPackagePoliciesResponse,
+ Output,
+ PackagePolicy,
+} from '@kbn/fleet-plugin/common';
+import {
+ AGENT_POLICY_API_ROUTES,
+ FLEET_SERVER_PACKAGE,
+ PACKAGE_POLICY_API_ROUTES,
+ PACKAGE_POLICY_SAVED_OBJECT_TYPE,
+} from '@kbn/fleet-plugin/common';
+import { APP_API_ROUTES } from '@kbn/fleet-plugin/common/constants';
+import type {
+ FleetServerHost,
+ GenerateServiceTokenResponse,
+ GetOneOutputResponse,
+ GetOutputsResponse,
+ PutOutputRequest,
+} from '@kbn/fleet-plugin/common/types';
+import {
+ fleetServerHostsRoutesService,
+ outputRoutesService,
+} from '@kbn/fleet-plugin/common/services';
+import execa from 'execa';
+import type {
+ PostFleetServerHostsRequest,
+ PostFleetServerHostsResponse,
+} from '@kbn/fleet-plugin/common/types/rest_spec/fleet_server_hosts';
+import chalk from 'chalk';
+import { isLocalhost } from '../common/localhost_services';
+import {
+ fetchFleetAgents,
+ fetchFleetServerUrl,
+ waitForHostToEnroll,
+} from '../common/fleet_services';
+import { getRuntimeServices } from './runtime';
+
+export const runFleetServerIfNeeded = async () => {
+ const {
+ log,
+ kibana: { isLocalhost: isKibanaOnLocalhost },
+ } = getRuntimeServices();
+
+ log.info(`Setting up fleet server (if necessary)`);
+ log.indent(4);
+
+ const fleetServerAlreadyEnrolled = await isFleetServerEnrolled();
+
+ if (fleetServerAlreadyEnrolled) {
+ log.info(`Fleet server is already enrolled with Fleet. Nothing to do.`);
+ log.indent(-4);
+ return;
+ }
+
+ try {
+ const fleetServerAgentPolicyId = await getOrCreateFleetServerAgentPolicyId();
+ const serviceToken = await generateFleetServiceToken();
+
+ if (isKibanaOnLocalhost) {
+ await configureFleetIfNeeded();
+ }
+
+ await startFleetServerWithDocker({
+ policyId: fleetServerAgentPolicyId,
+ serviceToken,
+ });
+ } catch (error) {
+ log.error(error);
+ log.indent(-4);
+ throw error;
+ }
+
+ log.indent(-4);
+};
+
+const isFleetServerEnrolled = async () => {
+ const { kbnClient } = getRuntimeServices();
+ const policyId = (await getFleetServerPackagePolicy())?.policy_id;
+
+ if (!policyId) {
+ return false;
+ }
+
+ const fleetAgentsResponse = await fetchFleetAgents(kbnClient, {
+ kuery: `(policy_id: "${policyId}" and active : true) and (status:online)`,
+ showInactive: false,
+ perPage: 1,
+ });
+
+ return Boolean(fleetAgentsResponse.total);
+};
+
+const getFleetServerPackagePolicy = async (): Promise => {
+ const { kbnClient } = getRuntimeServices();
+
+ return kbnClient
+ .request({
+ method: 'GET',
+ path: PACKAGE_POLICY_API_ROUTES.LIST_PATTERN,
+ query: {
+ perPage: 1,
+ kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: "${FLEET_SERVER_PACKAGE}"`,
+ },
+ })
+ .then((response) => response.data.items[0]);
+};
+
+const getOrCreateFleetServerAgentPolicyId = async (): Promise => {
+ const { log, kbnClient } = getRuntimeServices();
+
+ const existingFleetServerIntegrationPolicy = await getFleetServerPackagePolicy();
+
+ if (existingFleetServerIntegrationPolicy) {
+ log.verbose(
+ `Found existing Fleet Server Policy: ${JSON.stringify(
+ existingFleetServerIntegrationPolicy,
+ null,
+ 2
+ )}`
+ );
+ log.info(
+ `Using existing Fleet Server agent policy id: ${existingFleetServerIntegrationPolicy.policy_id}`
+ );
+
+ return existingFleetServerIntegrationPolicy.policy_id;
+ }
+
+ log.info(`Creating new Fleet Server policy`);
+
+ const createdFleetServerPolicy: AgentPolicy = await kbnClient
+ .request({
+ method: 'POST',
+ path: AGENT_POLICY_API_ROUTES.CREATE_PATTERN,
+ body: {
+ name: `Fleet Server policy (${Math.random().toString(32).substring(2)})`,
+ description: `Created by CLI Tool via: ${__filename}`,
+ namespace: 'default',
+ monitoring_enabled: ['logs', 'metrics'],
+ // This will ensure the Fleet Server integration policy
+ // is also created and added to the agent policy
+ has_fleet_server: true,
+ },
+ })
+ .then((response) => response.data.item);
+
+ log.indent(4);
+ log.info(
+ `Agent Policy created: ${createdFleetServerPolicy.name} (${createdFleetServerPolicy.id})`
+ );
+ log.verbose(createdFleetServerPolicy);
+ log.indent(-4);
+
+ return createdFleetServerPolicy.id;
+};
+
+const generateFleetServiceToken = async (): Promise => {
+ const { kbnClient, log } = getRuntimeServices();
+
+ const serviceToken: string = await kbnClient
+ .request({
+ method: 'POST',
+ path: APP_API_ROUTES.GENERATE_SERVICE_TOKEN_PATTERN,
+ body: {},
+ })
+ .then((response) => response.data.value);
+
+ log.info(`New service token created.`);
+
+ return serviceToken;
+};
+
+const startFleetServerWithDocker = async ({
+ policyId,
+ serviceToken,
+}: {
+ policyId: string;
+ serviceToken: string;
+}) => {
+ const {
+ log,
+ localhostRealIp,
+ elastic: { url: elasticUrl, isLocalhost: isElasticOnLocalhost },
+ kbnClient,
+ options: { version },
+ } = getRuntimeServices();
+
+ log.info(`Starting a new fleet server using Docker`);
+ log.indent(4);
+
+ const esURL = new URL(elasticUrl);
+ const containerName = `dev-fleet-server.${esURL.hostname}`;
+ let esUrlWithRealIp: string = elasticUrl;
+
+ if (isElasticOnLocalhost) {
+ esURL.hostname = localhostRealIp;
+ esUrlWithRealIp = esURL.toString();
+ }
+
+ try {
+ const dockerArgs = [
+ 'run',
+
+ '--restart',
+ 'no',
+
+ '--add-host',
+ 'host.docker.internal:host-gateway',
+
+ '--rm',
+
+ '--detach',
+
+ '--name',
+ containerName,
+
+ // The container's hostname will appear in Fleet when the agent enrolls
+ '--hostname',
+ containerName,
+
+ '--env',
+ 'FLEET_SERVER_ENABLE=1',
+
+ '--env',
+ `FLEET_SERVER_ELASTICSEARCH_HOST=${esUrlWithRealIp}`,
+
+ '--env',
+ `FLEET_SERVER_SERVICE_TOKEN=${serviceToken}`,
+
+ '--env',
+ `FLEET_SERVER_POLICY=${policyId}`,
+
+ '--publish',
+ '8220:8220',
+
+ `docker.elastic.co/beats/elastic-agent:${version}`,
+ ];
+
+ await execa('docker', ['kill', containerName])
+ .then(() => {
+ log.verbose(
+ `Killed an existing container with name [${containerName}]. New one will be started.`
+ );
+ })
+ .catch((error) => {
+ log.verbose(`Attempt to kill currently running fleet-server container (if any) with name [${containerName}] was unsuccessful:
+ ${error}
+(This is ok if one was not running already)`);
+ });
+
+ log.verbose(`docker arguments:\n${dockerArgs.join(' ')}`);
+
+ const containerId = (await execa('docker', dockerArgs)).stdout;
+
+ const fleetServerAgent = await waitForHostToEnroll(kbnClient, containerName);
+
+ log.verbose(`Fleet server enrolled agent:\n${JSON.stringify(fleetServerAgent, null, 2)}`);
+
+ await addFleetServerHostToFleetSettings(`https://${localhostRealIp}:8220`);
+
+ log.info(`Done. Fleet Server is running and connected to Fleet.
+ Container Name: ${containerName}
+ Container Id: ${containerId}
+
+ View running output: ${chalk.bold(`docker attach ---sig-proxy=false ${containerName}`)}
+ Shell access: ${chalk.bold(`docker exec -it ${containerName} /bin/bash`)}
+`);
+ } catch (error) {
+ log.error(error);
+ log.indent(-4);
+ throw error;
+ }
+
+ log.indent(-4);
+};
+
+const configureFleetIfNeeded = async () => {
+ const { log, kbnClient, localhostRealIp } = getRuntimeServices();
+
+ log.info('Checking if Fleet needs to be configured');
+ log.indent(4);
+
+ try {
+ // make sure that all ES hostnames are using localhost real IP
+ const fleetOutputs = await kbnClient
+ .request({
+ method: 'GET',
+ path: outputRoutesService.getListPath(),
+ })
+ .then((response) => response.data);
+
+ for (const { id, ...output } of fleetOutputs.items) {
+ if (output.type === 'elasticsearch') {
+ if (output.hosts) {
+ let needsUpdating = false;
+ const updatedHosts: Output['hosts'] = [];
+
+ for (const host of output.hosts) {
+ const hostURL = new URL(host);
+
+ if (isLocalhost(hostURL.hostname)) {
+ needsUpdating = true;
+ hostURL.hostname = localhostRealIp;
+ updatedHosts.push(hostURL.toString());
+
+ log.verbose(
+ `Fleet Settings for Elasticsearch Output [Name: ${
+ output.name
+ } (id: ${id})]: Host [${host}] updated to [${hostURL.toString()}]`
+ );
+ } else {
+ updatedHosts.push(host);
+ }
+ }
+
+ if (needsUpdating) {
+ const update: PutOutputRequest['body'] = {
+ ...(output as PutOutputRequest['body']), // cast needed to quite TS - looks like the types for Output in fleet differ a bit between create/update
+ hosts: updatedHosts,
+ };
+
+ log.info(`Updating Fleet Settings for Output [${output.name} (${id})]`);
+
+ await kbnClient.request({
+ method: 'PUT',
+ path: outputRoutesService.getUpdatePath(id),
+ body: update,
+ });
+ }
+ }
+ }
+ }
+ } catch (error) {
+ log.error(error);
+ log.indent(-4);
+ throw error;
+ }
+
+ log.indent(-4);
+};
+
+const addFleetServerHostToFleetSettings = async (
+ fleetServerHostUrl: string
+): Promise => {
+ const { kbnClient, log } = getRuntimeServices();
+
+ log.info(`Updating Fleet with new fleet server host: ${fleetServerHostUrl}`);
+ log.indent(4);
+
+ try {
+ const exitingFleetServerHostUrl = await fetchFleetServerUrl(kbnClient);
+
+ const newFleetHostEntry: PostFleetServerHostsRequest['body'] = {
+ name: `Dev fleet server running on localhost`,
+ host_urls: [fleetServerHostUrl],
+ is_default: !exitingFleetServerHostUrl,
+ };
+
+ const { item } = await kbnClient
+ .request({
+ method: 'POST',
+ path: fleetServerHostsRoutesService.getCreatePath(),
+ body: newFleetHostEntry,
+ })
+ .then((response) => response.data);
+
+ log.verbose(item);
+ log.indent(-4);
+
+ return item;
+ } catch (error) {
+ log.error(error);
+ log.indent(-4);
+ throw error;
+ }
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts
new file mode 100644
index 000000000000..587a75679e46
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { RunFn } from '@kbn/dev-cli-runner';
+import { run } from '@kbn/dev-cli-runner';
+import { setupAll } from './setup';
+
+const runSetupAll: RunFn = async (cliContext) => {
+ const username = cliContext.flags.username as string;
+ const password = cliContext.flags.password as string;
+ const kibanaUrl = cliContext.flags.kibanaUrl as string;
+ const elasticUrl = cliContext.flags.elasticUrl as string;
+ const version = cliContext.flags.version as string;
+ const policy = cliContext.flags.policy as string;
+ const log = cliContext.log;
+
+ await setupAll({
+ elasticUrl,
+ kibanaUrl,
+ username,
+ password,
+ version,
+ policy,
+ log,
+ });
+};
+
+export const cli = () => {
+ run(
+ runSetupAll,
+
+ // Options
+ {
+ description: `
+ Enrolls a new host running Elastic Agent with Fleet. It will (if necessary) first create a
+ Fleet Server instance using Docker, and then it will initialize a new Ubuntu VM using
+ 'multipass', install Elastic Agent and enroll it with Fleet. Can be used multiple times
+ against the same stack.`,
+ flags: {
+ string: ['kibana', 'elastic', 'username', 'password', 'version', 'policy'],
+ default: {
+ kibanaUrl: 'http://127.0.0.1:5601',
+ elasticUrl: 'http://127.0.0.1:9200',
+ username: 'elastic',
+ password: 'changeme',
+ version: '',
+ policy: '',
+ },
+ help: `
+ --version Optional. The version of the Agent to use for enrolling the new host.
+ Default: uses the same version as the stack (kibana). Version
+ can also be from 'SNAPSHOT'.
+ Examples: 8.6.0, 8.7.0-SNAPSHOT
+ --policy Optional. An Agent Policy ID to use when enrolling the new Host
+ running Elastic Agent.
+ --username Optional. User name to be used for auth against elasticsearch and
+ kibana (Default: elastic).
+ --password Optional. Password associated with the username (Default: changeme)
+ --kibanaUrl Optional. The url to Kibana (Default: http://127.0.0.1:5601)
+ --elasticUrl Optional. The url to Elasticsearch (Default: http://127.0.0.1:9200)
+ `,
+ },
+ }
+ );
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/pre_check.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/pre_check.ts
new file mode 100644
index 000000000000..df0934b8b666
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/pre_check.ts
@@ -0,0 +1,49 @@
+/*
+ * 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 execa from 'execa';
+import { getRuntimeServices } from './runtime';
+
+export const checkDependencies = async () => {
+ const { log } = getRuntimeServices();
+
+ log.info(`Checking dependencies`);
+
+ // TODO:PT validate that ES / KBN is reachable
+
+ await Promise.all([checkDocker(), checkVmRunner()]);
+};
+
+const checkDocker = async () => {
+ const { log } = getRuntimeServices();
+
+ try {
+ const dockerVersion = await execa('docker', ['--version']);
+
+ log.verbose(`Using docker: ${dockerVersion.stdout}`);
+ } catch (err) {
+ log.verbose(err);
+ throw new Error(
+ `Docker not found on local machine [${err.message}]. Install it from: https://www.docker.com\n\n`
+ );
+ }
+};
+
+const checkVmRunner = async () => {
+ const { log } = getRuntimeServices();
+
+ try {
+ const version = await execa('multipass', ['--version']);
+
+ log.verbose(`Using 'multipass': ${version.stdout}`);
+ } catch (err) {
+ log.verbose(err);
+ throw new Error(
+ `Mutipass not found on local machine [${err.message}]. Install it from: https://multipass.run\n\n`
+ );
+ }
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts
new file mode 100644
index 000000000000..989b99690c4e
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 { ToolingLog } from '@kbn/tooling-log';
+import { getAgentVersionMatchingCurrentStack } from '../common/fleet_services';
+import type { StartRuntimeServicesOptions } from './types';
+import type { RuntimeServices } from '../common/stack_services';
+import { createRuntimeServices } from '../common/stack_services';
+
+interface EndpointRunnerRuntimeServices extends RuntimeServices {
+ options: Required<
+ Omit
+ >;
+}
+
+// Internal singleton storing the services for the current run
+let runtimeServices: undefined | EndpointRunnerRuntimeServices;
+
+export const startRuntimeServices = async ({
+ log = new ToolingLog(),
+ elasticUrl,
+ kibanaUrl,
+ username,
+ password,
+ ...otherOptions
+}: StartRuntimeServicesOptions) => {
+ const stackServices = await createRuntimeServices({
+ kibanaUrl,
+ elasticsearchUrl: elasticUrl,
+ username,
+ password,
+ log,
+ });
+
+ runtimeServices = {
+ ...stackServices,
+ options: {
+ ...otherOptions,
+
+ version:
+ otherOptions.version ||
+ (await getAgentVersionMatchingCurrentStack(stackServices.kbnClient)),
+ },
+ };
+};
+
+export const stopRuntimeServices = async () => {
+ runtimeServices = undefined;
+};
+
+export const getRuntimeServices = () => {
+ if (!runtimeServices) {
+ throw new Error(`Runtime services have not be initialized yet!`);
+ }
+
+ return runtimeServices;
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts
new file mode 100644
index 000000000000..18ef51a35bcc
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { runFleetServerIfNeeded } from './fleet_server';
+import { startRuntimeServices, stopRuntimeServices } from './runtime';
+import { checkDependencies } from './pre_check';
+import { enrollEndpointHost } from './elastic_endpoint';
+import type { StartRuntimeServicesOptions } from './types';
+
+export const setupAll = async (options: StartRuntimeServicesOptions) => {
+ await startRuntimeServices(options);
+
+ await checkDependencies();
+
+ await runFleetServerIfNeeded();
+
+ await enrollEndpointHost();
+
+ await stopRuntimeServices();
+};
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts
new file mode 100644
index 000000000000..2af229bc74a5
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.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 type { ToolingLog } from '@kbn/tooling-log';
+
+export interface StartRuntimeServicesOptions {
+ kibanaUrl: string;
+ elasticUrl: string;
+ username: string;
+ password: string;
+ version: string;
+ policy: string;
+ log?: ToolingLog;
+}
diff --git a/x-pack/plugins/security_solution/scripts/endpoint/run_endpoint_agent.js b/x-pack/plugins/security_solution/scripts/endpoint/run_endpoint_agent.js
new file mode 100644
index 000000000000..1a79b1456b9c
--- /dev/null
+++ b/x-pack/plugins/security_solution/scripts/endpoint/run_endpoint_agent.js
@@ -0,0 +1,9 @@
+/*
+ * 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.
+ */
+
+require('../../../../../src/setup_node_env');
+require('./endpoint_agent_runner').cli();
From 59488afa5d7fe30b683d2035d73c1a09f1394b9f Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Thu, 2 Feb 2023 07:21:43 -0800
Subject: [PATCH 15/35] [DOCS] Add specifications for deprecated alert APIs
(#149655)
---
.../rules/rule-apis-passthru.asciidoc | 1118 ++++++++++++++
.../alerting/docs/openapi/bundled.json | 1313 +++++++++++++++++
.../alerting/docs/openapi/bundled.yaml | 858 +++++++++++
.../schemas/alert_response_properties.yaml | 82 +
.../alerting/docs/openapi/entrypoint.yaml | 41 +-
.../paths/s@{spaceid}@api@alerts@_find.yaml | 120 ++
.../paths/s@{spaceid}@api@alerts@_health.yaml | 83 ++
...@{spaceid}@api@alerts@alert@{alertid}.yaml | 290 ++++
...}@api@alerts@alert@{alertid}@_disable.yaml | 30 +
...d}@api@alerts@alert@{alertid}@_enable.yaml | 30 +
...@api@alerts@alert@{alertid}@_mute_all.yaml | 30 +
...pi@alerts@alert@{alertid}@_unmute_all.yaml | 30 +
...lert_instance@{alertinstanceid}@_mute.yaml | 37 +
...rt_instance@{alertinstanceid}@_unmute.yaml | 37 +
...{spaceid}@api@alerts@list_alert_types.yaml | 111 ++
15 files changed, 4189 insertions(+), 21 deletions(-)
create mode 100644 x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml
create mode 100644 x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@list_alert_types.yaml
diff --git a/docs/api-generated/rules/rule-apis-passthru.asciidoc b/docs/api-generated/rules/rule-apis-passthru.asciidoc
index d92712adeb10..0626e0e43a8b 100644
--- a/docs/api-generated/rules/rule-apis-passthru.asciidoc
+++ b/docs/api-generated/rules/rule-apis-passthru.asciidoc
@@ -26,6 +26,19 @@ Any modifications made to this file will be overwritten.
get /s/{spaceId}/api/alerting/_health
get /s/{spaceId}/api/alerting/rule/{ruleId}
get /s/{spaceId}/api/alerting/rule_types
+ post /s/{spaceId}/api/alerts/alert/{alertId}
+ post /s/{spaceId}/api/alerts/alert/{alertId}/_disable
+ post /s/{spaceId}/api/alerts/alert/{alertId}/_enable
+ get /s/{spaceId}/api/alerts/alerts/_find
+ get /s/{spaceId}/api/alerts/alert/{alertId}
+ get /s/{spaceId}/api/alerts/alerts/list_alert_types
+ get /s/{spaceId}/api/alerts/alerts/_health
+ post /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute
+ post /s/{spaceId}/api/alerts/alert/{alertId}/_mute_all
+ post /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute
+ post /s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all
+ put /s/{spaceId}/api/alerts/alert/{alertId}
+ delete /s/{spaceId}/api/alerts/alert/{alertId}
post /s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_mute
post /s/{spaceId}/api/alerting/rule/{ruleId}/_mute_all
post /s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_unmute
@@ -812,6 +825,896 @@ Any modifications made to this file will be overwritten.
401_response
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}
+
Create an alert. (legacyCreateAlert)
+
Deprecated in 7.13.0. Use the create rule API instead.
+
+
Path parameters
+
+
alertId (required)
+
+
Path Parameter — An UUID v1 or v4 identifier for the alert. If this parameter is omitted, the identifier is randomly generated. default: null
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
Consumes
+ This API call consumes the following media types via the request header:
+
+
+
Request body
+
+
+
+
Body Parameter —
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "alertTypeId" : ".index-threshold",
+ "throttle" : "throttle",
+ "updatedBy" : "elastic",
+ "executionStatus" : {
+ "lastExecutionDate" : "2022-12-06T00:13:43.89Z",
+ "status" : "ok"
+ },
+ "params" : {
+ "key" : ""
+ },
+ "enabled" : true,
+ "mutedInstanceIds" : [ "mutedInstanceIds", "mutedInstanceIds" ],
+ "tags" : [ "tags", "tags" ],
+ "createdAt" : "2022-12-05T23:36:58.284Z",
+ "schedule" : {
+ "interval" : "interval"
+ },
+ "notifyWhen" : "onActionGroupChange",
+ "createdBy" : "elastic",
+ "muteAll" : false,
+ "name" : "my alert",
+ "scheduledTaskId" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "id" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "actions" : [ "{}", "{}" ],
+ "apiKeyOwner" : "elastic",
+ "updatedAt" : "2022-12-05T23:36:58.284Z"
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
alert_response_properties
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/_disable
+
Disables an alert. (legacyDisableAlert)
+
Deprecated in 7.13.0. Use the disable rule API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/_enable
+
Enables an alert. (legacyEnableAlert)
+
Deprecated in 7.13.0. Use the enable rule API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/alerts/alerts/_find
+
Retrieves a paginated set of alerts. (legacyFindAlerts)
+
Deprecated in 7.13.0. Use the find rules API instead. NOTE: Alert params
are stored as a flattened field type and analyzed as keywords. As alerts change in Kibana, the results on each page of the response also change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
Query parameters
+
+
default_search_operator (optional)
+
+
Query Parameter — The default operator to use for the simple_query_string
. default: OR
fields (optional)
+
+
Query Parameter — The fields to return in the attributes
key of the response. default: null
filter (optional)
+
+
Query Parameter — A KQL string that you filter with an attribute from your saved object. It should look like savedObjectType.attributes.title: "myTitle"
. However, if you used a direct attribute of a saved object, such as updatedAt
, you must define your filter, for example, savedObjectType.updatedAt > 2018-12-22
. default: null
has_reference (optional)
+
+
Query Parameter — Filters the rules that have a relation with the reference objects with a specific type and identifier. default: null
page (optional)
+
+
Query Parameter — The page number to return. default: 1
per_page (optional)
+
+
Query Parameter — The number of alerts to return per page. default: 20
search (optional)
+
+
Query Parameter — An Elasticsearch simple_query_string
query that filters the alerts in the response. default: null
search_fields (optional)
+
+
Query Parameter — The fields to perform the simple_query_string
parsed query against. default: null
sort_field (optional)
+
+
Query Parameter — Determines which field is used to sort the results. The field must exist in the attributes
key of the response. default: null
sort_order (optional)
+
+
Query Parameter — Determines the sort order. default: desc
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "total" : 1,
+ "perPage" : 6,
+ "data" : [ {
+ "alertTypeId" : ".index-threshold",
+ "throttle" : "throttle",
+ "updatedBy" : "elastic",
+ "executionStatus" : {
+ "lastExecutionDate" : "2022-12-06T00:13:43.89Z",
+ "status" : "ok"
+ },
+ "params" : {
+ "key" : ""
+ },
+ "enabled" : true,
+ "mutedInstanceIds" : [ "mutedInstanceIds", "mutedInstanceIds" ],
+ "tags" : [ "tags", "tags" ],
+ "createdAt" : "2022-12-05T23:36:58.284Z",
+ "schedule" : {
+ "interval" : "interval"
+ },
+ "notifyWhen" : "onActionGroupChange",
+ "createdBy" : "elastic",
+ "muteAll" : false,
+ "name" : "my alert",
+ "scheduledTaskId" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "id" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "actions" : [ "{}", "{}" ],
+ "apiKeyOwner" : "elastic",
+ "updatedAt" : "2022-12-05T23:36:58.284Z"
+ }, {
+ "alertTypeId" : ".index-threshold",
+ "throttle" : "throttle",
+ "updatedBy" : "elastic",
+ "executionStatus" : {
+ "lastExecutionDate" : "2022-12-06T00:13:43.89Z",
+ "status" : "ok"
+ },
+ "params" : {
+ "key" : ""
+ },
+ "enabled" : true,
+ "mutedInstanceIds" : [ "mutedInstanceIds", "mutedInstanceIds" ],
+ "tags" : [ "tags", "tags" ],
+ "createdAt" : "2022-12-05T23:36:58.284Z",
+ "schedule" : {
+ "interval" : "interval"
+ },
+ "notifyWhen" : "onActionGroupChange",
+ "createdBy" : "elastic",
+ "muteAll" : false,
+ "name" : "my alert",
+ "scheduledTaskId" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "id" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "actions" : [ "{}", "{}" ],
+ "apiKeyOwner" : "elastic",
+ "updatedAt" : "2022-12-05T23:36:58.284Z"
+ } ],
+ "page" : 0
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
legacyFindAlerts_200_response
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/alerts/alert/{alertId}
+
Retrieves an alert by its identifier. (legacyGetAlert)
+
Deprecated in 7.13.0. Use the get rule API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "alertTypeId" : ".index-threshold",
+ "throttle" : "throttle",
+ "updatedBy" : "elastic",
+ "executionStatus" : {
+ "lastExecutionDate" : "2022-12-06T00:13:43.89Z",
+ "status" : "ok"
+ },
+ "params" : {
+ "key" : ""
+ },
+ "enabled" : true,
+ "mutedInstanceIds" : [ "mutedInstanceIds", "mutedInstanceIds" ],
+ "tags" : [ "tags", "tags" ],
+ "createdAt" : "2022-12-05T23:36:58.284Z",
+ "schedule" : {
+ "interval" : "interval"
+ },
+ "notifyWhen" : "onActionGroupChange",
+ "createdBy" : "elastic",
+ "muteAll" : false,
+ "name" : "my alert",
+ "scheduledTaskId" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "id" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "actions" : [ "{}", "{}" ],
+ "apiKeyOwner" : "elastic",
+ "updatedAt" : "2022-12-05T23:36:58.284Z"
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
alert_response_properties
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/alerts/alerts/list_alert_types
+
Retrieves a list of alert types. (legacyGetAlertTypes)
+
Deprecated in 7.13.0. Use the get rule types API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "defaultActionGroupId" : "defaultActionGroupId",
+ "isExportable" : true,
+ "actionVariables" : {
+ "context" : [ {
+ "name" : "name",
+ "description" : "description"
+ }, {
+ "name" : "name",
+ "description" : "description"
+ } ],
+ "state" : [ {
+ "name" : "name",
+ "description" : "description"
+ }, {
+ "name" : "name",
+ "description" : "description"
+ } ],
+ "params" : [ {
+ "name" : "name",
+ "description" : "description"
+ }, {
+ "name" : "name",
+ "description" : "description"
+ } ]
+ },
+ "actionGroups" : [ {
+ "name" : "name",
+ "id" : "id"
+ }, {
+ "name" : "name",
+ "id" : "id"
+ } ],
+ "name" : "name",
+ "producer" : "producer",
+ "authorizedConsumers" : "{}",
+ "recoveryActionGroup" : {
+ "name" : "name",
+ "id" : "id"
+ },
+ "enabledInLicense" : true,
+ "id" : "id",
+ "minimumLicenseRequired" : "minimumLicenseRequired"
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/alerts/alerts/_health
+
Retrieves the health status of the alerting framework. (legacyGetAlertingHealth)
+
Deprecated in 7.13.0. Use the get alerting framework health API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "hasPermanentEncryptionKey" : true,
+ "alertingFrameworkHealth" : {
+ "executionHealth" : {
+ "status" : "ok",
+ "timestamp" : "2023-01-13T01:28:00.28Z"
+ },
+ "decryptionHealth" : {
+ "status" : "ok",
+ "timestamp" : "2023-01-13T01:28:00.28Z"
+ },
+ "readHealth" : {
+ "status" : "ok",
+ "timestamp" : "2023-01-13T01:28:00.28Z"
+ }
+ },
+ "isSufficientlySecure" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
legacyGetAlertingHealth_200_response
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute
+
Mutes an alert instance. (legacyMuteAlertInstance)
+
Deprecated in 7.13.0. Use the mute alert API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — An identifier for the alert. default: null
alertInstanceId (required)
+
+
Path Parameter — An identifier for the alert instance. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/_mute_all
+
Mutes all alert instances. (legacyMuteAllAlertInstances)
+
Deprecated in 7.13.0. Use the mute all alerts API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute
+
Unmutes an alert instance. (legacyUnmuteAlertInstance)
+
Deprecated in 7.13.0. Use the unmute alert API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — An identifier for the alert. default: null
alertInstanceId (required)
+
+
Path Parameter — An identifier for the alert instance. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all
+
Unmutes all alert instances. (legacyUnmuteAllAlertInstances)
+
Deprecated in 7.13.0. Use the unmute all alerts API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
put /s/{spaceId}/api/alerts/alert/{alertId}
+
Updates the attributes for an alert. (legacyUpdateAlert)
+
Deprecated in 7.13.0. Use the update rule API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
Consumes
+ This API call consumes the following media types via the request header:
+
+
+
Request body
+
+
+
+
Body Parameter —
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "alertTypeId" : ".index-threshold",
+ "throttle" : "throttle",
+ "updatedBy" : "elastic",
+ "executionStatus" : {
+ "lastExecutionDate" : "2022-12-06T00:13:43.89Z",
+ "status" : "ok"
+ },
+ "params" : {
+ "key" : ""
+ },
+ "enabled" : true,
+ "mutedInstanceIds" : [ "mutedInstanceIds", "mutedInstanceIds" ],
+ "tags" : [ "tags", "tags" ],
+ "createdAt" : "2022-12-05T23:36:58.284Z",
+ "schedule" : {
+ "interval" : "interval"
+ },
+ "notifyWhen" : "onActionGroupChange",
+ "createdBy" : "elastic",
+ "muteAll" : false,
+ "name" : "my alert",
+ "scheduledTaskId" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "id" : "b530fed0-74f5-11ed-9801-35303b735aef",
+ "actions" : [ "{}", "{}" ],
+ "apiKeyOwner" : "elastic",
+ "updatedAt" : "2022-12-05T23:36:58.284Z"
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
alert_response_properties
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
delete /s/{spaceId}/api/alerts/alert/{alertId}
+
Permanently removes an alert. (legaryDeleteAlert)
+
Deprecated in 7.13.0. Use the delete rule API instead. WARNING: After you delete an alert, you cannot recover it.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
alertId (required)
+
+
Path Parameter — The identifier for the alert. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
+
+
actions (optional)
+
alertTypeId
String The ID of the alert type that you want to call when the alert is scheduled to run.
+
consumer
String The name of the application that owns the alert. This name has to match the Kibana feature name, as that dictates the required role-based access control privileges.
+
enabled (optional)
Boolean Indicates if you want to run the alert on an interval basis after it is created.
+
name
String A name to reference and search.
+
notifyWhen
String The condition for throttling the notification.
+
+
onActionGroupChange
onActiveAlert
onThrottleInterval
+
params
Object The parameters to pass to the alert type executor
params
value. This will also validate against the alert type params validator, if defined.
+
schedule
+
tags (optional)
+
throttle (optional)
String How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of
10m
or
1h
will prevent it from sending 90 notifications during this period.
+
+
+
+
+
The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+
+
interval (optional)
String The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+
+
+
+
+
+
+
actions (optional)
+
name
String A name to reference and search.
+
notifyWhen
String The condition for throttling the notification.
+
+
onActionGroupChange
onActiveAlert
onThrottleInterval
+
params
Object The parameters to pass to the alert type executor
params
value. This will also validate against the alert type params validator, if defined.
+
schedule
+
tags (optional)
+
throttle (optional)
String How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of
10m
or
1h
will prevent it from sending 90 notifications during this period.
+
+
+
+
+
+
+
actionTypeId
String The identifier for the action type.
+
group
String Grouping actions is recommended for escalations for different types of alert instances. If you don't need this functionality, set it to
default
.
+
id
String The ID of the action saved object to execute.
+
params
Object The map to the
params
that the action type will receive.
params
are handled as Mustache templates and passed a default set of context.
+
+
+
+
+
The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+
+
interval (optional)
String The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+
+
@@ -1204,6 +2182,46 @@ Any modifications made to this file will be overwritten.
params (optional)
map[String, oas_any_type_not_mapped] The parameters for the action, which are sent to the connector. The
params
are handled as Mustache templates and passed a default set of context.
+
+
+
+
+
actions (optional)
+
alertTypeId (optional)
+
apiKeyOwner (optional)
+
createdAt (optional)
Date The date and time that the alert was created. format: date-time
+
createdBy (optional)
String The identifier for the user that created the alert.
+
enabled (optional)
Boolean Indicates whether the alert is currently enabled.
+
executionStatus (optional)
+
id (optional)
String The identifier for the alert.
+
muteAll (optional)
+
mutedInstanceIds (optional)
+
name (optional)
+
notifyWhen (optional)
+
params (optional)
+
schedule (optional)
+
scheduledTaskId (optional)
+
tags (optional)
+
throttle (optional)
+
updatedAt (optional)
+
updatedBy (optional)
String The identifier for the user that updated this alert most recently.
+
+
+
+
+
+
+
lastExecutionDate (optional)
+
status (optional)
+
+
+
The create rule API request body varies depending on the type of rule and actions.
@@ -1396,6 +2414,106 @@ Any modifications made to this file will be overwritten.
name (optional)
+
+
+
+
+
data (optional)
+
page (optional)
+
perPage (optional)
+
total (optional)
+
+
+
+
+
+
+
actionGroups (optional)
+
actionVariables (optional)
+
authorizedConsumers (optional)
Object The list of the plugins IDs that have access to the alert type.
+
defaultActionGroupId (optional)
String The default identifier for the alert type group.
+
enabledInLicense (optional)
Boolean Indicates whether the rule type is enabled based on the subscription.
+
id (optional)
String The unique identifier for the alert type.
+
isExportable (optional)
Boolean Indicates whether the alert type is exportable in Saved Objects Management UI.
+
minimumLicenseRequired (optional)
String The subscriptions required to use the alert type.
+
name (optional)
String The descriptive name of the alert type.
+
producer (optional)
String An identifier for the application that produces this alert type.
+
recoveryActionGroup (optional)
+
+
+
+
+
A list of action variables that the alert type makes available via context and state in action parameter templates, and a short human readable description. The Alert UI will use this information to prompt users for these variables in action parameter editors.
+
+
context (optional)
+
params (optional)
+
state (optional)
+
+
+
+
+
+
+
name (optional)
+
description (optional)
+
+
+
+
+
An action group to use when an alert instance goes from an active state to an inactive one. If it is not specified, the default recovered action group is used.
+
+
id (optional)
+
name (optional)
+
+
+
+
+
+
+
alertingFrameworkHealth (optional)
+
hasPermanentEncryptionKey (optional)
Boolean If
false
, the encrypted saved object plugin does not have a permanent encryption key.
+
isSufficientlySecure (optional)
Boolean If
false
, security is enabled but TLS is not.
+
+
+
+
+
Three substates identify the health of the alerting framework: decryptionHealth
, executionHealth
, and readHealth
.
+
+
decryptionHealth (optional)
+
executionHealth (optional)
+
readHealth (optional)
+
+
+
+
+
The timestamp and status of the alert decryption.
+
+
status (optional)
+
+
error
ok
warn
+
timestamp (optional)
+
+
+
+
+
The timestamp and status of the alert execution.
+
+
status (optional)
+
+
error
ok
warn
+
timestamp (optional)
+
+
+
+
+
The timestamp and status of the alert reading events.
+
+
status (optional)
+
+
error
ok
warn
+
timestamp (optional)
+
+
Indicates how often alerts generate actions. Valid values include: onActionGroupChange
: Actions run when the alert status changes; onActiveAlert
: Actions run when the alert becomes active and at each check interval while the rule conditions are met; onThrottleInterval
: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met.
diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.json b/x-pack/plugins/alerting/docs/openapi/bundled.json
index 9d0395f18235..a245616abe46 100644
--- a/x-pack/plugins/alerting/docs/openapi/bundled.json
+++ b/x-pack/plugins/alerting/docs/openapi/bundled.json
@@ -1243,6 +1243,1207 @@
"url": "https://localhost:5601"
}
]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}": {
+ "delete": {
+ "summary": "Permanently removes an alert.",
+ "operationId": "legaryDeleteAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the delete rule API instead. WARNING: After you delete an alert, you cannot recover it.\n",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "get": {
+ "summary": "Retrieves an alert by its identifier.",
+ "operationId": "legacyGetAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the get rule API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/alert_response_properties"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "post": {
+ "summary": "Create an alert.",
+ "operationId": "legacyCreateAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the create rule API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "An UUID v1 or v4 identifier for the alert. If this parameter is omitted, the identifier is randomly generated.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy create alert request properties",
+ "type": "object",
+ "required": [
+ "alertTypeId",
+ "consumer",
+ "name",
+ "notifyWhen",
+ "params",
+ "schedule"
+ ],
+ "properties": {
+ "actions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "actionTypeId",
+ "group",
+ "id",
+ "params"
+ ],
+ "properties": {
+ "actionTypeId": {
+ "type": "string",
+ "description": "The identifier for the action type."
+ },
+ "group": {
+ "type": "string",
+ "description": "Grouping actions is recommended for escalations for different types of alert instances. If you don't need this functionality, set it to `default`.\n"
+ },
+ "id": {
+ "type": "string",
+ "description": "The ID of the action saved object to execute."
+ },
+ "params": {
+ "type": "object",
+ "description": "The map to the `params` that the action type will receive. `params` are handled as Mustache templates and passed a default set of context.\n"
+ }
+ }
+ }
+ },
+ "alertTypeId": {
+ "type": "string",
+ "description": "The ID of the alert type that you want to call when the alert is scheduled to run."
+ },
+ "consumer": {
+ "type": "string",
+ "description": "The name of the application that owns the alert. This name has to match the Kibana feature name, as that dictates the required role-based access control privileges."
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Indicates if you want to run the alert on an interval basis after it is created."
+ },
+ "name": {
+ "type": "string",
+ "description": "A name to reference and search."
+ },
+ "notifyWhen": {
+ "type": "string",
+ "description": "The condition for throttling the notification.",
+ "enum": [
+ "onActionGroupChange",
+ "onActiveAlert",
+ "onThrottleInterval"
+ ]
+ },
+ "params": {
+ "type": "object",
+ "description": "The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined."
+ },
+ "schedule": {
+ "type": "object",
+ "description": "The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.\n",
+ "properties": {
+ "interval": {
+ "type": "string",
+ "description": "The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.",
+ "example": "10s"
+ }
+ }
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "A list of keywords to reference and search."
+ },
+ "throttle": {
+ "type": "string",
+ "description": "How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.\n"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/alert_response_properties"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "put": {
+ "summary": "Updates the attributes for an alert.",
+ "operationId": "legacyUpdateAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the update rule API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy update alert request properties",
+ "type": "object",
+ "required": [
+ "name",
+ "notifyWhen",
+ "params",
+ "schedule"
+ ],
+ "properties": {
+ "actions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "actionTypeId",
+ "group",
+ "id",
+ "params"
+ ],
+ "properties": {
+ "actionTypeId": {
+ "type": "string",
+ "description": "The identifier for the action type."
+ },
+ "group": {
+ "type": "string",
+ "description": "Grouping actions is recommended for escalations for different types of alert instances. If you don't need this functionality, set it to `default`.\n"
+ },
+ "id": {
+ "type": "string",
+ "description": "The ID of the action saved object to execute."
+ },
+ "params": {
+ "type": "object",
+ "description": "The map to the `params` that the action type will receive. `params` are handled as Mustache templates and passed a default set of context.\n"
+ }
+ }
+ }
+ },
+ "name": {
+ "type": "string",
+ "description": "A name to reference and search."
+ },
+ "notifyWhen": {
+ "type": "string",
+ "description": "The condition for throttling the notification.",
+ "enum": [
+ "onActionGroupChange",
+ "onActiveAlert",
+ "onThrottleInterval"
+ ]
+ },
+ "params": {
+ "type": "object",
+ "description": "The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined."
+ },
+ "schedule": {
+ "type": "object",
+ "description": "The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.\n",
+ "properties": {
+ "interval": {
+ "type": "string",
+ "description": "The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.",
+ "example": "1d"
+ }
+ }
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "A list of keywords to reference and search."
+ },
+ "throttle": {
+ "type": "string",
+ "description": "How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.\n"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/alert_response_properties"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/_disable": {
+ "post": {
+ "summary": "Disables an alert.",
+ "operationId": "legacyDisableAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the disable rule API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/_enable": {
+ "post": {
+ "summary": "Enables an alert.",
+ "operationId": "legacyEnableAlert",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the enable rule API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/_mute_all": {
+ "post": {
+ "summary": "Mutes all alert instances.",
+ "operationId": "legacyMuteAllAlertInstances",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the mute all alerts API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all": {
+ "post": {
+ "summary": "Unmutes all alert instances.",
+ "operationId": "legacyUnmuteAllAlertInstances",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the unmute all alerts API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "The identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alerts/_find": {
+ "get": {
+ "summary": "Retrieves a paginated set of alerts.",
+ "operationId": "legacyFindAlerts",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the find rules API instead. NOTE: Alert `params` are stored as a flattened field type and analyzed as keywords. As alerts change in Kibana, the results on each page of the response also change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.\n",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "name": "default_search_operator",
+ "in": "query",
+ "description": "The default operator to use for the `simple_query_string`.",
+ "schema": {
+ "type": "string",
+ "default": "OR"
+ },
+ "example": "OR"
+ },
+ {
+ "name": "fields",
+ "in": "query",
+ "description": "The fields to return in the `attributes` key of the response.",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "name": "filter",
+ "in": "query",
+ "description": "A KQL string that you filter with an attribute from your saved object. It should look like `savedObjectType.attributes.title: \"myTitle\"`. However, if you used a direct attribute of a saved object, such as `updatedAt`, you must define your filter, for example, `savedObjectType.updatedAt > 2018-12-22`.\n",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "has_reference",
+ "in": "query",
+ "description": "Filters the rules that have a relation with the reference objects with a specific type and identifier.",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "name": "page",
+ "in": "query",
+ "description": "The page number to return.",
+ "schema": {
+ "type": "integer",
+ "default": 1
+ },
+ "example": 1
+ },
+ {
+ "name": "per_page",
+ "in": "query",
+ "description": "The number of alerts to return per page.",
+ "schema": {
+ "type": "integer",
+ "default": 20
+ },
+ "example": 20
+ },
+ {
+ "name": "search",
+ "in": "query",
+ "description": "An Elasticsearch `simple_query_string` query that filters the alerts in the response.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "search_fields",
+ "in": "query",
+ "description": "The fields to perform the `simple_query_string` parsed query against.",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "name": "sort_field",
+ "in": "query",
+ "description": "Determines which field is used to sort the results. The field must exist in the `attributes` key of the response.\n",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "sort_order",
+ "in": "query",
+ "description": "Determines the sort order.",
+ "schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ],
+ "default": "desc"
+ },
+ "example": "asc"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/alert_response_properties"
+ }
+ },
+ "page": {
+ "type": "integer"
+ },
+ "perPage": {
+ "type": "integer"
+ },
+ "total": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alerts/_health": {
+ "get": {
+ "summary": "Retrieves the health status of the alerting framework.",
+ "operationId": "legacyGetAlertingHealth",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the get alerting framework health API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alertingFrameworkHealth": {
+ "type": "object",
+ "description": "Three substates identify the health of the alerting framework: `decryptionHealth`, `executionHealth`, and `readHealth`.\n",
+ "properties": {
+ "decryptionHealth": {
+ "type": "object",
+ "description": "The timestamp and status of the alert decryption.",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "ok",
+ "enum": [
+ "error",
+ "ok",
+ "warn"
+ ]
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2023-01-13T01:28:00.280Z"
+ }
+ }
+ },
+ "executionHealth": {
+ "type": "object",
+ "description": "The timestamp and status of the alert execution.",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "ok",
+ "enum": [
+ "error",
+ "ok",
+ "warn"
+ ]
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2023-01-13T01:28:00.280Z"
+ }
+ }
+ },
+ "readHealth": {
+ "type": "object",
+ "description": "The timestamp and status of the alert reading events.",
+ "properties": {
+ "status": {
+ "type": "string",
+ "example": "ok",
+ "enum": [
+ "error",
+ "ok",
+ "warn"
+ ]
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2023-01-13T01:28:00.280Z"
+ }
+ }
+ }
+ }
+ },
+ "hasPermanentEncryptionKey": {
+ "type": "boolean",
+ "description": "If `false`, the encrypted saved object plugin does not have a permanent encryption key.",
+ "example": true
+ },
+ "isSufficientlySecure": {
+ "type": "boolean",
+ "description": "If `false`, security is enabled but TLS is not.",
+ "example": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alerts/list_alert_types": {
+ "get": {
+ "summary": "Retrieves a list of alert types.",
+ "operationId": "legacyGetAlertTypes",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the get rule types API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "actionGroups": {
+ "description": "An explicit list of groups for which the alert type can schedule actions, each with the action group's unique ID and human readable name. Alert actions validation uses this configuration to ensure that groups are valid.\n",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "actionVariables": {
+ "description": "A list of action variables that the alert type makes available via context and state in action parameter templates, and a short human readable description. The Alert UI will use this information to prompt users for these variables in action parameter editors.\n",
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "params": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "state": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "authorizedConsumers": {
+ "description": "The list of the plugins IDs that have access to the alert type.",
+ "type": "object"
+ },
+ "defaultActionGroupId": {
+ "description": "The default identifier for the alert type group.",
+ "type": "string"
+ },
+ "enabledInLicense": {
+ "description": "Indicates whether the rule type is enabled based on the subscription.",
+ "type": "boolean"
+ },
+ "id": {
+ "description": "The unique identifier for the alert type.",
+ "type": "string"
+ },
+ "isExportable": {
+ "description": "Indicates whether the alert type is exportable in Saved Objects Management UI.",
+ "type": "boolean"
+ },
+ "minimumLicenseRequired": {
+ "description": "The subscriptions required to use the alert type.",
+ "type": "string"
+ },
+ "name": {
+ "description": "The descriptive name of the alert type.",
+ "type": "string"
+ },
+ "producer": {
+ "description": "An identifier for the application that produces this alert type.",
+ "type": "string"
+ },
+ "recoveryActionGroup": {
+ "description": "An action group to use when an alert instance goes from an active state to an inactive one. If it is not specified, the default recovered action group is used.\n",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute": {
+ "post": {
+ "summary": "Mutes an alert instance.",
+ "operationId": "legacyMuteAlertInstance",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the mute alert API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "An identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ },
+ {
+ "in": "path",
+ "name": "alertInstanceId",
+ "description": "An identifier for the alert instance.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute": {
+ "post": {
+ "summary": "Unmutes an alert instance.",
+ "operationId": "legacyUnmuteAlertInstance",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the unmute alert API instead.",
+ "tags": [
+ "alerting"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ },
+ {
+ "in": "path",
+ "name": "alertId",
+ "description": "An identifier for the alert.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "41893910-6bca-11eb-9e0d-85d233e3ee35"
+ }
+ },
+ {
+ "in": "path",
+ "name": "alertInstanceId",
+ "description": "An identifier for the alert instance.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
}
},
"components": {
@@ -1674,6 +2875,118 @@
"$ref": "#/components/schemas/throttle"
}
}
+ },
+ "alert_response_properties": {
+ "title": "Legacy alert response properties",
+ "type": "object",
+ "properties": {
+ "actions": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "alertTypeId": {
+ "type": "string",
+ "example": ".index-threshold"
+ },
+ "apiKeyOwner": {
+ "type": "string",
+ "nullable": true,
+ "example": "elastic"
+ },
+ "createdAt": {
+ "type": "string",
+ "description": "The date and time that the alert was created.",
+ "format": "date-time",
+ "example": "2022-12-05T23:36:58.284Z"
+ },
+ "createdBy": {
+ "type": "string",
+ "description": "The identifier for the user that created the alert.",
+ "example": "elastic"
+ },
+ "enabled": {
+ "type": "boolean",
+ "description": "Indicates whether the alert is currently enabled.",
+ "example": true
+ },
+ "executionStatus": {
+ "type": "object",
+ "properties": {
+ "lastExecutionDate": {
+ "type": "string",
+ "format": "date-time",
+ "example": "2022-12-06T00:13:43.890Z"
+ },
+ "status": {
+ "type": "string",
+ "example": "ok"
+ }
+ }
+ },
+ "id": {
+ "type": "string",
+ "description": "The identifier for the alert.",
+ "example": "b530fed0-74f5-11ed-9801-35303b735aef"
+ },
+ "muteAll": {
+ "type": "boolean",
+ "example": false
+ },
+ "mutedInstanceIds": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ }
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the alert.",
+ "example": "my alert"
+ },
+ "notifyWhen": {
+ "type": "string",
+ "example": "onActionGroupChange"
+ },
+ "params": {
+ "type": "object",
+ "additionalProperties": true
+ },
+ "schedule": {
+ "type": "object",
+ "properties": {
+ "interval": {
+ "type": "string"
+ }
+ }
+ },
+ "scheduledTaskId": {
+ "type": "string",
+ "example": "b530fed0-74f5-11ed-9801-35303b735aef"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "throttle": {
+ "type": "string",
+ "nullable": true
+ },
+ "updatedAt": {
+ "type": "string",
+ "example": "2022-12-05T23:36:58.284Z"
+ },
+ "updatedBy": {
+ "type": "string",
+ "description": "The identifier for the user that updated this alert most recently.",
+ "nullable": true,
+ "example": "elastic"
+ }
+ }
}
},
"examples": {
diff --git a/x-pack/plugins/alerting/docs/openapi/bundled.yaml b/x-pack/plugins/alerting/docs/openapi/bundled.yaml
index 6515377574aa..ad2ea6a72f15 100644
--- a/x-pack/plugins/alerting/docs/openapi/bundled.yaml
+++ b/x-pack/plugins/alerting/docs/openapi/bundled.yaml
@@ -766,6 +766,781 @@ paths:
- url: https://localhost:5601
servers:
- url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}:
+ delete:
+ summary: Permanently removes an alert.
+ operationId: legaryDeleteAlert
+ deprecated: true
+ description: |
+ Deprecated in 7.13.0. Use the delete rule API instead. WARNING: After you delete an alert, you cannot recover it.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ get:
+ summary: Retrieves an alert by its identifier.
+ operationId: legacyGetAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/alert_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ post:
+ summary: Create an alert.
+ operationId: legacyCreateAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the create rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - in: path
+ name: alertId
+ description: An UUID v1 or v4 identifier for the alert. If this parameter is omitted, the identifier is randomly generated.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - $ref: '#/components/parameters/space_id'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy create alert request properties
+ type: object
+ required:
+ - alertTypeId
+ - consumer
+ - name
+ - notifyWhen
+ - params
+ - schedule
+ properties:
+ actions:
+ type: array
+ items:
+ type: object
+ required:
+ - actionTypeId
+ - group
+ - id
+ - params
+ properties:
+ actionTypeId:
+ type: string
+ description: The identifier for the action type.
+ group:
+ type: string
+ description: |
+ Grouping actions is recommended for escalations for different types of alert instances. If you don't need this functionality, set it to `default`.
+ id:
+ type: string
+ description: The ID of the action saved object to execute.
+ params:
+ type: object
+ description: |
+ The map to the `params` that the action type will receive. `params` are handled as Mustache templates and passed a default set of context.
+ alertTypeId:
+ type: string
+ description: The ID of the alert type that you want to call when the alert is scheduled to run.
+ consumer:
+ type: string
+ description: The name of the application that owns the alert. This name has to match the Kibana feature name, as that dictates the required role-based access control privileges.
+ enabled:
+ type: boolean
+ description: Indicates if you want to run the alert on an interval basis after it is created.
+ name:
+ type: string
+ description: A name to reference and search.
+ notifyWhen:
+ type: string
+ description: The condition for throttling the notification.
+ enum:
+ - onActionGroupChange
+ - onActiveAlert
+ - onThrottleInterval
+ params:
+ type: object
+ description: The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined.
+ schedule:
+ type: object
+ description: |
+ The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+ properties:
+ interval:
+ type: string
+ description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+ example: 10s
+ tags:
+ type: array
+ items:
+ type: string
+ description: A list of keywords to reference and search.
+ throttle:
+ type: string
+ description: |
+ How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/alert_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ put:
+ summary: Updates the attributes for an alert.
+ operationId: legacyUpdateAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the update rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy update alert request properties
+ type: object
+ required:
+ - name
+ - notifyWhen
+ - params
+ - schedule
+ properties:
+ actions:
+ type: array
+ items:
+ type: object
+ required:
+ - actionTypeId
+ - group
+ - id
+ - params
+ properties:
+ actionTypeId:
+ type: string
+ description: The identifier for the action type.
+ group:
+ type: string
+ description: |
+ Grouping actions is recommended for escalations for different types of alert instances. If you don't need this functionality, set it to `default`.
+ id:
+ type: string
+ description: The ID of the action saved object to execute.
+ params:
+ type: object
+ description: |
+ The map to the `params` that the action type will receive. `params` are handled as Mustache templates and passed a default set of context.
+ name:
+ type: string
+ description: A name to reference and search.
+ notifyWhen:
+ type: string
+ description: The condition for throttling the notification.
+ enum:
+ - onActionGroupChange
+ - onActiveAlert
+ - onThrottleInterval
+ params:
+ type: object
+ description: The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined.
+ schedule:
+ type: object
+ description: |
+ The schedule specifying when this alert should be run. A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+ properties:
+ interval:
+ type: string
+ description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+ example: 1d
+ tags:
+ type: array
+ items:
+ type: string
+ description: A list of keywords to reference and search.
+ throttle:
+ type: string
+ description: |
+ How often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes, setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/alert_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/_disable:
+ post:
+ summary: Disables an alert.
+ operationId: legacyDisableAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the disable rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/_enable:
+ post:
+ summary: Enables an alert.
+ operationId: legacyEnableAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the enable rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/_mute_all:
+ post:
+ summary: Mutes all alert instances.
+ operationId: legacyMuteAllAlertInstances
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the mute all alerts API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all:
+ post:
+ summary: Unmutes all alert instances.
+ operationId: legacyUnmuteAllAlertInstances
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the unmute all alerts API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alerts/_find:
+ get:
+ summary: Retrieves a paginated set of alerts.
+ operationId: legacyFindAlerts
+ deprecated: true
+ description: |
+ Deprecated in 7.13.0. Use the find rules API instead. NOTE: Alert `params` are stored as a flattened field type and analyzed as keywords. As alerts change in Kibana, the results on each page of the response also change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ - name: default_search_operator
+ in: query
+ description: The default operator to use for the `simple_query_string`.
+ schema:
+ type: string
+ default: OR
+ example: OR
+ - name: fields
+ in: query
+ description: The fields to return in the `attributes` key of the response.
+ schema:
+ type: array
+ items:
+ type: string
+ - name: filter
+ in: query
+ description: |
+ A KQL string that you filter with an attribute from your saved object. It should look like `savedObjectType.attributes.title: "myTitle"`. However, if you used a direct attribute of a saved object, such as `updatedAt`, you must define your filter, for example, `savedObjectType.updatedAt > 2018-12-22`.
+ schema:
+ type: string
+ - name: has_reference
+ in: query
+ description: Filters the rules that have a relation with the reference objects with a specific type and identifier.
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ type:
+ type: string
+ - name: page
+ in: query
+ description: The page number to return.
+ schema:
+ type: integer
+ default: 1
+ example: 1
+ - name: per_page
+ in: query
+ description: The number of alerts to return per page.
+ schema:
+ type: integer
+ default: 20
+ example: 20
+ - name: search
+ in: query
+ description: An Elasticsearch `simple_query_string` query that filters the alerts in the response.
+ schema:
+ type: string
+ - name: search_fields
+ in: query
+ description: The fields to perform the `simple_query_string` parsed query against.
+ schema:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ - name: sort_field
+ in: query
+ description: |
+ Determines which field is used to sort the results. The field must exist in the `attributes` key of the response.
+ schema:
+ type: string
+ - name: sort_order
+ in: query
+ description: Determines the sort order.
+ schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ default: desc
+ example: asc
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/alert_response_properties'
+ page:
+ type: integer
+ perPage:
+ type: integer
+ total:
+ type: integer
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alerts/_health:
+ get:
+ summary: Retrieves the health status of the alerting framework.
+ operationId: legacyGetAlertingHealth
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get alerting framework health API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ alertingFrameworkHealth:
+ type: object
+ description: |
+ Three substates identify the health of the alerting framework: `decryptionHealth`, `executionHealth`, and `readHealth`.
+ properties:
+ decryptionHealth:
+ type: object
+ description: The timestamp and status of the alert decryption.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: '2023-01-13T01:28:00.280Z'
+ executionHealth:
+ type: object
+ description: The timestamp and status of the alert execution.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: '2023-01-13T01:28:00.280Z'
+ readHealth:
+ type: object
+ description: The timestamp and status of the alert reading events.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: '2023-01-13T01:28:00.280Z'
+ hasPermanentEncryptionKey:
+ type: boolean
+ description: If `false`, the encrypted saved object plugin does not have a permanent encryption key.
+ example: true
+ isSufficientlySecure:
+ type: boolean
+ description: If `false`, security is enabled but TLS is not.
+ example: true
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alerts/list_alert_types:
+ get:
+ summary: Retrieves a list of alert types.
+ operationId: legacyGetAlertTypes
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get rule types API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ actionGroups:
+ description: |
+ An explicit list of groups for which the alert type can schedule actions, each with the action group's unique ID and human readable name. Alert actions validation uses this configuration to ensure that groups are valid.
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ actionVariables:
+ description: |
+ A list of action variables that the alert type makes available via context and state in action parameter templates, and a short human readable description. The Alert UI will use this information to prompt users for these variables in action parameter editors.
+ type: object
+ properties:
+ context:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ params:
+ type: array
+ items:
+ type: object
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ state:
+ type: array
+ items:
+ type: object
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ authorizedConsumers:
+ description: The list of the plugins IDs that have access to the alert type.
+ type: object
+ defaultActionGroupId:
+ description: The default identifier for the alert type group.
+ type: string
+ enabledInLicense:
+ description: Indicates whether the rule type is enabled based on the subscription.
+ type: boolean
+ id:
+ description: The unique identifier for the alert type.
+ type: string
+ isExportable:
+ description: Indicates whether the alert type is exportable in Saved Objects Management UI.
+ type: boolean
+ minimumLicenseRequired:
+ description: The subscriptions required to use the alert type.
+ type: string
+ name:
+ description: The descriptive name of the alert type.
+ type: string
+ producer:
+ description: An identifier for the application that produces this alert type.
+ type: string
+ recoveryActionGroup:
+ description: |
+ An action group to use when an alert instance goes from an active state to an inactive one. If it is not specified, the default recovered action group is used.
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute:
+ post:
+ summary: Mutes an alert instance.
+ operationId: legacyMuteAlertInstance
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the mute alert API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: An identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - in: path
+ name: alertInstanceId
+ description: An identifier for the alert instance.
+ required: true
+ schema:
+ type: string
+ example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute:
+ post:
+ summary: Unmutes an alert instance.
+ operationId: legacyUnmuteAlertInstance
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the unmute alert API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ - in: path
+ name: alertId
+ description: An identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - in: path
+ name: alertInstanceId
+ description: An identifier for the alert instance.
+ required: true
+ schema:
+ type: string
+ example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
components:
securitySchemes:
basicAuth:
@@ -1096,6 +1871,89 @@ components:
$ref: '#/components/schemas/tags'
throttle:
$ref: '#/components/schemas/throttle'
+ alert_response_properties:
+ title: Legacy alert response properties
+ type: object
+ properties:
+ actions:
+ type: array
+ items:
+ type: object
+ alertTypeId:
+ type: string
+ example: .index-threshold
+ apiKeyOwner:
+ type: string
+ nullable: true
+ example: elastic
+ createdAt:
+ type: string
+ description: The date and time that the alert was created.
+ format: date-time
+ example: '2022-12-05T23:36:58.284Z'
+ createdBy:
+ type: string
+ description: The identifier for the user that created the alert.
+ example: elastic
+ enabled:
+ type: boolean
+ description: Indicates whether the alert is currently enabled.
+ example: true
+ executionStatus:
+ type: object
+ properties:
+ lastExecutionDate:
+ type: string
+ format: date-time
+ example: '2022-12-06T00:13:43.890Z'
+ status:
+ type: string
+ example: ok
+ id:
+ type: string
+ description: The identifier for the alert.
+ example: b530fed0-74f5-11ed-9801-35303b735aef
+ muteAll:
+ type: boolean
+ example: false
+ mutedInstanceIds:
+ type: array
+ nullable: true
+ items:
+ type: string
+ name:
+ type: string
+ description: The name of the alert.
+ example: my alert
+ notifyWhen:
+ type: string
+ example: onActionGroupChange
+ params:
+ type: object
+ additionalProperties: true
+ schedule:
+ type: object
+ properties:
+ interval:
+ type: string
+ scheduledTaskId:
+ type: string
+ example: b530fed0-74f5-11ed-9801-35303b735aef
+ tags:
+ type: array
+ items:
+ type: string
+ throttle:
+ type: string
+ nullable: true
+ updatedAt:
+ type: string
+ example: '2022-12-05T23:36:58.284Z'
+ updatedBy:
+ type: string
+ description: The identifier for the user that updated this alert most recently.
+ nullable: true
+ example: elastic
examples:
get_rule_response:
summary: The get rule API returns a JSON object that contains details about the rule.
diff --git a/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml b/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml
new file mode 100644
index 000000000000..06fa627311e7
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/components/schemas/alert_response_properties.yaml
@@ -0,0 +1,82 @@
+title: Legacy alert response properties
+type: object
+properties:
+ actions:
+ type: array
+ items:
+ type: object
+ alertTypeId:
+ type: string
+ example: ".index-threshold"
+ apiKeyOwner:
+ type: string
+ nullable: true
+ example: elastic
+ createdAt:
+ type: string
+ description: The date and time that the alert was created.
+ format: date-time
+ example: '2022-12-05T23:36:58.284Z'
+ createdBy:
+ type: string
+ description: The identifier for the user that created the alert.
+ example: elastic
+ enabled:
+ type: boolean
+ description: Indicates whether the alert is currently enabled.
+ example: true
+ executionStatus:
+ type: object
+ properties:
+ lastExecutionDate:
+ type: string
+ format: date-time
+ example: '2022-12-06T00:13:43.890Z'
+ status:
+ type: string
+ example: ok
+ id:
+ type: string
+ description: The identifier for the alert.
+ example: b530fed0-74f5-11ed-9801-35303b735aef
+ muteAll:
+ type: boolean
+ example: false
+ mutedInstanceIds:
+ type: array
+ nullable: true
+ items:
+ type: string
+ name:
+ type: string
+ description: The name of the alert.
+ example: my alert
+ notifyWhen:
+ type: string
+ example: onActionGroupChange
+ params:
+ type: object
+ additionalProperties: true
+ schedule:
+ type: object
+ properties:
+ interval:
+ type: string
+ scheduledTaskId:
+ type: string
+ example: b530fed0-74f5-11ed-9801-35303b735aef
+ tags:
+ type: array
+ items:
+ type: string
+ throttle:
+ type: string
+ nullable: true
+ updatedAt:
+ type: string
+ example: '2022-12-05T23:36:58.284Z'
+ updatedBy:
+ type: string
+ description: The identifier for the user that updated this alert most recently.
+ nullable: true
+ example: elastic
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml
index 3b141954b30d..52b1babd68c8 100644
--- a/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml
+++ b/x-pack/plugins/alerting/docs/openapi/entrypoint.yaml
@@ -35,28 +35,27 @@ paths:
$ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@alert@{alertid}@_mute.yaml'
'/s/{spaceId}/api/alerting/rule/{ruleId}/alert/{alertId}/_unmute':
$ref: 'paths/s@{spaceid}@api@alerting@rule@{ruleid}@alert@{alertid}@_unmute.yaml'
-
# Deprecated APIs
-# '/s/{spaceId}/api/alerts/alert/{alertId}':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/_disable':
-# $ref: 'paths/s@{spaceid}@api@alertss@alert@{alertid}@_disable.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/_enable':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/_mute_all':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml'
-# '/s/{spaceId}/api/alerts/alerts/_find':
-# $ref: 'paths/s@{spaceid}@api@alerts@_find.yaml'
-# '/s/{spaceId}/api/alerts/alerts/_health':
-# $ref: 'paths/s@{spaceid}@api@alerts@_health.yaml'
-# '/s/{spaceId}/api/alerts/alerts/list_alert_types':
-# $ref: 'paths/s@{spaceid}@api@alerts@list_alert_types.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml'
-# '/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute':
-# $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/_disable':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/_enable':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/_mute_all':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/_unmute_all':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml'
+ '/s/{spaceId}/api/alerts/alerts/_find':
+ $ref: 'paths/s@{spaceid}@api@alerts@_find.yaml'
+ '/s/{spaceId}/api/alerts/alerts/_health':
+ $ref: 'paths/s@{spaceid}@api@alerts@_health.yaml'
+ '/s/{spaceId}/api/alerts/alerts/list_alert_types':
+ $ref: 'paths/s@{spaceid}@api@alerts@list_alert_types.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_mute':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml'
+ '/s/{spaceId}/api/alerts/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute':
+ $ref: 'paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml'
components:
securitySchemes:
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml
new file mode 100644
index 000000000000..bc8e2ecae490
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_find.yaml
@@ -0,0 +1,120 @@
+get:
+ summary: Retrieves a paginated set of alerts.
+ operationId: legacyFindAlerts
+ deprecated: true
+ description: >
+ Deprecated in 7.13.0. Use the find rules API instead.
+ NOTE: Alert `params` are stored as a flattened field type and analyzed as keywords.
+ As alerts change in Kibana, the results on each page of the response also change.
+ Use the find API for traditional paginated results, but avoid using it to export large amounts of data.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ - name: default_search_operator
+ in: query
+ description: The default operator to use for the `simple_query_string`.
+ schema:
+ type: string
+ default: OR
+ example: OR
+ - name: fields
+ in: query
+ description: The fields to return in the `attributes` key of the response.
+ schema:
+ type: array
+ items:
+ type: string
+ - name: filter
+ in: query
+ description: >
+ A KQL string that you filter with an attribute from your saved object.
+ It should look like `savedObjectType.attributes.title: "myTitle"`.
+ However, if you used a direct attribute of a saved object, such as
+ `updatedAt`, you must define your filter, for example,
+ `savedObjectType.updatedAt > 2018-12-22`.
+ schema:
+ type: string
+ - name: has_reference
+ in: query
+ description: Filters the rules that have a relation with the reference objects with a specific type and identifier.
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ type:
+ type: string
+ - name: page
+ in: query
+ description: The page number to return.
+ schema:
+ type: integer
+ default: 1
+ example: 1
+ - name: per_page
+ in: query
+ description: The number of alerts to return per page.
+ schema:
+ type: integer
+ default: 20
+ example: 20
+ - name: search
+ in: query
+ description: An Elasticsearch `simple_query_string` query that filters the alerts in the response.
+ schema:
+ type: string
+ - name: search_fields
+ in: query
+ description: The fields to perform the `simple_query_string` parsed query against.
+ schema:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ - name: sort_field
+ in: query
+ description: >
+ Determines which field is used to sort the results. The field must exist
+ in the `attributes` key of the response.
+ schema:
+ type: string
+ - name: sort_order
+ in: query
+ description: Determines the sort order.
+ schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ default: desc
+ example: asc
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '../components/schemas/alert_response_properties.yaml'
+ page:
+ type: integer
+ perPage:
+ type: integer
+ total:
+ type: integer
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml
new file mode 100644
index 000000000000..2b9cd953596b
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@_health.yaml
@@ -0,0 +1,83 @@
+get:
+ summary: Retrieves the health status of the alerting framework.
+ operationId: legacyGetAlertingHealth
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get alerting framework health API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ alertingFrameworkHealth:
+ type: object
+ description: >
+ Three substates identify the health of the alerting framework: `decryptionHealth`, `executionHealth`, and `readHealth`.
+ properties:
+ decryptionHealth:
+ type: object
+ description: The timestamp and status of the alert decryption.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: "2023-01-13T01:28:00.280Z"
+ executionHealth:
+ type: object
+ description: The timestamp and status of the alert execution.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: "2023-01-13T01:28:00.280Z"
+ readHealth:
+ type: object
+ description: The timestamp and status of the alert reading events.
+ properties:
+ status:
+ type: string
+ example: ok
+ enum:
+ - error
+ - ok
+ - warn
+ timestamp:
+ type: string
+ format: date-time
+ example: "2023-01-13T01:28:00.280Z"
+ hasPermanentEncryptionKey:
+ type: boolean
+ description: If `false`, the encrypted saved object plugin does not have a permanent encryption key.
+ example: true
+ isSufficientlySecure:
+ type: boolean
+ description: If `false`, security is enabled but TLS is not.
+ example: true
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml
new file mode 100644
index 000000000000..7976041b1448
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}.yaml
@@ -0,0 +1,290 @@
+delete:
+ summary: Permanently removes an alert.
+ operationId: legaryDeleteAlert
+ deprecated: true
+ description: >
+ Deprecated in 7.13.0. Use the delete rule API instead.
+ WARNING: After you delete an alert, you cannot recover it.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+get:
+ summary: Retrieves an alert by its identifier.
+ operationId: legacyGetAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/alert_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+post:
+ summary: Create an alert.
+ operationId: legacyCreateAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the create rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - in: path
+ name: alertId
+ description: An UUID v1 or v4 identifier for the alert. If this parameter is omitted, the identifier is randomly generated.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - $ref: '../components/parameters/space_id.yaml'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy create alert request properties
+ type: object
+ required:
+ - alertTypeId
+ - consumer
+ - name
+ - notifyWhen
+ - params
+ - schedule
+ properties:
+ actions:
+ type: array
+ items:
+ type: object
+ required:
+ - actionTypeId
+ - group
+ - id
+ - params
+ properties:
+ actionTypeId:
+ type: string
+ description: The identifier for the action type.
+ group:
+ type: string
+ description: >
+ Grouping actions is recommended for escalations for different types of alert instances.
+ If you don't need this functionality, set it to `default`.
+ id:
+ type: string
+ description: The ID of the action saved object to execute.
+ params:
+ type: object
+ description: >
+ The map to the `params` that the action type will receive.
+ `params` are handled as Mustache templates and passed a default set of context.
+ alertTypeId:
+ type: string
+ description: The ID of the alert type that you want to call when the alert is scheduled to run.
+ consumer:
+ type: string
+ description: The name of the application that owns the alert. This name has to match the Kibana feature name, as that dictates the required role-based access control privileges.
+ enabled:
+ type: boolean
+ description: Indicates if you want to run the alert on an interval basis after it is created.
+ name:
+ type: string
+ description: A name to reference and search.
+ notifyWhen:
+ type: string
+ description: The condition for throttling the notification.
+ enum:
+ - onActionGroupChange
+ - onActiveAlert
+ - onThrottleInterval
+ params:
+ type: object
+ description: The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined.
+ schedule:
+ type: object
+ description: >
+ The schedule specifying when this alert should be run.
+ A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+ properties:
+ interval:
+ type: string
+ description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+ example: "10s"
+ tags:
+ type: array
+ items:
+ type: string
+ description: A list of keywords to reference and search.
+ throttle:
+ type: string
+ description: >
+ How often this alert should fire the same actions.
+ This will prevent the alert from sending out the same notification over and over.
+ For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes,
+ setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/alert_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+put:
+ summary: Updates the attributes for an alert.
+ operationId: legacyUpdateAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the update rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy update alert request properties
+ type: object
+ required:
+ - name
+ - notifyWhen
+ - params
+ - schedule
+ properties:
+ actions:
+ type: array
+ items:
+ type: object
+ required:
+ - actionTypeId
+ - group
+ - id
+ - params
+ properties:
+ actionTypeId:
+ type: string
+ description: The identifier for the action type.
+ group:
+ type: string
+ description: >
+ Grouping actions is recommended for escalations for different types of alert instances.
+ If you don't need this functionality, set it to `default`.
+ id:
+ type: string
+ description: The ID of the action saved object to execute.
+ params:
+ type: object
+ description: >
+ The map to the `params` that the action type will receive.
+ `params` are handled as Mustache templates and passed a default set of context.
+ name:
+ type: string
+ description: A name to reference and search.
+ notifyWhen:
+ type: string
+ description: The condition for throttling the notification.
+ enum:
+ - onActionGroupChange
+ - onActiveAlert
+ - onThrottleInterval
+ params:
+ type: object
+ description: The parameters to pass to the alert type executor `params` value. This will also validate against the alert type params validator, if defined.
+ schedule:
+ type: object
+ description: >
+ The schedule specifying when this alert should be run.
+ A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule.
+ properties:
+ interval:
+ type: string
+ description: The interval format specifies the interval in seconds, minutes, hours or days at which the alert should execute.
+ example: "1d"
+ tags:
+ type: array
+ items:
+ type: string
+ description: A list of keywords to reference and search.
+ throttle:
+ type: string
+ description: >
+ How often this alert should fire the same actions.
+ This will prevent the alert from sending out the same notification over and over.
+ For example, if an alert with a schedule of 1 minute stays in a triggered state for 90 minutes,
+ setting a throttle of `10m` or `1h` will prevent it from sending 90 notifications during this period.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/alert_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml
new file mode 100644
index 000000000000..09e27e410db0
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_disable.yaml
@@ -0,0 +1,30 @@
+post:
+ summary: Disables an alert.
+ operationId: legacyDisableAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the disable rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml
new file mode 100644
index 000000000000..8a8af9f7a748
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_enable.yaml
@@ -0,0 +1,30 @@
+post:
+ summary: Enables an alert.
+ operationId: legacyEnableAlert
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the enable rule API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml
new file mode 100644
index 000000000000..48c55a553e19
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_mute_all.yaml
@@ -0,0 +1,30 @@
+post:
+ summary: Mutes all alert instances.
+ operationId: legacyMuteAllAlertInstances
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the mute all alerts API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml
new file mode 100644
index 000000000000..8749d657b2de
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@_unmute_all.yaml
@@ -0,0 +1,30 @@
+post:
+ summary: Unmutes all alert instances.
+ operationId: legacyUnmuteAllAlertInstances
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the unmute all alerts API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: The identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml
new file mode 100644
index 000000000000..ca407b420ece
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_mute.yaml
@@ -0,0 +1,37 @@
+post:
+ summary: Mutes an alert instance.
+ operationId: legacyMuteAlertInstance
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the mute alert API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: An identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - in: path
+ name: alertInstanceId
+ description: An identifier for the alert instance.
+ required: true
+ schema:
+ type: string
+ example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml
new file mode 100644
index 000000000000..85d6ef9c4384
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@alert@{alertid}@alert_instance@{alertinstanceid}@_unmute.yaml
@@ -0,0 +1,37 @@
+post:
+ summary: Unmutes an alert instance.
+ operationId: legacyUnmuteAlertInstance
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the unmute alert API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: ../components/headers/kbn_xsrf.yaml
+ - $ref: '../components/parameters/space_id.yaml'
+ - in: path
+ name: alertId
+ description: An identifier for the alert.
+ required: true
+ schema:
+ type: string
+ example: 41893910-6bca-11eb-9e0d-85d233e3ee35
+ - in: path
+ name: alertInstanceId
+ description: An identifier for the alert instance.
+ required: true
+ schema:
+ type: string
+ example: dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
diff --git a/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@list_alert_types.yaml b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@list_alert_types.yaml
new file mode 100644
index 000000000000..1f23d5c94e29
--- /dev/null
+++ b/x-pack/plugins/alerting/docs/openapi/paths/s@{spaceid}@api@alerts@list_alert_types.yaml
@@ -0,0 +1,111 @@
+get:
+ summary: Retrieves a list of alert types.
+ operationId: legacyGetAlertTypes
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get rule types API instead.
+ tags:
+ - alerting
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ actionGroups:
+ description: >
+ An explicit list of groups for which the alert type can
+ schedule actions, each with the action group's unique ID and
+ human readable name. Alert actions validation uses this
+ configuration to ensure that groups are valid.
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ actionVariables:
+ description: >
+ A list of action variables that the alert type makes available
+ via context and state in action parameter templates, and a
+ short human readable description. The Alert UI will use this
+ information to prompt users for these variables in action
+ parameter editors.
+ type: object
+ properties:
+ context:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ params:
+ type: array
+ items:
+ type: object
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ state:
+ type: array
+ items:
+ type: object
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ authorizedConsumers:
+ description: The list of the plugins IDs that have access to the alert type.
+ type: object
+ defaultActionGroupId:
+ description: The default identifier for the alert type group.
+ type: string
+ enabledInLicense:
+ description: Indicates whether the rule type is enabled based on the subscription.
+ type: boolean
+ id:
+ description: The unique identifier for the alert type.
+ type: string
+ isExportable:
+ description: Indicates whether the alert type is exportable in Saved Objects Management UI.
+ type: boolean
+ minimumLicenseRequired:
+ description: The subscriptions required to use the alert type.
+ type: string
+ name:
+ description: The descriptive name of the alert type.
+ type: string
+ producer:
+ description: An identifier for the application that produces this alert type.
+ type: string
+ recoveryActionGroup:
+ description: >
+ An action group to use when an alert instance goes from an active state to an inactive one.
+ If it is not specified, the default recovered action group is used.
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+servers:
+ - url: https://localhost:5601
\ No newline at end of file
From 83c382b0887754376840131a2b8e8208d93f855d Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Thu, 2 Feb 2023 07:22:32 -0800
Subject: [PATCH 16/35] [DOCS] Add specifications for deprecated connector APIs
(#149637)
---
.../connector-apis-passthru.asciidoc | 520 +++++++++++++++++-
.../plugins/actions/docs/openapi/bundled.json | 514 ++++++++++++++++-
.../plugins/actions/docs/openapi/bundled.yaml | 314 ++++++++++-
.../openapi/components/headers/kbn_xsrf.yaml | 1 +
.../components/parameters/action_id.yaml | 7 +
.../schemas/action_response_properties.yaml | 21 +
.../schemas/config_properties_servicenow.yaml | 2 +-
.../config_properties_servicenow_itom.yaml | 2 +-
.../actions/docs/openapi/entrypoint.yaml | 18 +-
.../paths/s@{spaceid}@api@actions.yaml | 78 +++
...paceid}@api@actions@action@{actionid}.yaml | 98 ++++
...pi@actions@action@{actionid}@_execute.yaml | 57 ++
...paceid}@api@actions@list_action_types.yaml | 50 ++
13 files changed, 1660 insertions(+), 22 deletions(-)
create mode 100644 x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml
create mode 100644 x-pack/plugins/actions/docs/openapi/components/schemas/action_response_properties.yaml
create mode 100644 x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions.yaml
create mode 100644 x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}.yaml
create mode 100644 x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml
create mode 100644 x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml
diff --git a/docs/api-generated/connectors/connector-apis-passthru.asciidoc b/docs/api-generated/connectors/connector-apis-passthru.asciidoc
index 98ef93db6e82..c51d4332bc50 100644
--- a/docs/api-generated/connectors/connector-apis-passthru.asciidoc
+++ b/docs/api-generated/connectors/connector-apis-passthru.asciidoc
@@ -23,6 +23,13 @@ Any modifications made to this file will be overwritten.
get /s/{spaceId}/api/actions/connector/{connectorId}
get /s/{spaceId}/api/actions/connector_types
get /s/{spaceId}/api/actions/connectors
+ post /s/{spaceId}/api/actions
+ delete /s/{spaceId}/api/actions/action/{actionId}
+ get /s/{spaceId}/api/actions/action/{actionId}
+ get /s/{spaceId}/api/actions/list_action_types
+ get /s/{spaceId}/api/actions
+ post /s/{spaceId}/api/actions/action/{actionId}/_execute
+ put /s/{spaceId}/api/actions/action/{actionId}
post /s/{spaceId}/api/actions/connector/{connectorId}/_execute
put /s/{spaceId}/api/actions/connector/{connectorId}
@@ -60,7 +67,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -116,7 +123,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -316,6 +323,441 @@ Any modifications made to this file will be overwritten.
401_response
+
+
+
Up
+
post /s/{spaceId}/api/actions
+
Creates a connector. (legacyCreateConnector)
+
Deprecated in 7.13.0. Use the create connector API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
Consumes
+ This API call consumes the following media types via the request header:
+
+
+
Request body
+
+
+
+
Body Parameter —
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "isPreconfigured" : true,
+ "isDeprecated" : true,
+ "actionTypeId" : "actionTypeId",
+ "name" : "name",
+ "id" : "id",
+ "config" : "{}",
+ "isMissingSecrets" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
action_response_properties
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
delete /s/{spaceId}/api/actions/action/{actionId}
+
Deletes a connector. (legacyDeleteConnector)
+
Deprecated in 7.13.0. Use the delete connector API instead. WARNING: When you delete a connector, it cannot be recovered.
+
+
Path parameters
+
+
actionId (required)
+
+
Path Parameter — An identifier for the action. default: null
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
+
+
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
204
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/actions/action/{actionId}
+
Retrieves a connector by ID. (legacyGetConnector)
+
Deprecated in 7.13.0. Use the get connector API instead.
+
+
Path parameters
+
+
actionId (required)
+
+
Path Parameter — An identifier for the action. default: null
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "isPreconfigured" : true,
+ "isDeprecated" : true,
+ "actionTypeId" : "actionTypeId",
+ "name" : "name",
+ "id" : "id",
+ "config" : "{}",
+ "isMissingSecrets" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
action_response_properties
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/actions/list_action_types
+
Retrieves a list of all connector types. (legacyGetConnectorTypes)
+
Deprecated in 7.13.0. Use the get all connector types API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "enabledInConfig" : true,
+ "name" : "name",
+ "enabledInLicense" : true,
+ "id" : "id",
+ "minimumLicenseRequired" : "minimumLicenseRequired",
+ "enabled" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
get /s/{spaceId}/api/actions
+
Retrieves all connectors. (legacyGetConnectors)
+
Deprecated in 7.13.0. Use the get all connectors API instead.
+
+
Path parameters
+
+
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "isPreconfigured" : true,
+ "isDeprecated" : true,
+ "actionTypeId" : "actionTypeId",
+ "name" : "name",
+ "id" : "id",
+ "config" : "{}",
+ "isMissingSecrets" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
post /s/{spaceId}/api/actions/action/{actionId}/_execute
+
Runs a connector. (legacyRunConnector)
+
Deprecated in 7.13.0. Use the run connector API instead.
+
+
Path parameters
+
+
actionId (required)
+
+
Path Parameter — An identifier for the action. default: null
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
Consumes
+ This API call consumes the following media types via the request header:
+
+
+
Request body
+
+
+
+
Body Parameter —
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "actionId" : "actionId",
+ "status" : "status"
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
legacyRunConnector_200_response
+
401
+ Authorization information is missing or invalid.
+
401_response
+
+
+
+
+
Up
+
put /s/{spaceId}/api/actions/action/{actionId}
+
Updates the attributes for a connector. (legacyUpdateConnector)
+
Deprecated in 7.13.0. Use the update connector API instead.
+
+
Path parameters
+
+
actionId (required)
+
+
Path Parameter — An identifier for the action. default: null
spaceId (required)
+
+
Path Parameter — An identifier for the space. If /s/
and the identifier are omitted from the path, the default space is used. default: null
+
+
+
Consumes
+ This API call consumes the following media types via the request header:
+
+
+
Request body
+
+
+
+
Body Parameter —
+
+
+
+
Request headers
+
+
kbn-xsrf (required)
+
+
Header Parameter — Cross-site request forgery protection default: null
+
+
+
+
+
+
Return type
+
+
+
+
+
Example data
+
Content-Type: application/json
+
{
+ "isPreconfigured" : true,
+ "isDeprecated" : true,
+ "actionTypeId" : "actionTypeId",
+ "name" : "name",
+ "id" : "id",
+ "config" : "{}",
+ "isMissingSecrets" : true
+}
+
+
Produces
+ This API call produces the following media types according to the request header;
+ the media type will be conveyed by the response header.
+
+
+
Responses
+
200
+ Indicates a successful call.
+
action_response_properties
+
404
+ Object is not found.
+
404_response
+
+
Up
@@ -350,7 +792,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -421,7 +863,7 @@ Any modifications made to this file will be overwritten.
kbn-xsrf (required)
-
Header Parameter — default: null
+
Header Parameter — Cross-site request forgery protection default: null
@@ -478,12 +920,17 @@ Any modifications made to this file will be overwritten.
Create_connector_request_body_properties
- Create connector request body properties
Get_connector_types_response_body_properties_inner
-
Get_connectors_response_body_properties
- Get connectors response body properties
+
Legacy_create_connector_request_properties
- Legacy create connector request properties
+
Legacy_get_connector_types_response_body_properties_inner
-
+
Legacy_run_connector_request_body_properties
- Legacy run connector request body properties
+
Legacy_update_connector_request_body_properties
- Legacy update connector request body properties
Rule_name_mapping
- Rule name mapping
Run_connector_request_body_properties
- Run connector request body properties
Run_connector_request_body_properties_params
-
Severity_mapping
- Severity mapping
Subaction_parameters
- Subaction parameters
Update_connector_request_body_properties
- Update connector request body properties
+
action_response_properties
- Action response properties
config_properties_cases_webhook
- Connector request properties for Webhook - Case Management connector
config_properties_index
- Connector request properties for an index connector
config_properties_jira
- Connector request properties for a Jira connector
@@ -530,6 +977,7 @@ Any modifications made to this file will be overwritten.
create_connector_request_xmatters
- Create xMatters connector request
features
-
getConnector_404_response
-
+
legacyRunConnector_200_response
-
runConnector_200_response
-
runConnector_200_response_data
-
run_connector_params_documents
- Index connector parameters
@@ -708,6 +1156,44 @@ Any modifications made to this file will be overwritten.
referenced_by_count
Integer Indicates the number of saved objects that reference the connector. If
is_preconfigured
is true, this value is not calculated.
+
+
+
+
+
actionTypeId (optional)
String The connector type identifier.
+
config (optional)
Object The configuration for the connector. Configuration properties vary depending on the connector type.
+
name (optional)
String The display name for the connector.
+
secrets (optional)
Object The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. NOTE: Remember these values. You must provide them each time you update the connector.
+
+
+
+
+
+
+
enabled (optional)
Boolean Indicates whether the connector type is enabled in Kibana.
+
enabledInConfig (optional)
Boolean Indicates whether the connector type is enabled in the Kibana
.yml
file.
+
enabledInLicense (optional)
Boolean Indicates whether the connector is enabled in the license.
+
id (optional)
String The unique identifier for the connector type.
+
minimumLicenseRequired (optional)
String The license that is required to use the connector type.
+
name (optional)
String The name of the connector type.
+
+
+
+
+
The properties vary depending on the connector type.
+
+
params
Object The parameters of the connector. Parameter properties vary depending on the connector type.
+
+
+
+
+
The properties vary depending on the connector type.
+
+
config (optional)
Object The new connector configuration. Configuration properties vary depending on the connector type.
+
name (optional)
String The new name for the connector.
+
secrets (optional)
Object The updated secrets configuration for the connector. Secrets properties vary depending on the connector type.
+
+
Mapping for the name of the alert's rule.
@@ -769,6 +1255,19 @@ Any modifications made to this file will be overwritten.
secrets
+
+
+
The properties vary depending on the action type.
+
+
actionTypeId (optional)
+
config (optional)
+
id (optional)
+
isDeprecated (optional)
Boolean Indicates whether the action type is deprecated.
+
isMissingSecrets (optional)
Boolean Indicates whether secrets are missing for the action.
+
isPreconfigured (optional)
Boolean Indicates whether it is a preconfigured action.
+
name (optional)
+
+
Defines properties for connectors when type is .cases-webhook
.
@@ -834,7 +1333,7 @@ Any modifications made to this file will be overwritten.
apiUrl
String The ServiceNow instance URL.
clientId (optional)
String The client ID assigned to your OAuth application. This property is required when
isOAuth
is
true
.
-
isOAuth (optional)
String The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
+
isOAuth (optional)
Boolean The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
jwtKeyId (optional)
String The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when
isOAuth
is
true
.
userIdentifierValue (optional)
String The identifier to use for OAuth authentication. This identifier should be the user field you selected when you created an OAuth JWT API endpoint for external clients in your ServiceNow instance. For example, if the selected user field is
Email
, the user identifier should be the user's email address. This property is required when
isOAuth
is
true
.
usesTableApi (optional)
Boolean Determines whether the connector uses the Table API or the Import Set API. This property is supported only for ServiceNow ITSM and ServiceNow SecOps connectors. NOTE: If this property is set to
false
, the Elastic application should be installed in ServiceNow.
@@ -846,7 +1345,7 @@ Any modifications made to this file will be overwritten.
apiUrl
String The ServiceNow instance URL.
clientId (optional)
String The client ID assigned to your OAuth application. This property is required when
isOAuth
is
true
.
-
isOAuth (optional)
String The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
+
isOAuth (optional)
Boolean The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
jwtKeyId (optional)
String The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when
isOAuth
is
true
.
userIdentifierValue (optional)
String The identifier to use for OAuth authentication. This identifier should be the user field you selected when you created an OAuth JWT API endpoint for external clients in your ServiceNow instance. For example, if the selected user field is
Email
, the user identifier should be the user's email address. This property is required when
isOAuth
is
true
.
@@ -1351,6 +1850,15 @@ Any modifications made to this file will be overwritten.
statusCode (optional)
+
+
+
+
+
actionId (optional)
+
data (optional)
+
status (optional)
String The status of the action.
+
+
diff --git a/x-pack/plugins/actions/docs/openapi/bundled.json b/x-pack/plugins/actions/docs/openapi/bundled.json
index d887c6de5a3e..cd2df0206b1b 100644
--- a/x-pack/plugins/actions/docs/openapi/bundled.json
+++ b/x-pack/plugins/actions/docs/openapi/bundled.json
@@ -816,6 +816,474 @@
"url": "https://localhost:5601"
}
]
+ },
+ "/s/{spaceId}/api/actions/action/{actionId}": {
+ "delete": {
+ "summary": "Deletes a connector.",
+ "operationId": "legacyDeleteConnector",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the delete connector API instead. WARNING: When you delete a connector, it cannot be recovered.\n",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/action_id"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "Indicates a successful call."
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "get": {
+ "summary": "Retrieves a connector by ID.",
+ "operationId": "legacyGetConnector",
+ "description": "Deprecated in 7.13.0. Use the get connector API instead.",
+ "deprecated": true,
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/action_id"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/action_response_properties"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "put": {
+ "summary": "Updates the attributes for a connector.",
+ "operationId": "legacyUpdateConnector",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the update connector API instead.",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/action_id"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy update connector request body properties",
+ "description": "The properties vary depending on the connector type.",
+ "type": "object",
+ "properties": {
+ "config": {
+ "type": "object",
+ "description": "The new connector configuration. Configuration properties vary depending on the connector type."
+ },
+ "name": {
+ "type": "string",
+ "description": "The new name for the connector."
+ },
+ "secrets": {
+ "type": "object",
+ "description": "The updated secrets configuration for the connector. Secrets properties vary depending on the connector type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/action_response_properties"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Object is not found.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/404_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/actions": {
+ "get": {
+ "summary": "Retrieves all connectors.",
+ "operationId": "legacyGetConnectors",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the get all connectors API instead.",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/action_response_properties"
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "post": {
+ "summary": "Creates a connector.",
+ "operationId": "legacyCreateConnector",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the create connector API instead.",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy create connector request properties",
+ "type": "object",
+ "properties": {
+ "actionTypeId": {
+ "type": "string",
+ "description": "The connector type identifier."
+ },
+ "config": {
+ "type": "object",
+ "description": "The configuration for the connector. Configuration properties vary depending on the connector type."
+ },
+ "name": {
+ "type": "string",
+ "description": "The display name for the connector."
+ },
+ "secrets": {
+ "type": "object",
+ "description": "The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. NOTE: Remember these values. You must provide them each time you update the connector.\n"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/action_response_properties"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/actions/list_action_types": {
+ "get": {
+ "summary": "Retrieves a list of all connector types.",
+ "operationId": "legacyGetConnectorTypes",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the get all connector types API instead.",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy get connector types response body properties",
+ "description": "The properties vary for each connector type.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Indicates whether the connector type is enabled in Kibana."
+ },
+ "enabledInConfig": {
+ "type": "boolean",
+ "description": "Indicates whether the connector type is enabled in the Kibana `.yml` file."
+ },
+ "enabledInLicense": {
+ "type": "boolean",
+ "description": "Indicates whether the connector is enabled in the license.",
+ "example": true
+ },
+ "id": {
+ "type": "string",
+ "description": "The unique identifier for the connector type."
+ },
+ "minimumLicenseRequired": {
+ "type": "string",
+ "description": "The license that is required to use the connector type."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the connector type."
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "/s/{spaceId}/api/actions/action/{actionId}/_execute": {
+ "post": {
+ "summary": "Runs a connector.",
+ "operationId": "legacyRunConnector",
+ "deprecated": true,
+ "description": "Deprecated in 7.13.0. Use the run connector API instead.",
+ "tags": [
+ "connectors"
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/kbn_xsrf"
+ },
+ {
+ "$ref": "#/components/parameters/action_id"
+ },
+ {
+ "$ref": "#/components/parameters/space_id"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "title": "Legacy run connector request body properties",
+ "description": "The properties vary depending on the connector type.",
+ "type": "object",
+ "required": [
+ "params"
+ ],
+ "properties": {
+ "params": {
+ "type": "object",
+ "description": "The parameters of the connector. Parameter properties vary depending on the connector type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Indicates a successful call.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "actionId": {
+ "type": "string"
+ },
+ "data": {
+ "oneOf": [
+ {
+ "type": "object",
+ "description": "Information returned from the action.",
+ "additionalProperties": true
+ },
+ {
+ "type": "array",
+ "description": "An array of information returned from the action.",
+ "items": {
+ "type": "object"
+ }
+ }
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "The status of the action."
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization information is missing or invalid.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/401_response"
+ }
+ }
+ }
+ }
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "url": "https://localhost:5601"
+ }
+ ]
}
},
"components": {
@@ -837,6 +1305,7 @@
},
"in": "header",
"name": "kbn-xsrf",
+ "description": "Cross-site request forgery protection",
"required": true
},
"space_id": {
@@ -858,6 +1327,16 @@
"type": "string",
"example": "df770e30-8b8b-11ed-a780-3b746c987a81"
}
+ },
+ "action_id": {
+ "in": "path",
+ "name": "actionId",
+ "description": "An identifier for the action.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "example": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
+ }
}
},
"schemas": {
@@ -1421,7 +1900,7 @@
"isOAuth": {
"description": "The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).\n",
"default": false,
- "type": "string"
+ "type": "boolean"
},
"jwtKeyId": {
"description": "The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when `isOAuth` is `true`.\n",
@@ -1516,7 +1995,7 @@
"isOAuth": {
"description": "The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).\n",
"default": false,
- "type": "string"
+ "type": "boolean"
},
"jwtKeyId": {
"description": "The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when `isOAuth` is `true`.\n",
@@ -3779,6 +4258,37 @@
}
}
}
+ },
+ "action_response_properties": {
+ "title": "Action response properties",
+ "description": "The properties vary depending on the action type.",
+ "type": "object",
+ "properties": {
+ "actionTypeId": {
+ "type": "string"
+ },
+ "config": {
+ "type": "object"
+ },
+ "id": {
+ "type": "string"
+ },
+ "isDeprecated": {
+ "type": "boolean",
+ "description": "Indicates whether the action type is deprecated."
+ },
+ "isMissingSecrets": {
+ "type": "boolean",
+ "description": "Indicates whether secrets are missing for the action."
+ },
+ "isPreconfigured": {
+ "type": "boolean",
+ "description": "Indicates whether it is a preconfigured action."
+ },
+ "name": {
+ "type": "string"
+ }
+ }
}
},
"examples": {
diff --git a/x-pack/plugins/actions/docs/openapi/bundled.yaml b/x-pack/plugins/actions/docs/openapi/bundled.yaml
index 1652412e027e..6c28df55a358 100644
--- a/x-pack/plugins/actions/docs/openapi/bundled.yaml
+++ b/x-pack/plugins/actions/docs/openapi/bundled.yaml
@@ -472,6 +472,285 @@ paths:
- url: https://localhost:5601
servers:
- url: https://localhost:5601
+ /s/{spaceId}/api/actions/action/{actionId}:
+ delete:
+ summary: Deletes a connector.
+ operationId: legacyDeleteConnector
+ deprecated: true
+ description: |
+ Deprecated in 7.13.0. Use the delete connector API instead. WARNING: When you delete a connector, it cannot be recovered.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/action_id'
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ get:
+ summary: Retrieves a connector by ID.
+ operationId: legacyGetConnector
+ description: Deprecated in 7.13.0. Use the get connector API instead.
+ deprecated: true
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/action_id'
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/action_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ put:
+ summary: Updates the attributes for a connector.
+ operationId: legacyUpdateConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the update connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/action_id'
+ - $ref: '#/components/parameters/space_id'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy update connector request body properties
+ description: The properties vary depending on the connector type.
+ type: object
+ properties:
+ config:
+ type: object
+ description: The new connector configuration. Configuration properties vary depending on the connector type.
+ name:
+ type: string
+ description: The new name for the connector.
+ secrets:
+ type: object
+ description: The updated secrets configuration for the connector. Secrets properties vary depending on the connector type.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/action_response_properties'
+ '404':
+ description: Object is not found.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/404_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/actions:
+ get:
+ summary: Retrieves all connectors.
+ operationId: legacyGetConnectors
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get all connectors API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/action_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ post:
+ summary: Creates a connector.
+ operationId: legacyCreateConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the create connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/space_id'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy create connector request properties
+ type: object
+ properties:
+ actionTypeId:
+ type: string
+ description: The connector type identifier.
+ config:
+ type: object
+ description: The configuration for the connector. Configuration properties vary depending on the connector type.
+ name:
+ type: string
+ description: The display name for the connector.
+ secrets:
+ type: object
+ description: |
+ The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type. NOTE: Remember these values. You must provide them each time you update the connector.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/action_response_properties'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/actions/list_action_types:
+ get:
+ summary: Retrieves a list of all connector types.
+ operationId: legacyGetConnectorTypes
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get all connector types API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/space_id'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ title: Legacy get connector types response body properties
+ description: The properties vary for each connector type.
+ type: array
+ items:
+ type: object
+ properties:
+ enabled:
+ type: boolean
+ description: Indicates whether the connector type is enabled in Kibana.
+ enabledInConfig:
+ type: boolean
+ description: Indicates whether the connector type is enabled in the Kibana `.yml` file.
+ enabledInLicense:
+ type: boolean
+ description: Indicates whether the connector is enabled in the license.
+ example: true
+ id:
+ type: string
+ description: The unique identifier for the connector type.
+ minimumLicenseRequired:
+ type: string
+ description: The license that is required to use the connector type.
+ name:
+ type: string
+ description: The name of the connector type.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
+ /s/{spaceId}/api/actions/action/{actionId}/_execute:
+ post:
+ summary: Runs a connector.
+ operationId: legacyRunConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the run connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '#/components/parameters/kbn_xsrf'
+ - $ref: '#/components/parameters/action_id'
+ - $ref: '#/components/parameters/space_id'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy run connector request body properties
+ description: The properties vary depending on the connector type.
+ type: object
+ required:
+ - params
+ properties:
+ params:
+ type: object
+ description: The parameters of the connector. Parameter properties vary depending on the connector type.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ actionId:
+ type: string
+ data:
+ oneOf:
+ - type: object
+ description: Information returned from the action.
+ additionalProperties: true
+ - type: array
+ description: An array of information returned from the action.
+ items:
+ type: object
+ status:
+ type: string
+ description: The status of the action.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/401_response'
+ servers:
+ - url: https://localhost:5601
+ servers:
+ - url: https://localhost:5601
components:
securitySchemes:
basicAuth:
@@ -487,6 +766,7 @@ components:
type: string
in: header
name: kbn-xsrf
+ description: Cross-site request forgery protection
required: true
space_id:
in: path
@@ -504,6 +784,14 @@ components:
schema:
type: string
example: df770e30-8b8b-11ed-a780-3b746c987a81
+ action_id:
+ in: path
+ name: actionId
+ description: An identifier for the action.
+ required: true
+ schema:
+ type: string
+ example: c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
schemas:
config_properties_cases_webhook:
title: Connector request properties for Webhook - Case Management connector
@@ -945,7 +1233,7 @@ components:
description: |
The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
default: false
- type: string
+ type: boolean
jwtKeyId:
description: |
The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when `isOAuth` is `true`.
@@ -1022,7 +1310,7 @@ components:
description: |
The type of authentication to use. The default value is false, which means basic authentication is used instead of open authorization (OAuth).
default: false
- type: string
+ type: boolean
jwtKeyId:
description: |
The key identifier assigned to the JWT verifier map of your OAuth application. This property is required when `isOAuth` is `true`.
@@ -2671,6 +2959,28 @@ components:
urgency:
type: string
description: The urgency of the incident for ServiceNow ITSM connectors.
+ action_response_properties:
+ title: Action response properties
+ description: The properties vary depending on the action type.
+ type: object
+ properties:
+ actionTypeId:
+ type: string
+ config:
+ type: object
+ id:
+ type: string
+ isDeprecated:
+ type: boolean
+ description: Indicates whether the action type is deprecated.
+ isMissingSecrets:
+ type: boolean
+ description: Indicates whether secrets are missing for the action.
+ isPreconfigured:
+ type: boolean
+ description: Indicates whether it is a preconfigured action.
+ name:
+ type: string
examples:
create_index_connector_request:
summary: Create an index connector.
diff --git a/x-pack/plugins/actions/docs/openapi/components/headers/kbn_xsrf.yaml b/x-pack/plugins/actions/docs/openapi/components/headers/kbn_xsrf.yaml
index 3d8dfae634e6..fe0402a43aa0 100644
--- a/x-pack/plugins/actions/docs/openapi/components/headers/kbn_xsrf.yaml
+++ b/x-pack/plugins/actions/docs/openapi/components/headers/kbn_xsrf.yaml
@@ -2,4 +2,5 @@ schema:
type: string
in: header
name: kbn-xsrf
+description: Cross-site request forgery protection
required: true
diff --git a/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml b/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml
new file mode 100644
index 000000000000..3ee0b642c9de
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/components/parameters/action_id.yaml
@@ -0,0 +1,7 @@
+in: path
+name: actionId
+description: An identifier for the action.
+required: true
+schema:
+ type: string
+ example: c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad
diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/action_response_properties.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/action_response_properties.yaml
new file mode 100644
index 000000000000..ccc6b4bf8a46
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/components/schemas/action_response_properties.yaml
@@ -0,0 +1,21 @@
+title: Action response properties
+description: The properties vary depending on the action type.
+type: object
+properties:
+ actionTypeId:
+ type: string
+ config:
+ type: object
+ id:
+ type: string
+ isDeprecated:
+ type: boolean
+ description: Indicates whether the action type is deprecated.
+ isMissingSecrets:
+ type: boolean
+ description: Indicates whether secrets are missing for the action.
+ isPreconfigured:
+ type: boolean
+ description: Indicates whether it is a preconfigured action.
+ name:
+ type: string
\ No newline at end of file
diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow.yaml
index f7013535f2e5..702a738d436a 100644
--- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow.yaml
+++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow.yaml
@@ -17,7 +17,7 @@ properties:
The type of authentication to use. The default value is false, which means
basic authentication is used instead of open authorization (OAuth).
default: false
- type: string
+ type: boolean
jwtKeyId:
description: >
The key identifier assigned to the JWT verifier map of your OAuth application.
diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow_itom.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow_itom.yaml
index f35f96629c86..34aa025298b5 100644
--- a/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow_itom.yaml
+++ b/x-pack/plugins/actions/docs/openapi/components/schemas/config_properties_servicenow_itom.yaml
@@ -17,7 +17,7 @@ properties:
The type of authentication to use. The default value is false, which means
basic authentication is used instead of open authorization (OAuth).
default: false
- type: string
+ type: boolean
jwtKeyId:
description: >
The key identifier assigned to the JWT verifier map of your OAuth application.
diff --git a/x-pack/plugins/actions/docs/openapi/entrypoint.yaml b/x-pack/plugins/actions/docs/openapi/entrypoint.yaml
index 579845aa9f6d..04e844607f82 100644
--- a/x-pack/plugins/actions/docs/openapi/entrypoint.yaml
+++ b/x-pack/plugins/actions/docs/openapi/entrypoint.yaml
@@ -26,16 +26,14 @@ paths:
'/s/{spaceId}/api/actions/connector/{connectorId}/_execute':
$ref: paths/s@{spaceid}@api@actions@connector@{connectorid}@_execute.yaml
# Deprecated endpoints:
-# '/s/{spaceId}/api/actions/action/{actionId}':
-# $ref: 'paths/s@{spaceid}@api@actions@action@{actionid}.yaml'
-# '/s/{spaceId}/api/actions':
-# $ref: 'paths/s@{spaceid}@api@actions.yaml'
-# '/s/{spaceId}/api/actions/list_action_types':
-# $ref: 'paths/s@{spaceid}@api@actions@list_action_types.yaml'
-# '/s/{spaceId}/api/actions/action':
-# $ref: 'paths/s@{spaceid}@api@actions@action.yaml'
-# '/s/{spaceId}/api/actions/action/{actionId}/_execute':
-# $ref: 'paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml'
+ '/s/{spaceId}/api/actions/action/{actionId}':
+ $ref: 'paths/s@{spaceid}@api@actions@action@{actionid}.yaml'
+ '/s/{spaceId}/api/actions':
+ $ref: 'paths/s@{spaceid}@api@actions.yaml'
+ '/s/{spaceId}/api/actions/list_action_types':
+ $ref: 'paths/s@{spaceid}@api@actions@list_action_types.yaml'
+ '/s/{spaceId}/api/actions/action/{actionId}/_execute':
+ $ref: 'paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml'
components:
securitySchemes:
basicAuth:
diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions.yaml
new file mode 100644
index 000000000000..6b697dca8c4c
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions.yaml
@@ -0,0 +1,78 @@
+get:
+ summary: Retrieves all connectors.
+ operationId: legacyGetConnectors
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get all connectors API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '../components/schemas/action_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+post:
+ summary: Creates a connector.
+ operationId: legacyCreateConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the create connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/headers/kbn_xsrf.yaml'
+ - $ref: '../components/parameters/space_id.yaml'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy create connector request properties
+ type: object
+ properties:
+ actionTypeId:
+ type: string
+ description: The connector type identifier.
+ config:
+ type: object
+ description: The configuration for the connector. Configuration properties vary depending on the connector type.
+ name:
+ type: string
+ description: The display name for the connector.
+ secrets:
+ type: object
+ description: >
+ The secrets configuration for the connector.
+ Secrets configuration properties vary depending on the connector type.
+ NOTE: Remember these values. You must provide them each time you update the connector.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/action_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
+
diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}.yaml
new file mode 100644
index 000000000000..42b0b90a8e48
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}.yaml
@@ -0,0 +1,98 @@
+delete:
+ summary: Deletes a connector.
+ operationId: legacyDeleteConnector
+ deprecated: true
+ description: >
+ Deprecated in 7.13.0. Use the delete connector API instead.
+ WARNING: When you delete a connector, it cannot be recovered.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/headers/kbn_xsrf.yaml'
+ - $ref: '../components/parameters/action_id.yaml'
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '204':
+ description: Indicates a successful call.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+get:
+ summary: Retrieves a connector by ID.
+ operationId: legacyGetConnector
+ description: Deprecated in 7.13.0. Use the get connector API instead.
+ deprecated: true
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/parameters/action_id.yaml'
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/action_response_properties.yaml'
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+put:
+ summary: Updates the attributes for a connector.
+ operationId: legacyUpdateConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the update connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/headers/kbn_xsrf.yaml'
+ - $ref: '../components/parameters/action_id.yaml'
+ - $ref: '../components/parameters/space_id.yaml'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy update connector request body properties
+ description: The properties vary depending on the connector type.
+ type: object
+ properties:
+ config:
+ type: object
+ description: The new connector configuration. Configuration properties vary depending on the connector type.
+ name:
+ type: string
+ description: The new name for the connector.
+ secrets:
+ type: object
+ description: The updated secrets configuration for the connector. Secrets properties vary depending on the connector type.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/action_response_properties.yaml'
+ '404':
+ description: Object is not found.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/404_response.yaml'
+ servers:
+ - url: https://localhost:5601
+
+servers:
+ - url: https://localhost:5601
diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml
new file mode 100644
index 000000000000..d7a9f5caf090
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@action@{actionid}@_execute.yaml
@@ -0,0 +1,57 @@
+post:
+ summary: Runs a connector.
+ operationId: legacyRunConnector
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the run connector API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/headers/kbn_xsrf.yaml'
+ - $ref: '../components/parameters/action_id.yaml'
+ - $ref: '../components/parameters/space_id.yaml'
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ title: Legacy run connector request body properties
+ description: The properties vary depending on the connector type.
+ type: object
+ required:
+ - params
+ properties:
+ params:
+ type: object
+ description: The parameters of the connector. Parameter properties vary depending on the connector type.
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ actionId:
+ type: string
+ data:
+ oneOf:
+ - type: object
+ description: Information returned from the action.
+ additionalProperties: true
+ - type: array
+ description: An array of information returned from the action.
+ items:
+ type: object
+ status:
+ type: string
+ description: The status of the action.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
diff --git a/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml
new file mode 100644
index 000000000000..3ad21728ccc7
--- /dev/null
+++ b/x-pack/plugins/actions/docs/openapi/paths/s@{spaceid}@api@actions@list_action_types.yaml
@@ -0,0 +1,50 @@
+get:
+ summary: Retrieves a list of all connector types.
+ operationId: legacyGetConnectorTypes
+ deprecated: true
+ description: Deprecated in 7.13.0. Use the get all connector types API instead.
+ tags:
+ - connectors
+ parameters:
+ - $ref: '../components/parameters/space_id.yaml'
+ responses:
+ '200':
+ description: Indicates a successful call.
+ content:
+ application/json:
+ schema:
+ title: Legacy get connector types response body properties
+ description: The properties vary for each connector type.
+ type: array
+ items:
+ type: object
+ properties:
+ enabled:
+ type: boolean
+ description: Indicates whether the connector type is enabled in Kibana.
+ enabledInConfig:
+ type: boolean
+ description: Indicates whether the connector type is enabled in the Kibana `.yml` file.
+ enabledInLicense:
+ type: boolean
+ description: Indicates whether the connector is enabled in the license.
+ example: true
+ id:
+ type: string
+ description: The unique identifier for the connector type.
+ minimumLicenseRequired:
+ type: string
+ description: The license that is required to use the connector type.
+ name:
+ type: string
+ description: The name of the connector type.
+ '401':
+ description: Authorization information is missing or invalid.
+ content:
+ application/json:
+ schema:
+ $ref: '../components/schemas/401_response.yaml'
+ servers:
+ - url: https://localhost:5601
+servers:
+ - url: https://localhost:5601
From 6e6cd7fae65bf856cd2db0cbe5e334bd8005f53c Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Thu, 2 Feb 2023 10:23:39 -0500
Subject: [PATCH 17/35] [Fleet] Update write indices on experimental datastream
settings change (#149967)
---
.../experimental_datastream_features.test.ts | 102 +++++++++++++++---
.../experimental_datastream_features.ts | 35 ++++--
.../apis/epm/data_stream.ts | 90 ++++++++++++++++
.../data_stream/test_logs/fields/fields.yml | 4 +
.../test_metrics/fields/fields.yml | 4 +
5 files changed, 213 insertions(+), 22 deletions(-)
diff --git a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.test.ts b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.test.ts
index db37f85f5670..500cf141fed2 100644
--- a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.test.ts
+++ b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.test.ts
@@ -9,10 +9,15 @@ import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import type { NewPackagePolicy, PackagePolicy } from '../../types';
+import { updateCurrentWriteIndices } from '../epm/elasticsearch/template/template';
import { getInstallation } from '../epm/packages';
import { handleExperimentalDatastreamFeatureOptIn } from './experimental_datastream_features';
+const mockedUpdateCurrentWriteIndices = updateCurrentWriteIndices as jest.MockedFunction<
+ typeof updateCurrentWriteIndices
+>;
+
jest.mock('../epm/packages', () => {
return {
getInstallation: jest.fn(),
@@ -27,6 +32,9 @@ jest.mock('../epm/packages', () => {
};
});
+jest.mock('../app_context');
+jest.mock('../epm/elasticsearch/template/template');
+
const mockGetInstallation = getInstallation as jest.Mock;
jest.mock('../epm/elasticsearch/template/install', () => {
@@ -140,6 +148,7 @@ function getExistingTestPackagePolicy({
describe('experimental_datastream_features', () => {
beforeEach(() => {
soClient.get.mockClear();
+ mockedUpdateCurrentWriteIndices.mockReset();
esClient.cluster.getComponentTemplate.mockClear();
esClient.cluster.putComponentTemplate.mockClear();
@@ -173,6 +182,24 @@ describe('experimental_datastream_features', () => {
},
],
});
+
+ esClient.indices.getIndexTemplate.mockResolvedValueOnce({
+ index_templates: [
+ {
+ name: 'metrics-test.test',
+ index_template: {
+ template: {
+ settings: {},
+ mappings: {},
+ },
+ composed_of: [],
+ index_patterns: '',
+ },
+ },
+ ],
+ });
+
+ esClient.indices.getIndexTemplate.mockClear();
});
const soClient = savedObjectsClientMock.create();
@@ -310,22 +337,6 @@ describe('experimental_datastream_features', () => {
isDocValueOnlyOther: false,
});
- esClient.indices.getIndexTemplate.mockResolvedValueOnce({
- index_templates: [
- {
- name: 'metrics-test.test',
- index_template: {
- template: {
- settings: {},
- mappings: {},
- },
- composed_of: [],
- index_patterns: '',
- },
- },
- ],
- });
-
await handleExperimentalDatastreamFeatureOptIn({ soClient, esClient, packagePolicy });
expect(esClient.indices.getIndexTemplate).toHaveBeenCalled();
@@ -372,6 +383,33 @@ describe('experimental_datastream_features', () => {
expect(esClient.cluster.getComponentTemplate).not.toHaveBeenCalled();
expect(esClient.cluster.putComponentTemplate).not.toHaveBeenCalled();
});
+
+ it('does not update write indices', async () => {
+ const packagePolicy = getExistingTestPackagePolicy({
+ isSyntheticSourceEnabled: true,
+ isTSDBEnabled: false,
+ isDocValueOnlyNumeric: false,
+ isDocValueOnlyOther: false,
+ });
+
+ mockGetInstallation.mockResolvedValueOnce({
+ experimental_data_stream_features: [
+ {
+ data_stream: 'metrics-test.test',
+ features: {
+ synthetic_source: true,
+ tsdb: false,
+ doc_value_only_numeric: false,
+ doc_value_only_other: false,
+ },
+ },
+ ],
+ });
+
+ await handleExperimentalDatastreamFeatureOptIn({ soClient, esClient, packagePolicy });
+
+ expect(mockedUpdateCurrentWriteIndices).not.toHaveBeenCalled();
+ });
});
describe('when opt in status is changed', () => {
@@ -509,6 +547,38 @@ describe('experimental_datastream_features', () => {
})
);
});
+
+ it('should update existing write indices', async () => {
+ const packagePolicy = getExistingTestPackagePolicy({
+ isSyntheticSourceEnabled: false,
+ isTSDBEnabled: true,
+ isDocValueOnlyNumeric: false,
+ isDocValueOnlyOther: false,
+ });
+
+ esClient.indices.getIndexTemplate.mockResolvedValueOnce({
+ index_templates: [
+ {
+ name: 'metrics-test.test',
+ index_template: {
+ template: {
+ settings: {},
+ mappings: {},
+ },
+ composed_of: [],
+ index_patterns: '',
+ },
+ },
+ ],
+ });
+
+ await handleExperimentalDatastreamFeatureOptIn({ soClient, esClient, packagePolicy });
+
+ expect(mockedUpdateCurrentWriteIndices).toHaveBeenCalledTimes(1);
+ expect(
+ mockedUpdateCurrentWriteIndices.mock.calls[0][2].map(({ templateName }) => templateName)
+ ).toEqual(['metrics-test.test']);
+ });
});
});
});
diff --git a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts
index 88bde17e2aa3..e98f45a5671c 100644
--- a/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts
+++ b/x-pack/plugins/fleet/server/services/package_policies/experimental_datastream_features.ts
@@ -13,8 +13,15 @@ import { merge } from 'lodash';
import { getRegistryDataStreamAssetBaseName } from '../../../common/services';
import type { ExperimentalIndexingFeature } from '../../../common/types';
-import type { NewPackagePolicy, PackagePolicy } from '../../types';
+import type {
+ NewPackagePolicy,
+ PackagePolicy,
+ IndexTemplate,
+ IndexTemplateEntry,
+} from '../../types';
+import { appContextService } from '../app_context';
import { prepareTemplate } from '../epm/elasticsearch/template/install';
+import { updateCurrentWriteIndices } from '../epm/elasticsearch/template/template';
import { getInstallation, getPackageInfo } from '../epm/packages';
import { updateDatastreamExperimentalFeatures } from '../epm/packages/update';
import {
@@ -71,6 +78,8 @@ export async function handleExperimentalDatastreamFeatureOptIn({
});
}
+ const updatedIndexTemplates: IndexTemplateEntry[] = [];
+
for (const featureMapEntry of packagePolicy.package.experimental_data_stream_features) {
const existingOptIn = installation?.experimental_data_stream_features?.find(
(optIn) => optIn.data_stream === featureMapEntry.data_stream
@@ -126,6 +135,10 @@ export async function handleExperimentalDatastreamFeatureOptIn({
let sourceModeSettings = {};
+ const indexTemplateRes = await esClient.indices.getIndexTemplate({
+ name: featureMapEntry.data_stream,
+ });
+
if (isSyntheticSourceOptInChanged) {
sourceModeSettings = {
_source: {
@@ -152,12 +165,10 @@ export async function handleExperimentalDatastreamFeatureOptIn({
});
}
- if (isTSDBOptInChanged) {
- const indexTemplateRes = await esClient.indices.getIndexTemplate({
- name: featureMapEntry.data_stream,
- });
- const indexTemplate = indexTemplateRes.index_templates[0].index_template;
+ const indexTemplate = indexTemplateRes.index_templates[0].index_template;
+ let updatedIndexTemplate = indexTemplate as IndexTemplate;
+ if (isTSDBOptInChanged) {
const indexTemplateBody = {
...indexTemplate,
template: {
@@ -171,12 +182,24 @@ export async function handleExperimentalDatastreamFeatureOptIn({
},
};
+ updatedIndexTemplate = indexTemplateBody as IndexTemplate;
+
await esClient.indices.putIndexTemplate({
name: featureMapEntry.data_stream,
// @ts-expect-error
body: indexTemplateBody,
});
}
+
+ updatedIndexTemplates.push({
+ templateName: featureMapEntry.data_stream,
+ indexTemplate: updatedIndexTemplate,
+ });
+ }
+
+ // Trigger rollover for updated datastreams
+ if (updatedIndexTemplates.length > 0) {
+ await updateCurrentWriteIndices(esClient, appContextService.getLogger(), updatedIndexTemplates);
}
// Update the installation object to persist the experimental feature map
diff --git a/x-pack/test/fleet_api_integration/apis/epm/data_stream.ts b/x-pack/test/fleet_api_integration/apis/epm/data_stream.ts
index 466f150ec1e0..2e5197f4543c 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/data_stream.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/data_stream.ts
@@ -6,6 +6,7 @@
*/
import expect from '@kbn/expect';
+import { v4 as uuidv4 } from 'uuid';
import { asyncForEach } from '@kbn/std';
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
import { skipIfNoDockerRegistry } from '../../helpers';
@@ -167,5 +168,94 @@ export default function (providerContext: FtrProviderContext) {
});
await installPackage(pkgName, pkgUpdateVersion);
});
+
+ describe('When enabling experimental data stream features', () => {
+ let agentPolicyId: string;
+ let packagePolicyId: string;
+
+ let packagePolicyData: any;
+
+ beforeEach(async () => {
+ const { body: agentPolicyResponse } = await supertest
+ .post(`/api/fleet/agent_policies`)
+ .set('kbn-xsrf', 'xxxx')
+ .send({
+ name: `Test policy ${uuidv4()}`,
+ namespace: 'default',
+ })
+ .expect(200);
+ agentPolicyId = agentPolicyResponse.item.id;
+ packagePolicyData = {
+ force: true,
+ name: `test-package-experimental-feature-${uuidv4()}`,
+ description: '',
+ namespace: 'default',
+ policy_id: agentPolicyId,
+ enabled: true,
+ inputs: [],
+ package: {
+ name: pkgName,
+ version: pkgVersion,
+ },
+ };
+ const { body: responseWithForce } = await supertest
+ .post(`/api/fleet/package_policies`)
+ .set('kbn-xsrf', 'xxxx')
+ .send(packagePolicyData)
+ .expect(200);
+
+ packagePolicyId = responseWithForce.item.id;
+ });
+ afterEach(async () => {
+ await supertest
+ .post(`/api/fleet/agent_policies/delete`)
+ .send({
+ agentPolicyId,
+ })
+ .set('kbn-xsrf', 'xxxx')
+ .expect(200);
+ });
+
+ async function getLogsDefaultBackingIndicesLength() {
+ const resLogsDatastream = await es.transport.request(
+ {
+ method: 'GET',
+ path: `/_data_stream/${logsTemplateName}-${namespaces[0]}`,
+ },
+ { meta: true }
+ );
+
+ return resLogsDatastream.body.data_streams[0].indices.length;
+ }
+
+ it('should rollover datstream after enabling a expiremental datastream feature that need a rollover', async () => {
+ expect(await getLogsDefaultBackingIndicesLength()).to.be(1);
+
+ await supertest
+ .put(`/api/fleet/package_policies/${packagePolicyId}`)
+ .set('kbn-xsrf', 'xxxx')
+ .send({
+ ...packagePolicyData,
+ package: {
+ ...packagePolicyData.package,
+ experimental_data_stream_features: [
+ {
+ data_stream: logsTemplateName,
+ features: {
+ synthetic_source: false,
+ tsdb: false,
+ doc_value_only_numeric: true,
+ doc_value_only_other: true,
+ },
+ },
+ ],
+ },
+ })
+ .expect(200);
+
+ // Datastream should have been rolled over
+ expect(await getLogsDefaultBackingIndicesLength()).to.be(2);
+ });
+ });
});
}
diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml
index 6e003ed0ad14..928d5a4a426c 100644
--- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml
+++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml
@@ -10,6 +10,10 @@
type: constant_keyword
description: >
Data stream namespace.
+- name: numeric_field
+ type: integer
+ description: >
+ Numeric field
- name: '@timestamp'
type: date
description: >
diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml
index 6e003ed0ad14..928d5a4a426c 100644
--- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml
+++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml
@@ -10,6 +10,10 @@
type: constant_keyword
description: >
Data stream namespace.
+- name: numeric_field
+ type: integer
+ description: >
+ Numeric field
- name: '@timestamp'
type: date
description: >
From 39d941e35b823c8583dfa879e1588e233959cda7 Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Thu, 2 Feb 2023 10:24:14 -0500
Subject: [PATCH 18/35] [Fleet] Fix metrics flaky test (#150119)
---
x-pack/test/fleet_api_integration/apis/agents/list.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/x-pack/test/fleet_api_integration/apis/agents/list.ts b/x-pack/test/fleet_api_integration/apis/agents/list.ts
index 05a2f25cfc4e..fde30c9185be 100644
--- a/x-pack/test/fleet_api_integration/apis/agents/list.ts
+++ b/x-pack/test/fleet_api_integration/apis/agents/list.ts
@@ -17,8 +17,7 @@ export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const es = getService('es');
let elasticAgentpkgVersion: string;
- // FLAKY: https://github.com/elastic/kibana/issues/149937
- describe.skip('fleet_list_agent', () => {
+ describe('fleet_list_agent', () => {
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/fleet/agents');
const getPkRes = await supertest
@@ -131,11 +130,12 @@ export default function ({ getService }: FtrProviderContext) {
});
it('should return metrics if available and called with withMetrics', async () => {
+ const now = Date.now();
await es.index({
index: 'metrics-elastic_agent.elastic_agent-default',
refresh: 'wait_for',
document: {
- '@timestamp': new Date(Date.now() - 3 * 60 * 1000).toISOString(),
+ '@timestamp': new Date(now - 2 * 60 * 1000).toISOString(),
data_stream: {
namespace: 'default',
type: 'metrics',
@@ -160,7 +160,7 @@ export default function ({ getService }: FtrProviderContext) {
index: 'metrics-elastic_agent.elastic_agent-default',
refresh: 'wait_for',
document: {
- '@timestamp': new Date(Date.now() - 2 * 60 * 1000).toISOString(),
+ '@timestamp': new Date(now - 1 * 60 * 1000).toISOString(),
elastic_agent: { id: 'agent1', process: 'elastic_agent' },
data_stream: {
namespace: 'default',
From b1617498a5f7866e6cd625641b785bab756376f6 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Thu, 2 Feb 2023 17:49:30 +0200
Subject: [PATCH 19/35] Mark spaces as optional on the contracts (#150021)
## Summary
Part of https://github.com/elastic/kibana/issues/149687
Lens defines the space plugin as optional. I am fixing the types here in
order to be also optional on the contract.
---
x-pack/plugins/lens/public/app_plugin/app.test.tsx | 2 +-
x-pack/plugins/lens/public/app_plugin/types.ts | 2 +-
x-pack/plugins/lens/public/plugin.ts | 2 +-
.../plugins/lens/public/state_management/load_initial.test.tsx | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
index aa815e833a6d..73afa216e62d 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
@@ -1607,7 +1607,7 @@ describe('Lens App', () => {
},
},
});
- expect(services.spaces.ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({
+ expect(services.spaces?.ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({
currentObjectId: '1234',
objectNoun: 'Lens visualization',
otherObjectId: '2',
diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts
index 314bc3a2e52f..193ba130e02e 100644
--- a/x-pack/plugins/lens/public/app_plugin/types.ts
+++ b/x-pack/plugins/lens/public/app_plugin/types.ts
@@ -158,7 +158,7 @@ export interface LensAppServices {
savedObjectsTagging?: SavedObjectTaggingPluginStart;
getOriginatingAppName: () => string | undefined;
presentationUtil: PresentationUtilPluginStart;
- spaces: SpacesApi;
+ spaces?: SpacesApi;
charts: ChartsPluginSetup;
share?: SharePluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts
index 6db5d4abb90e..2e269f37e7f8 100644
--- a/x-pack/plugins/lens/public/plugin.ts
+++ b/x-pack/plugins/lens/public/plugin.ts
@@ -146,7 +146,7 @@ export interface LensPluginStartDependencies {
dataViewFieldEditor: IndexPatternFieldEditorStart;
dataViewEditor: DataViewEditorStart;
inspector: InspectorStartContract;
- spaces: SpacesPluginStart;
+ spaces?: SpacesPluginStart;
usageCollection?: UsageCollectionStart;
docLinks: DocLinksStart;
share?: SharePluginStart;
diff --git a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
index 2d8ce9405f12..ecf519382ff6 100644
--- a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
+++ b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
@@ -309,7 +309,7 @@ describe('Initializing the store', () => {
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
savedObjectId: defaultSavedObjectId,
});
- expect(deps.lensServices.spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith({
+ expect(deps.lensServices.spaces?.ui.redirectLegacyUrl).toHaveBeenCalledWith({
path: '#/edit/id2?search',
aliasPurpose: 'savedObjectConversion',
objectNoun: 'Lens visualization',
From df809bd53a4ea10b94c894774e1ac6b0c4c3e2e2 Mon Sep 17 00:00:00 2001
From: Jean-Louis Leysens
Date: Thu, 2 Feb 2023 17:02:38 +0100
Subject: [PATCH 20/35] [Saved objects] Prepare SO management for versionable
types (#149495)
## Summary
Part of preparing HTTP APIs and associated interfaces for versioning:
* Add domain-specific interfaces to the saved object management plugin
* Add a V1 interface of domain types to `common`
* Remove use of deprecated `SavedObject` type from public
* Follows on from https://github.com/elastic/kibana/pull/148602
Related https://github.com/elastic/kibana/issues/149098
Fixes https://github.com/elastic/kibana/pull/149495
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../saved_objects_management/common/index.ts | 2 +-
.../saved_objects_management/common/types.ts | 61 ------
.../common/types/README.md | 58 ++++++
.../common/types/index.ts | 19 ++
.../types/latest.ts} | 6 +-
.../common/types/v1.ts | 178 ++++++++++++++++++
.../public/lib/bulk_delete_objects.ts | 24 +--
.../public/lib/bulk_get_objects.ts | 15 +-
.../public/lib/case_conversion.test.ts | 25 ---
.../lib/fetch_export_by_type_and_search.ts | 2 +-
.../public/lib/find_objects.ts | 35 +---
.../public/lib/get_allowed_types.ts | 12 +-
.../public/lib/get_relationships.test.ts | 4 +-
.../public/lib/get_relationships.ts | 8 +-
.../public/lib/get_saved_object_counts.ts | 12 +-
.../lib/process_import_response.test.ts | 2 +-
.../public/lib/process_import_response.ts | 2 +-
.../public/lib/resolve_import_errors.ts | 2 +-
.../components/relationships.tsx | 4 +-
.../saved_objects_table.test.tsx | 3 +-
.../objects_table/saved_objects_table.tsx | 3 +-
.../saved_objects_management/public/plugin.ts | 6 +-
.../saved_objects_management/public/types.ts | 1 -
.../server/lib/find_relationships.ts | 9 +-
.../server/lib/index.ts | 1 +
.../server/lib/to_saved_object_with_meta.ts | 24 +++
.../server/routes/bulk_delete.ts | 4 +-
.../server/routes/bulk_get.ts | 17 +-
.../server/routes/find.ts | 33 ++--
.../server/routes/get_allowed_types.ts | 4 +-
.../server/routes/relationships.ts | 7 +-
.../server/routes/scroll_count.ts | 7 +-
.../saved_objects_management/server/types.ts | 1 -
.../saved_objects_management/tsconfig.json | 1 -
.../visible_in_management.ts | 2 +-
35 files changed, 383 insertions(+), 211 deletions(-)
delete mode 100644 src/plugins/saved_objects_management/common/types.ts
create mode 100644 src/plugins/saved_objects_management/common/types/README.md
create mode 100644 src/plugins/saved_objects_management/common/types/index.ts
rename src/plugins/saved_objects_management/{public/lib/case_conversion.ts => common/types/latest.ts} (66%)
create mode 100644 src/plugins/saved_objects_management/common/types/v1.ts
delete mode 100644 src/plugins/saved_objects_management/public/lib/case_conversion.test.ts
create mode 100644 src/plugins/saved_objects_management/server/lib/to_saved_object_with_meta.ts
diff --git a/src/plugins/saved_objects_management/common/index.ts b/src/plugins/saved_objects_management/common/index.ts
index bc4631d2c8e6..895d21610d88 100644
--- a/src/plugins/saved_objects_management/common/index.ts
+++ b/src/plugins/saved_objects_management/common/index.ts
@@ -12,6 +12,6 @@ export type {
SavedObjectRelation,
SavedObjectRelationKind,
SavedObjectInvalidRelation,
- SavedObjectGetRelationshipsResponse,
SavedObjectManagementTypeInfo,
+ v1,
} from './types';
diff --git a/src/plugins/saved_objects_management/common/types.ts b/src/plugins/saved_objects_management/common/types.ts
deleted file mode 100644
index 957fec6a87e5..000000000000
--- a/src/plugins/saved_objects_management/common/types.ts
+++ /dev/null
@@ -1,61 +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 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 type { SavedObject } from '@kbn/core/types';
-import type { SavedObjectsNamespaceType } from '@kbn/core/public';
-
-/**
- * The metadata injected into a {@link SavedObject | saved object} when returning
- * {@link SavedObjectWithMetadata | enhanced objects} from the plugin API endpoints.
- */
-export interface SavedObjectMetadata {
- icon?: string;
- title?: string;
- editUrl?: string;
- inAppUrl?: { path: string; uiCapabilitiesPath: string };
- namespaceType?: SavedObjectsNamespaceType;
- hiddenType?: boolean;
-}
-
-/**
- * A {@link SavedObject | saved object} enhanced with meta properties used by the client-side plugin.
- */
-export type SavedObjectWithMetadata = SavedObject & {
- meta: SavedObjectMetadata;
-};
-
-export type SavedObjectRelationKind = 'child' | 'parent';
-
-/**
- * Represents a relation between two {@link SavedObject | saved object}
- */
-export interface SavedObjectRelation {
- id: string;
- type: string;
- relationship: SavedObjectRelationKind;
- meta: SavedObjectMetadata;
-}
-
-export interface SavedObjectInvalidRelation {
- id: string;
- type: string;
- relationship: SavedObjectRelationKind;
- error: string;
-}
-
-export interface SavedObjectGetRelationshipsResponse {
- relations: SavedObjectRelation[];
- invalidRelations: SavedObjectInvalidRelation[];
-}
-
-export interface SavedObjectManagementTypeInfo {
- name: string;
- namespaceType: SavedObjectsNamespaceType;
- hidden: boolean;
- displayName: string;
-}
diff --git a/src/plugins/saved_objects_management/common/types/README.md b/src/plugins/saved_objects_management/common/types/README.md
new file mode 100644
index 000000000000..bb8ccccaa3f2
--- /dev/null
+++ b/src/plugins/saved_objects_management/common/types/README.md
@@ -0,0 +1,58 @@
+## Versioned interfaces
+
+This folder contains types that are shared between the server and client:
+
+```ts
+// v1.ts
+export interface SavedObjectWithMetadata { name: string }
+
+// index.ts
+import * as v1 from './v1';
+export type { v1 };
+
+// Used elsewhere
+import type { v1 } from '../common';
+const myObject: v1.SavedObjectWithMetadata = { name: 'my object' };
+```
+
+**Do not alter a versioned type**. Types may be in use by clients (if the code is released).
+Alterations must be made on a new version of the TS interface.
+
+## Create a new version
+
+Versions in this plugin are determined using monotonically increasing numbers: 1, 2, 3, etc.
+
+1. Find the latest version, e.g: `v2`.
+2. Create a new file, e.g., `v3.ts` if it does not exist.
+3. Copy the type(s) to change from previous version. E.g. `v2.ts`'s `SavedObjectWithMetadata`.
+4. Alter the interface as needed
+5. Re-export `v2` types to "inherit" the entire previous version's types: `export * from './v2';`
+6. Export your new version from latest: `export * from './v3';`. This may result in TS errors
+ to be fixed.
+7. Export your new file from index.ts as `v3`.
+
+Your `v3.ts` file should look something like:
+
+```ts
+export * from './v3';
+export interface SavedObjectWithMetadata { name: string; a_new_field: string; }
+```
+
+In this way the entire API is accessible from `v3` including types that may
+not have changed.
+
+Any alterations post-release must be in a new version (start at step 1).
+
+
+## The `latest.ts` file
+
+The `latest.ts` file is a container for all "latest" versions of types. This is useful
+for app code that always needs the latest version of your interfaces. E.g.:
+
+```ts
+import type { SavedObjectWithMetadata } from '../common';
+```
+
+Notice that there is no version number mentioned. Either in the interface name
+or import path. To update the "latest" type you must re-export the new version
+from the appropriate versioned path.
diff --git a/src/plugins/saved_objects_management/common/types/index.ts b/src/plugins/saved_objects_management/common/types/index.ts
new file mode 100644
index 000000000000..8e6e27b97715
--- /dev/null
+++ b/src/plugins/saved_objects_management/common/types/index.ts
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+export type {
+ SavedObjectInvalidRelation,
+ SavedObjectManagementTypeInfo,
+ SavedObjectMetadata,
+ SavedObjectRelation,
+ SavedObjectRelationKind,
+ SavedObjectWithMetadata,
+} from './latest';
+
+import type * as v1 from './v1';
+export type { v1 };
diff --git a/src/plugins/saved_objects_management/public/lib/case_conversion.ts b/src/plugins/saved_objects_management/common/types/latest.ts
similarity index 66%
rename from src/plugins/saved_objects_management/public/lib/case_conversion.ts
rename to src/plugins/saved_objects_management/common/types/latest.ts
index d05955cc7c77..e9c79f0f50f9 100644
--- a/src/plugins/saved_objects_management/public/lib/case_conversion.ts
+++ b/src/plugins/saved_objects_management/common/types/latest.ts
@@ -6,8 +6,4 @@
* Side Public License, v 1.
*/
-import { mapKeys, camelCase } from 'lodash';
-
-export function keysToCamelCaseShallow(object: Record) {
- return mapKeys(object, (value, key) => camelCase(key));
-}
+export * from './v1';
diff --git a/src/plugins/saved_objects_management/common/types/v1.ts b/src/plugins/saved_objects_management/common/types/v1.ts
new file mode 100644
index 000000000000..86b2486c0872
--- /dev/null
+++ b/src/plugins/saved_objects_management/common/types/v1.ts
@@ -0,0 +1,178 @@
+/*
+ * 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 type { SavedObjectError } from '@kbn/core/types';
+import type { SavedObjectsNamespaceType } from '@kbn/core/public';
+
+/** Domain interfaces */
+
+/**
+ * Saved Object Management metadata associated with a saved object. See
+ * {@link SavedObjectWithMetadata}.
+ */
+export interface SavedObjectMetadata {
+ icon?: string;
+ title?: string;
+ editUrl?: string;
+ inAppUrl?: { path: string; uiCapabilitiesPath: string };
+ namespaceType?: SavedObjectsNamespaceType;
+ hiddenType?: boolean;
+}
+
+/**
+ * One saved object's reference to another saved object.
+ */
+export interface SavedObjectReference {
+ name: string;
+ type: string;
+ id: string;
+}
+
+/**
+ * A saved object.
+ *
+ * @note This is intended as a domain-specific representation of a SavedObject
+ * which is intended for server-side only use.
+ */
+export interface SavedObjectWithMetadata {
+ id: string;
+ type: string;
+ meta: SavedObjectMetadata;
+ error?: SavedObjectError;
+ created_at?: string;
+ updated_at?: string;
+ attributes: T;
+ namespaces?: string[];
+ references: SavedObjectReference[];
+}
+
+export type SavedObjectRelationKind = 'child' | 'parent';
+
+/**
+ * Represents a relation between two {@link SavedObjectWithMetadata | saved objects}.
+ */
+export interface SavedObjectRelation {
+ id: string;
+ type: string;
+ relationship: SavedObjectRelationKind;
+ meta: SavedObjectMetadata;
+}
+
+/**
+ * Represents a relation between two {@link SavedObjectWithMetadata | saved objects}.
+ */
+export interface SavedObjectInvalidRelation {
+ id: string;
+ type: string;
+ relationship: SavedObjectRelationKind;
+ error: string;
+}
+
+export interface SavedObjectManagementTypeInfo {
+ name: string;
+ // TODO: Fix. We should not directly expose these values to public code.
+ namespaceType: SavedObjectsNamespaceType;
+ hidden: boolean;
+ displayName: string;
+}
+
+/** HTTP API interfaces */
+
+export type BulkGetBodyHTTP = Array<{
+ id: string;
+ type: string;
+}>;
+
+export type BulkGetResponseHTTP = SavedObjectWithMetadata[];
+
+export type BulkDeleteBodyHTTP = Array<{
+ type: string;
+ id: string;
+}>;
+
+export type BulkDeleteResponseHTTP = Array<{
+ /** The ID of the saved object */
+ id: string;
+ /** The type of the saved object */
+ type: string;
+ /** The status of deleting the object: true for deleted, false for error */
+ success: boolean;
+ /** Reason the object could not be deleted (success is false) */
+ error?: SavedObjectError;
+}>;
+
+export type FindSearchOperatorHTTP = 'AND' | 'OR';
+export type FindSortOrderHTTP = 'asc' | 'desc';
+
+export interface ReferenceHTTP {
+ type: string;
+ id: string;
+}
+
+export interface FindQueryHTTP {
+ perPage?: number;
+ page?: number;
+ type: string | string[];
+ // TODO: Fix. this API allows writing an arbitrary query that is passed straight to our persistence layer, thus leaking SO attributes to the public...
+ search?: string;
+ defaultSearchOperator?: FindSearchOperatorHTTP;
+ // TODO: Fix. this API allows sorting by any field, thus leaking SO attributes to the public...
+ sortField?: string;
+ sortOrder?: FindSortOrderHTTP;
+ hasReference?: ReferenceHTTP | ReferenceHTTP[];
+ hasReferenceOperator?: FindSearchOperatorHTTP;
+ // TODO: Fix. This exposes attribute schemas to clients.
+ fields?: string | string[];
+}
+
+export interface FindResponseHTTP {
+ saved_objects: SavedObjectWithMetadata[];
+ total: number;
+ page: number;
+ per_page: number;
+}
+
+export interface GetAllowedTypesResponseHTTP {
+ types: SavedObjectManagementTypeInfo[];
+}
+
+export interface RelationshipsParamsHTTP {
+ type: string;
+ id: string;
+}
+
+export interface RelationshipsQueryHTTP {
+ size: number;
+ savedObjectTypes: string | string[];
+}
+
+export interface RelationshipsResponseHTTP {
+ relations: SavedObjectRelation[];
+ invalidRelations: SavedObjectInvalidRelation[];
+}
+
+export interface ScrollCountBodyHTTP {
+ typesToInclude: string[];
+ // TODO: Fix. this API allows writing an arbitrary query that is passed straight to our persistence layer, thus leaking SO attributes to the public...
+ searchString?: string;
+ references?: Array<{ type: string; id: string }>;
+}
+
+export interface DeleteObjectBodyHTTP {
+ id: string;
+ type: string;
+}
+
+export interface DeleteObjectResponseHTTP {
+ id: string;
+}
+
+/**
+ * In this case "string" is a direct mapping from "typesToInlcude" in {@link ScrollCountBodyHTTP['typesToInclude']']}
+ */
+export type ScrollCountResponseHTTP = Record;
diff --git a/src/plugins/saved_objects_management/public/lib/bulk_delete_objects.ts b/src/plugins/saved_objects_management/public/lib/bulk_delete_objects.ts
index 30a02f8fa42a..3f98f1fc3955 100644
--- a/src/plugins/saved_objects_management/public/lib/bulk_delete_objects.ts
+++ b/src/plugins/saved_objects_management/public/lib/bulk_delete_objects.ts
@@ -6,24 +6,14 @@
* Side Public License, v 1.
*/
-import { HttpStart } from '@kbn/core/public';
-import { SavedObjectError, SavedObjectTypeIdTuple } from '@kbn/core-saved-objects-common';
-
-interface SavedObjectDeleteStatus {
- id: string;
- success: boolean;
- type: string;
- error?: SavedObjectError;
-}
+import type { HttpStart } from '@kbn/core/public';
+import type { v1 } from '../../common';
export function bulkDeleteObjects(
http: HttpStart,
- objects: SavedObjectTypeIdTuple[]
-): Promise {
- return http.post(
- '/internal/kibana/management/saved_objects/_bulk_delete',
- {
- body: JSON.stringify(objects),
- }
- );
+ objects: v1.BulkDeleteBodyHTTP
+): Promise {
+ return http.post('/internal/kibana/management/saved_objects/_bulk_delete', {
+ body: JSON.stringify(objects),
+ });
}
diff --git a/src/plugins/saved_objects_management/public/lib/bulk_get_objects.ts b/src/plugins/saved_objects_management/public/lib/bulk_get_objects.ts
index 370939d62e1d..61d4ca8bb934 100644
--- a/src/plugins/saved_objects_management/public/lib/bulk_get_objects.ts
+++ b/src/plugins/saved_objects_management/public/lib/bulk_get_objects.ts
@@ -6,15 +6,14 @@
* Side Public License, v 1.
*/
-import { HttpStart } from '@kbn/core/public';
-import { SavedObjectWithMetadata } from '../types';
+import type { HttpStart } from '@kbn/core/public';
+import type { v1 } from '../../common';
export async function bulkGetObjects(
http: HttpStart,
- objects: Array<{ type: string; id: string }>
-): Promise {
- return await http.post(
- `/api/kibana/management/saved_objects/_bulk_get`,
- { body: JSON.stringify(objects) }
- );
+ objects: v1.BulkGetBodyHTTP
+): Promise {
+ return await http.post(`/api/kibana/management/saved_objects/_bulk_get`, {
+ body: JSON.stringify(objects),
+ });
}
diff --git a/src/plugins/saved_objects_management/public/lib/case_conversion.test.ts b/src/plugins/saved_objects_management/public/lib/case_conversion.test.ts
deleted file mode 100644
index 111a62a1c5b9..000000000000
--- a/src/plugins/saved_objects_management/public/lib/case_conversion.test.ts
+++ /dev/null
@@ -1,25 +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 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 { keysToCamelCaseShallow } from './case_conversion';
-
-describe('keysToCamelCaseShallow', () => {
- test("should convert all of an object's keys to camel case", () => {
- const data = {
- camelCase: 'camelCase',
- 'kebab-case': 'kebabCase',
- snake_case: 'snakeCase',
- };
-
- const result = keysToCamelCaseShallow(data);
-
- expect(result.camelCase).toBe('camelCase');
- expect(result.kebabCase).toBe('kebabCase');
- expect(result.snakeCase).toBe('snakeCase');
- });
-});
diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts
index eb14f96c700e..161d9034bcb7 100644
--- a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts
+++ b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import { HttpStart, SavedObjectsFindOptionsReference } from '@kbn/core/public';
+import type { HttpStart, SavedObjectsFindOptionsReference } from '@kbn/core/public';
export async function fetchExportByTypeAndSearch({
http,
diff --git a/src/plugins/saved_objects_management/public/lib/find_objects.ts b/src/plugins/saved_objects_management/public/lib/find_objects.ts
index aff66e0a08cc..658bb64800a3 100644
--- a/src/plugins/saved_objects_management/public/lib/find_objects.ts
+++ b/src/plugins/saved_objects_management/public/lib/find_objects.ts
@@ -6,32 +6,17 @@
* Side Public License, v 1.
*/
-import { HttpStart, SavedObjectsFindOptions } from '@kbn/core/public';
-import { keysToCamelCaseShallow } from './case_conversion';
-import { SavedObjectWithMetadata } from '../types';
-
-interface SavedObjectsFindResponse {
- total: number;
- page: number;
- perPage: number;
- savedObjects: SavedObjectWithMetadata[];
-}
+import { HttpStart } from '@kbn/core/public';
+import type { v1 } from '../../common';
export async function findObjects(
http: HttpStart,
- findOptions: SavedObjectsFindOptions
-): Promise {
- const response = await http.get>(
- '/api/kibana/management/saved_objects/_find',
- {
- query: {
- ...findOptions,
- hasReference: findOptions.hasReference
- ? JSON.stringify(findOptions.hasReference)
- : undefined,
- } as Record,
- }
- );
-
- return keysToCamelCaseShallow(response) as SavedObjectsFindResponse;
+ findOptions: v1.FindQueryHTTP
+): Promise {
+ return http.get('/api/kibana/management/saved_objects/_find', {
+ query: {
+ ...findOptions,
+ hasReference: findOptions.hasReference ? JSON.stringify(findOptions.hasReference) : undefined,
+ } as Record,
+ });
}
diff --git a/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts b/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts
index 7e68dbc6be37..3bf9c0f44d2c 100644
--- a/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts
+++ b/src/plugins/saved_objects_management/public/lib/get_allowed_types.ts
@@ -7,14 +7,12 @@
*/
import type { HttpStart } from '@kbn/core/public';
-import type { SavedObjectManagementTypeInfo } from '../../common/types';
+import type { v1 } from '../../common';
-interface GetAllowedTypesResponse {
- types: SavedObjectManagementTypeInfo[];
-}
-
-export async function getAllowedTypes(http: HttpStart): Promise {
- const response = await http.get(
+export async function getAllowedTypes(
+ http: HttpStart
+): Promise {
+ const response = await http.get(
'/api/kibana/management/saved_objects/_allowed_types'
);
return response.types;
diff --git a/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts b/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts
index c52ce26e96a3..c3e001588802 100644
--- a/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts
+++ b/src/plugins/saved_objects_management/public/lib/get_relationships.test.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import { SavedObjectGetRelationshipsResponse } from '../types';
+import { v1 } from '../../common';
import { httpServiceMock } from '@kbn/core/public/mocks';
import { getRelationships } from './get_relationships';
@@ -23,7 +23,7 @@ describe('getRelationships', () => {
});
it('should handle successful responses', async () => {
- const serverResponse: SavedObjectGetRelationshipsResponse = {
+ const serverResponse: v1.RelationshipsResponseHTTP = {
relations: [],
invalidRelations: [],
};
diff --git a/src/plugins/saved_objects_management/public/lib/get_relationships.ts b/src/plugins/saved_objects_management/public/lib/get_relationships.ts
index f0431144573d..7647da05029d 100644
--- a/src/plugins/saved_objects_management/public/lib/get_relationships.ts
+++ b/src/plugins/saved_objects_management/public/lib/get_relationships.ts
@@ -6,21 +6,21 @@
* Side Public License, v 1.
*/
-import { HttpStart } from '@kbn/core/public';
+import type { HttpStart } from '@kbn/core/public';
import { get } from 'lodash';
-import { SavedObjectGetRelationshipsResponse } from '../types';
+import type { v1 } from '../../common';
export async function getRelationships(
http: HttpStart,
type: string,
id: string,
savedObjectTypes: string[]
-): Promise {
+): Promise {
const url = `/api/kibana/management/saved_objects/relationships/${encodeURIComponent(
type
)}/${encodeURIComponent(id)}`;
try {
- return await http.get(url, {
+ return await http.get(url, {
query: {
savedObjectTypes,
},
diff --git a/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts
index 6d31d7085606..23d2818257be 100644
--- a/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts
+++ b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts
@@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
-import { HttpStart, SavedObjectsFindOptionsReference } from '@kbn/core/public';
+import type { HttpStart, SavedObjectsFindOptionsReference } from '@kbn/core/public';
+import type { v1 } from '../../common';
export async function getSavedObjectCounts({
http,
@@ -18,9 +19,8 @@ export async function getSavedObjectCounts({
typesToInclude: string[];
searchString?: string;
references?: SavedObjectsFindOptionsReference[];
-}): Promise> {
- return await http.post>(
- `/api/kibana/management/saved_objects/scroll/counts`,
- { body: JSON.stringify({ typesToInclude, searchString, references }) }
- );
+}): Promise {
+ return await http.post(`/api/kibana/management/saved_objects/scroll/counts`, {
+ body: JSON.stringify({ typesToInclude, searchString, references }),
+ });
}
diff --git a/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts
index 6e354d994220..6b54e7cc617f 100644
--- a/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts
+++ b/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import {
+import type {
SavedObjectsImportConflictError,
SavedObjectsImportAmbiguousConflictError,
SavedObjectsImportUnknownError,
diff --git a/src/plugins/saved_objects_management/public/lib/process_import_response.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.ts
index 4f2624c73ed1..480c10a345cd 100644
--- a/src/plugins/saved_objects_management/public/lib/process_import_response.ts
+++ b/src/plugins/saved_objects_management/public/lib/process_import_response.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import {
+import type {
SavedObjectsImportResponse,
SavedObjectsImportConflictError,
SavedObjectsImportAmbiguousConflictError,
diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts
index 9e9062709674..7558af02cd2a 100644
--- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts
+++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import {
+import type {
HttpStart,
SavedObjectsImportConflictError,
SavedObjectsImportRetry,
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx
index bb097f64c443..825e66197913 100644
--- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx
@@ -28,17 +28,17 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { IBasePath } from '@kbn/core/public';
import type { SavedObjectManagementTypeInfo } from '../../../../common/types';
import { getDefaultTitle, getSavedObjectLabel } from '../../../lib';
+import type { v1 } from '../../../../common';
import {
SavedObjectWithMetadata,
SavedObjectRelationKind,
SavedObjectRelation,
SavedObjectInvalidRelation,
- SavedObjectGetRelationshipsResponse,
} from '../../../types';
export interface RelationshipsProps {
basePath: IBasePath;
- getRelationships: (type: string, id: string) => Promise;
+ getRelationships: (type: string, id: string) => Promise;
savedObject: SavedObjectWithMetadata;
close: () => void;
goInspectObject: (obj: SavedObjectWithMetadata) => void;
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx
index 69ced5010c56..c4ec166799f3 100644
--- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx
@@ -121,6 +121,7 @@ describe('SavedObjectsTable', () => {
};
http.post.mockResolvedValue([]);
+ http.delete.mockResolvedValue({ id: 'test' });
getSavedObjectCountsMock.mockReturnValue({
'index-pattern': 0,
@@ -147,7 +148,7 @@ describe('SavedObjectsTable', () => {
findObjectsMock.mockImplementation(() => ({
total: 4,
- savedObjects: [
+ saved_objects: [
{
id: '1',
type: 'index-pattern',
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx
index 1762ccafe698..9f6bf01f46ca 100644
--- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx
@@ -34,6 +34,7 @@ import {
SavedObjectsExportResultDetails,
getTagFindReferences,
} from '../../lib';
+
import { SavedObjectWithMetadata } from '../../types';
import {
SavedObjectsManagementActionServiceStart,
@@ -257,7 +258,7 @@ export class SavedObjectsTable extends Component Promise;
+ ) => Promise;
getSavedObjectLabel: typeof getSavedObjectLabel;
getDefaultTitle: typeof getDefaultTitle;
parseQuery: typeof parseQuery;
diff --git a/src/plugins/saved_objects_management/public/types.ts b/src/plugins/saved_objects_management/public/types.ts
index 91e6e58e3dd0..fbe8a0cac894 100644
--- a/src/plugins/saved_objects_management/public/types.ts
+++ b/src/plugins/saved_objects_management/public/types.ts
@@ -12,6 +12,5 @@ export type {
SavedObjectRelationKind,
SavedObjectRelation,
SavedObjectInvalidRelation,
- SavedObjectGetRelationshipsResponse,
SavedObjectManagementTypeInfo,
} from '../common';
diff --git a/src/plugins/saved_objects_management/server/lib/find_relationships.ts b/src/plugins/saved_objects_management/server/lib/find_relationships.ts
index 329d985b0b26..00fae0b24ab4 100644
--- a/src/plugins/saved_objects_management/server/lib/find_relationships.ts
+++ b/src/plugins/saved_objects_management/server/lib/find_relationships.ts
@@ -9,11 +9,8 @@
import { SavedObjectsClientContract } from '@kbn/core/server';
import { injectMetaAttributes } from './inject_meta_attributes';
import { ISavedObjectsManagement } from '../services';
-import {
- SavedObjectInvalidRelation,
- SavedObjectWithMetadata,
- SavedObjectGetRelationshipsResponse,
-} from '../types';
+import { v1 } from '../../common';
+import { SavedObjectInvalidRelation, SavedObjectWithMetadata } from '../types';
export async function findRelationships({
type,
@@ -29,7 +26,7 @@ export async function findRelationships({
client: SavedObjectsClientContract;
referenceTypes: string[];
savedObjectsManagement: ISavedObjectsManagement;
-}): Promise {
+}): Promise {
const { references = [] } = await client.get(type, id);
// Use a map to avoid duplicates, it does happen but have a different "name" in the reference
diff --git a/src/plugins/saved_objects_management/server/lib/index.ts b/src/plugins/saved_objects_management/server/lib/index.ts
index cfb5a124bea5..9ee181246075 100644
--- a/src/plugins/saved_objects_management/server/lib/index.ts
+++ b/src/plugins/saved_objects_management/server/lib/index.ts
@@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
+export { toSavedObjectWithMeta } from './to_saved_object_with_meta';
export { injectMetaAttributes } from './inject_meta_attributes';
export { findAll } from './find_all';
export { findRelationships } from './find_relationships';
diff --git a/src/plugins/saved_objects_management/server/lib/to_saved_object_with_meta.ts b/src/plugins/saved_objects_management/server/lib/to_saved_object_with_meta.ts
new file mode 100644
index 000000000000..03a900b0ddc9
--- /dev/null
+++ b/src/plugins/saved_objects_management/server/lib/to_saved_object_with_meta.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 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 { SavedObject } from '@kbn/core/server';
+import { SavedObjectWithMetadata } from '../../common/types/v1';
+
+export function toSavedObjectWithMeta(so: SavedObject): SavedObjectWithMetadata {
+ return {
+ id: so.id,
+ type: so.type,
+ namespaces: so.namespaces,
+ references: so.references,
+ updated_at: so.updated_at,
+ attributes: so.attributes,
+ created_at: so.created_at,
+ error: so.error,
+ meta: {},
+ };
+}
diff --git a/src/plugins/saved_objects_management/server/routes/bulk_delete.ts b/src/plugins/saved_objects_management/server/routes/bulk_delete.ts
index e3f4db044ef2..1776a73a7504 100644
--- a/src/plugins/saved_objects_management/server/routes/bulk_delete.ts
+++ b/src/plugins/saved_objects_management/server/routes/bulk_delete.ts
@@ -8,6 +8,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '@kbn/core/server';
+import type { v1 } from '../../common';
export const registerBulkDeleteRoute = (router: IRouter) => {
router.post(
@@ -29,7 +30,8 @@ export const registerBulkDeleteRoute = (router: IRouter) => {
const client = getClient();
const response = await client.bulkDelete(objects, { force: true });
- return res.ok({ body: response.statuses });
+ const body: v1.BulkDeleteResponseHTTP = response.statuses;
+ return res.ok({ body });
})
);
};
diff --git a/src/plugins/saved_objects_management/server/routes/bulk_get.ts b/src/plugins/saved_objects_management/server/routes/bulk_get.ts
index 9e31b1c24b0b..c93645cef650 100644
--- a/src/plugins/saved_objects_management/server/routes/bulk_get.ts
+++ b/src/plugins/saved_objects_management/server/routes/bulk_get.ts
@@ -7,9 +7,10 @@
*/
import { schema } from '@kbn/config-schema';
-import { IRouter } from '@kbn/core/server';
-import { injectMetaAttributes } from '../lib';
-import { ISavedObjectsManagement } from '../services';
+import type { IRouter } from '@kbn/core/server';
+import { injectMetaAttributes, toSavedObjectWithMeta } from '../lib';
+import type { v1 } from '../../common';
+import type { ISavedObjectsManagement } from '../services';
export const registerBulkGetRoute = (
router: IRouter,
@@ -39,14 +40,16 @@ export const registerBulkGetRoute = (
const client = getClient({ includedHiddenTypes });
const response = await client.bulkGet(objects);
- const enhancedObjects = response.saved_objects.map((obj) => {
- if (!obj.error) {
+
+ const body: v1.BulkGetResponseHTTP = response.saved_objects.map((obj) => {
+ const so = toSavedObjectWithMeta(obj);
+ if (!so.error) {
return injectMetaAttributes(obj, managementService);
}
- return obj;
+ return so;
});
- return res.ok({ body: enhancedObjects });
+ return res.ok({ body });
})
);
};
diff --git a/src/plugins/saved_objects_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts
index 7b9e8b7c0ddc..bf5810c4beb9 100644
--- a/src/plugins/saved_objects_management/server/routes/find.ts
+++ b/src/plugins/saved_objects_management/server/routes/find.ts
@@ -7,9 +7,11 @@
*/
import { schema } from '@kbn/config-schema';
-import { IRouter } from '@kbn/core/server';
-import { injectMetaAttributes } from '../lib';
-import { ISavedObjectsManagement } from '../services';
+import type { IRouter } from '@kbn/core/server';
+
+import type { v1 } from '../../common';
+import { injectMetaAttributes, toSavedObjectWithMeta } from '../lib';
+import type { ISavedObjectsManagement } from '../services';
export const registerFindRoute = (
router: IRouter,
@@ -77,22 +79,23 @@ export const registerFindRoute = (
searchFields: [...searchFields],
});
- const enhancedSavedObjects = findResponse.saved_objects
- .map((so) => injectMetaAttributes(so, managementService))
- .map((obj) => {
- const result = { ...obj, attributes: {} as Record };
+ const savedObjects = findResponse.saved_objects.map(toSavedObjectWithMeta);
+
+ const response: v1.FindResponseHTTP = {
+ saved_objects: savedObjects.map((so) => {
+ const obj = injectMetaAttributes(so, managementService);
+ const result = { ...obj, attributes: {} as Record };
for (const field of includedFields) {
- result.attributes[field] = obj.attributes[field];
+ result.attributes[field] = (obj.attributes as Record)[field];
}
return result;
- });
+ }),
+ total: findResponse.total,
+ per_page: findResponse.per_page,
+ page: findResponse.page,
+ };
- return res.ok({
- body: {
- ...findResponse,
- saved_objects: enhancedSavedObjects,
- },
- });
+ return res.ok({ body: response });
})
);
};
diff --git a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts
index cdd6dc215d69..3a6b0e5809d1 100644
--- a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts
+++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts
@@ -6,8 +6,8 @@
* Side Public License, v 1.
*/
-import { IRouter, SavedObjectsType } from '@kbn/core/server';
-import { SavedObjectManagementTypeInfo } from '../../common';
+import type { IRouter, SavedObjectsType } from '@kbn/core/server';
+import type { SavedObjectManagementTypeInfo } from '../../common';
const convertType = (sot: SavedObjectsType): SavedObjectManagementTypeInfo => {
return {
diff --git a/src/plugins/saved_objects_management/server/routes/relationships.ts b/src/plugins/saved_objects_management/server/routes/relationships.ts
index 8900987a645f..91cbbbabb6de 100644
--- a/src/plugins/saved_objects_management/server/routes/relationships.ts
+++ b/src/plugins/saved_objects_management/server/routes/relationships.ts
@@ -7,10 +7,11 @@
*/
import { schema } from '@kbn/config-schema';
-import { IRouter } from '@kbn/core/server';
+import type { IRouter } from '@kbn/core/server';
import { chain } from 'lodash';
import { findRelationships } from '../lib';
-import { ISavedObjectsManagement } from '../services';
+import type { ISavedObjectsManagement } from '../services';
+import type { v1 } from '../../common';
export const registerRelationshipsRoute = (
router: IRouter,
@@ -48,7 +49,7 @@ export const registerRelationshipsRoute = (
const client = getClient({ includedHiddenTypes });
- const findRelationsResponse = await findRelationships({
+ const findRelationsResponse: v1.RelationshipsResponseHTTP = await findRelationships({
type,
id,
client,
diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts
index 26dd1d57b4cf..210bb3b27c67 100644
--- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts
+++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts
@@ -7,8 +7,9 @@
*/
import { schema } from '@kbn/config-schema';
-import { IRouter, SavedObjectsCreatePointInTimeFinderOptions } from '@kbn/core/server';
+import type { IRouter, SavedObjectsCreatePointInTimeFinderOptions } from '@kbn/core/server';
import { chain } from 'lodash';
+import type { v1 } from '../../common';
import { findAll } from '../lib';
export const registerScrollForCountRoute = (router: IRouter) => {
@@ -70,8 +71,10 @@ export const registerScrollForCountRoute = (router: IRouter) => {
}
}
+ const body: v1.ScrollCountResponseHTTP = counts;
+
return res.ok({
- body: counts,
+ body,
});
})
);
diff --git a/src/plugins/saved_objects_management/server/types.ts b/src/plugins/saved_objects_management/server/types.ts
index 93f6f3d09547..00cef5f0dbea 100644
--- a/src/plugins/saved_objects_management/server/types.ts
+++ b/src/plugins/saved_objects_management/server/types.ts
@@ -18,5 +18,4 @@ export type {
SavedObjectRelationKind,
SavedObjectRelation,
SavedObjectInvalidRelation,
- SavedObjectGetRelationshipsResponse,
} from '../common';
diff --git a/src/plugins/saved_objects_management/tsconfig.json b/src/plugins/saved_objects_management/tsconfig.json
index 98dcb8c30317..0ed7eee5b203 100644
--- a/src/plugins/saved_objects_management/tsconfig.json
+++ b/src/plugins/saved_objects_management/tsconfig.json
@@ -22,7 +22,6 @@
"@kbn/i18n-react",
"@kbn/test-jest-helpers",
"@kbn/core-saved-objects-api-server",
- "@kbn/core-saved-objects-common",
"@kbn/monaco",
"@kbn/config-schema",
"@kbn/core-custom-branding-browser-mocks",
diff --git a/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts
index 9e154ddde153..c4cbd575dc06 100644
--- a/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts
+++ b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts
@@ -9,7 +9,7 @@
import { join } from 'path';
import expect from '@kbn/expect';
import type { Response } from 'supertest';
-import { SavedObject } from '@kbn/core/types';
+import { SavedObject } from '@kbn/core/server';
import type { SavedObjectManagementTypeInfo } from '@kbn/saved-objects-management-plugin/common/types';
import type { PluginFunctionalProviderContext } from '../../services';
From 1418d753eacd0095cca4f08af5c5b12cc9af0817 Mon Sep 17 00:00:00 2001
From: Jeramy Soucy
Date: Thu, 2 Feb 2023 11:11:35 -0500
Subject: [PATCH 21/35] [Docs] Adds authentication providers sync to load
balancing documentation (#149961)
Closes #113928
## Summary
- Adds 'xpack.security.authc.providers' to the list of settings that
must be the same across all Kibana instances behind a load balancer.
- Adds a warning block explaining why the authentication providers need
to match, and an additional configuration case where this applies
(Kibana instances that are backed by the same ES instance and share the
same kibana.index).
---
docs/user/production-considerations/production.asciidoc | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docs/user/production-considerations/production.asciidoc b/docs/user/production-considerations/production.asciidoc
index 84727e536cfe..92cb77cc401f 100644
--- a/docs/user/production-considerations/production.asciidoc
+++ b/docs/user/production-considerations/production.asciidoc
@@ -51,11 +51,18 @@ Settings that must be the same:
[source,js]
--------
xpack.security.encryptionKey //decrypting session information
+xpack.security.authc.* // authentication configuration
+xpack.security.session.* // session configuration
xpack.reporting.encryptionKey //decrypting reports
xpack.encryptedSavedObjects.encryptionKey // decrypting saved objects
xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys // saved objects encryption key rotation, if any
--------
+[WARNING]
+====
+If the authentication configuration does not match, sessions from unrecognized providers in each {kib} instance will be deleted during that instance's regular session cleanup. Similarly, inconsistencies in session configuration can also lead to undesired session logouts. This also applies to any {kib} instances that are backed by the same {es} instance and share the same kibana.index, even if they are not behind the same load balancer.
+====
+
Separate configuration files can be used from the command line by using the `-c` flag:
[source,js]
--------
From 927091dfea916fbed0a8d1f6e6481294dd057f70 Mon Sep 17 00:00:00 2001
From: Konrad Szwarc
Date: Thu, 2 Feb 2023 17:13:01 +0100
Subject: [PATCH 22/35] Fixed back navigation (#150042)
---
.../view/artifacts/layout/policy_artifacts_layout.tsx | 1 +
.../public/management/pages/policy/view/policy_hooks.ts | 8 ++++----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx
index baecdb8d03aa..666bb5534d3e 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx
@@ -59,6 +59,7 @@ export const PolicyArtifactsLayout = React.memo(
() => getExceptionsListApiClient(),
[getExceptionsListApiClient]
);
+
const { getAppUrl } = useAppUrl();
const navigateCallback = usePolicyDetailsArtifactsNavigateCallback(
exceptionsListApiClient.listId
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts
index 001e3c5727a5..26424205db01 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts
@@ -6,7 +6,7 @@
*/
import { useCallback, useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
+import { useHistory, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import {
ENDPOINT_BLOCKLISTS_LIST_ID,
@@ -50,7 +50,7 @@ export function usePolicyDetailsArtifactsNavigateCallback(listId: string) {
const location = usePolicyDetailsSelector(getCurrentArtifactsLocation);
const history = useHistory();
const policyId = usePolicyDetailsSelector(policyIdFromParams);
-
+ const { state } = useLocation();
const getPath = useCallback(
(args: Partial) => {
if (listId === ENDPOINT_TRUSTED_APPS_LIST_ID) {
@@ -79,8 +79,8 @@ export function usePolicyDetailsArtifactsNavigateCallback(listId: string) {
);
return useCallback(
- (args: Partial) => history.push(getPath(args)),
- [getPath, history]
+ (args: Partial) => history.push(getPath(args), state),
+ [getPath, history, state]
);
}
From ff39dca4a866f839e34ba651068c8016561422e2 Mon Sep 17 00:00:00 2001
From: Aleksandr Maus
Date: Thu, 2 Feb 2023 11:17:17 -0500
Subject: [PATCH 23/35] Osquery: Update exported fields reference for osquery
5.5.1 (#143754)
---
.../exported-fields-reference.asciidoc | 49 +++++++++++++++----
1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/docs/osquery/exported-fields-reference.asciidoc b/docs/osquery/exported-fields-reference.asciidoc
index c0405f3e6568..c27b6e67a406 100644
--- a/docs/osquery/exported-fields-reference.asciidoc
+++ b/docs/osquery/exported-fields-reference.asciidoc
@@ -80,6 +80,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _systemd_units.active_state_ - The high-level unit activation state, i.e. generalization of SUB
+*activity* - keyword, number.long
+
+* _unified_log.activity_ - the activity ID associate with the entry.
+
*actual* - keyword, number.long
* _fan_speed_sensors.actual_ - Actual speed
@@ -114,7 +118,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*algorithm* - keyword, text.text
-* _authorized_keys.algorithm_ - algorithm of key
+* _authorized_keys.algorithm_ - Key type
*alias* - keyword, text.text
@@ -621,6 +625,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _ntfs_journal_events.category_ - The category that the event originated from
* _power_sensors.category_ - The sensor category: currents, voltage, wattage
* _system_extensions.category_ - System extension category
+* _unified_log.category_ - The category of the os_log_t used
* _yara_events.category_ - The category of the file
*cdhash* - keyword, text.text
@@ -645,6 +650,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _docker_containers.cgroup_namespace_ - cgroup namespace
* _process_namespaces.cgroup_namespace_ - cgroup namespace inode
+*cgroup_path* - keyword, text.text
+
+* _processes.cgroup_path_ - The full hierarchical path of the process's control group
+
*chain* - keyword, text.text
* _iptables.chain_ - Size of module content.
@@ -836,9 +845,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*comment* - keyword, text.text
* _authorizations.comment_ - Label top-level key
+* _authorized_keys.comment_ - Optional comment
* _docker_image_history.comment_ - Instruction comment
* _etc_protocols.comment_ - Comment with protocol description
-* _etc_services.comment_ - Optional comment for a service.
+* _etc_services.comment_ - Optional comment for a service
* _groups.comment_ - Remarks or comments associated with the group
* _keychain_items.comment_ - Optional keychain comment
@@ -1937,6 +1947,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _alf.firewall_unload_ - 1 If firewall unloading enabled else 0
+*firmware_type* - keyword, text.text
+
+* _platform_info.firmware_type_ - The type of firmware (Uefi, Bios, Unknown).
+
*firmware_version* - keyword, text.text
* _ibridge_info.firmware_version_ - The build version of the firmware
@@ -2236,7 +2250,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*hostname* - keyword, text.text
-* _curl_certificate.hostname_ - Hostname (domain[:port]) to CURL
+* _curl_certificate.hostname_ - Hostname to CURL (domain[:port], for example, osquery.io)
* _system_info.hostname_ - Network hostname including domain
* _ycloud_instance_metadata.hostname_ - Hostname of the VM
@@ -2683,7 +2697,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*key* - keyword, text.text
-* _authorized_keys.key_ - parsed authorized keys line
+* _authorized_keys.key_ - Key encoded as base64
* _azure_instance_tags.key_ - The tag key
* _chrome_extensions.key_ - The extension key, from the manifest file
* _docker_container_envs.key_ - Environment variable name
@@ -2857,9 +2871,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _docker_image_layers.layer_order_ - Layer Order (1 = base layer)
-*level* - keyword, number.long
+*level* - keyword
* _asl.level_ - Log level number. See levels in asl.h.
+* _unified_log.level_ - the severity level of the entry
* _windows_eventlog.level_ - Severity level associated with the event
* _windows_events.level_ - The severity level associated with the event
@@ -3093,6 +3108,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _pipes.max_instances_ - The maximum number of instances creatable for this pipe
+*max_rows* - keyword, number.long
+
+* _unified_log.max_rows_ - The max number of rows returned (defaults to 100).
+
*max_speed* - keyword, number.long
* _memory_devices.max_speed_ - Max speed of memory device in megatransfers per second (MT/s)
@@ -3221,6 +3240,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _lxd_cluster_members.message_ - Message from the node (Online/Offline)
* _selinux_events.message_ - Message
* _syslog_events.message_ - The syslog message
+* _unified_log.message_ - Composed message
* _user_events.message_ - Message from the event
*metadata_endpoint* - keyword, text.text
@@ -3699,8 +3719,9 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _chrome_extensions.optional_permissions_json_ - The JSON-encoded permissions optionally required by the extensions
-*options* - keyword
+*options* - keyword, text.text
+* _authorized_keys.options_ - Optional list of login options
* _dns_resolvers.options_ - Resolver options
* _nfs_shares.options_ - Options string set on the export share
@@ -4129,9 +4150,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _processes.pid_ - Process (or thread) ID
* _running_apps.pid_ - The pid of the application
* _seccomp_events.pid_ - Process ID
-* _services.pid_ - the Process ID of the service
+* _services.pid_ - The Process ID of the service
* _shared_memory.pid_ - Process ID to last use the segment
* _socket_events.pid_ - Process (or thread) ID
+* _unified_log.pid_ - The pid of the process that made the entry
* _user_events.pid_ - Process (or thread) ID
* _windows_crashes.pid_ - Process ID of the crashed process
* _windows_eventlog.pid_ - Process ID which emitted the event record
@@ -4305,6 +4327,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*process* - keyword, text.text
* _alf_explicit_auths.process_ - Process name explicitly allowed
+* _unified_log.process_ - The name of the process that made the entry
*process_being_tapped* - keyword, number.long
@@ -4852,6 +4875,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*sender* - keyword, text.text
* _asl.sender_ - Sender's identification string. Default is process name.
+* _unified_log.sender_ - The name of the binary image that made the entry
*sensor_backend_server* - keyword, text.text
@@ -5311,6 +5335,10 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _kva_speculative_info.stibp_support_enabled_ - Windows uses STIBP.
+*storage* - keyword, number.long
+
+* _unified_log.storage_ - The storage category for the entry.
+
*storage_driver* - keyword, text.text
* _docker_info.storage_driver_ - Storage driver
@@ -5388,6 +5416,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*subsystem* - keyword, text.text
* _system_controls.subsystem_ - Subsystem ID, control type
+* _unified_log.subsystem_ - The subsystem of the os_log_t used
*subsystem_model* - keyword, text.text
@@ -5556,6 +5585,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _bpf_process_events.tid_ - Thread ID
* _bpf_socket_events.tid_ - Thread ID
+* _unified_log.tid_ - The tid of the thread that made the entry
* _windows_crashes.tid_ - Thread ID of the crashed thread
* _windows_eventlog.tid_ - Thread ID which emitted the event record
@@ -5607,6 +5637,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
*timestamp* - keyword, text.text
* _time.timestamp_ - Current timestamp (log format) in UTC
+* _unified_log.timestamp_ - Unix timestamp associated with the entry
* _windows_eventlog.timestamp_ - Timestamp to selectively filter the events
*timestamp_ms* - keyword, number.long
@@ -5697,7 +5728,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _ntfs_acl_permissions.type_ - Type of access mode for the access control entry.
* _nvram.type_ - Data type (CFData, CFString, etc)
* _osquery_events.type_ - Either publisher or subscriber
-* _osquery_extensions.type_ - SDK extension type: extension or module
+* _osquery_extensions.type_ - SDK extension type: core, extension, or module
* _osquery_flags.type_ - Flag type
* _process_open_pipes.type_ - Pipe Type: named vs unnamed/anonymous
* _registry.type_ - Type of the registry value, or 'subkey' if item is a subkey
@@ -5742,7 +5773,7 @@ For more information about osquery tables, see the https://osquery.io/schema[osq
* _known_hosts.uid_ - The local user that owns the known_hosts file
* _launchd_overrides.uid_ - User ID applied to the override, 0 applies to all
* _package_bom.uid_ - Expected user of file or directory
-* _password_policy.uid_ - User ID for the policy if available
+* _password_policy.uid_ - User ID for the policy, -1 for policies that are global
* _process_events.uid_ - User ID at process start
* _process_file_events.uid_ - The uid of the process performing the action
* _processes.uid_ - Unsigned user ID
From d854a9a35b903114d5ba0978e48a082fb0e3b5b3 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Thu, 2 Feb 2023 09:40:39 -0700
Subject: [PATCH 24/35] [ML] Data Frame Analytics: adds functional test for
scatterplot chart link to custom visualization (#150103)
## Summary
Follow up to https://github.com/elastic/kibana/pull/149647
Link to flaky test runner:
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/1856
This PR adds a functional test for the scatterplot matrix link in the
DFA results view to custom visualizations UI.
### Checklist
Delete any items that are not applicable to this PR.
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---
.../scatterplot_matrix/scatterplot_matrix.tsx | 2 +-
.../results_view_content.ts | 4 ++++
.../ml/data_frame_analytics_results.ts | 21 +++++++++++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx
index 6e718e0f0ccd..9a10dc2b782c 100644
--- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx
+++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx
@@ -532,7 +532,7 @@ export const ScatterplotMatrix: FC = ({
openInNewTab: false,
});
}}
- data-test-subj="mlSplomoExploreInCustomVisualizationLink"
+ data-test-subj="mlSplomExploreInCustomVisualizationLink"
>
{
+ await ml.dataFrameAnalyticsResults.assertOpensExploreInCustomVisualization();
+ });
});
}
});
diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts
index 65531647d00e..a4378636673d 100644
--- a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts
+++ b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts
@@ -20,6 +20,7 @@ export function MachineLearningDataFrameAnalyticsResultsProvider(
const headerPage = getPageObject('header');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
+ const find = getService('find');
return {
async assertRegressionEvaluatePanelElementsExists() {
@@ -74,6 +75,26 @@ export function MachineLearningDataFrameAnalyticsResultsProvider(
);
},
+ async getViewContainer() {
+ return find.byCssSelector('div.vgaVis__view');
+ },
+
+ async assertOpensExploreInCustomVisualization() {
+ await testSubjects.existOrFail('mlSplomExploreInCustomVisualizationLink', {
+ timeout: 5000,
+ });
+ await testSubjects.click('mlSplomExploreInCustomVisualizationLink');
+ await testSubjects.existOrFail('visualizationLoader');
+
+ const view = await this.getViewContainer();
+ expect(view).to.be.ok();
+ const size = await view.getSize();
+ expect(size).to.have.property('width');
+ expect(size).to.have.property('height');
+ expect(size.width).to.be.above(0);
+ expect(size.height).to.be.above(0);
+ },
+
async enableResultsTablePreviewHistogramCharts(expectedButtonState: boolean) {
await retry.tryForTime(5000, async () => {
const actualState =
From 801377f682ca39a2f171372e21e4e2a97f57fffd Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Thu, 2 Feb 2023 09:41:04 -0700
Subject: [PATCH 25/35] [ML] Data Frame Analytics maps view: Fix update of map
when selecting results index node (#149993)
## Summary
Fixes https://github.com/elastic/kibana/issues/147775
Ensures node details persist between renders so they don't get
overwritten when refetching from the original source node, causing the
whole map to re-render
https://user-images.githubusercontent.com/6446462/215898574-d6410492-3ca6-4879-b103-a774c3e4f428.mp4
### Checklist
Delete any items that are not applicable to this PR.
- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../job_map/use_fetch_analytics_map_data.ts | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/use_fetch_analytics_map_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/use_fetch_analytics_map_data.ts
index a1660f126bbb..846e27288491 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/use_fetch_analytics_map_data.ts
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/job_map/use_fetch_analytics_map_data.ts
@@ -5,8 +5,9 @@
* 2.0.
*/
-import { useState } from 'react';
+import { useState, useRef } from 'react';
import { i18n } from '@kbn/i18n';
+import { asyncForEach } from '@kbn/std';
import { uniqWith, isEqual } from 'lodash';
import cytoscape from 'cytoscape';
import { ml } from '../../../services/ml_api_service';
@@ -23,11 +24,11 @@ interface GetDataObjectParameter {
export const useFetchAnalyticsMapData = () => {
const [isLoading, setIsLoading] = useState(false);
const [elements, setElements] = useState([]);
- const [nodeDetails, setNodeDetails] = useState>({});
const [error, setError] = useState();
const [message, setMessage] = useState();
// Keeps track of which nodes have been used as root so we can refetch related nodes on refresh
const [usedAsRoot, setUsedAsRoot] = useState>({});
+ const nodeDetails = useRef>({});
const fetchAndSetElements = async (idToUse: string, treatAsRoot: boolean, type?: string) => {
setIsLoading(true);
@@ -57,11 +58,11 @@ export const useFetchAnalyticsMapData = () => {
if (nodeElements?.length > 0) {
if (treatAsRoot === false) {
setElements(nodeElements);
- setNodeDetails(details);
+ nodeDetails.current = details;
} else {
const uniqueElements = uniqWith([...nodeElements, ...elements], isEqual);
setElements(uniqueElements);
- setNodeDetails({ ...details, ...nodeDetails });
+ nodeDetails.current = { ...details, ...nodeDetails.current };
}
}
setIsLoading(false);
@@ -88,11 +89,9 @@ export const useFetchAnalyticsMapData = () => {
// If related nodes had been fetched from any node then refetch
if (Object.keys(usedAsRoot).length) {
- for (const nodeId in usedAsRoot) {
- if (usedAsRoot.hasOwnProperty(nodeId)) {
- await fetchAndSetElements(nodeId, true, usedAsRoot[nodeId]);
- }
- }
+ await asyncForEach(Object.keys(usedAsRoot), async (nodeId) => {
+ await fetchAndSetElements(nodeId, true, usedAsRoot[nodeId]);
+ });
}
};
@@ -102,7 +101,7 @@ export const useFetchAnalyticsMapData = () => {
fetchAndSetElementsWrapper,
isLoading,
message,
- nodeDetails,
+ nodeDetails: nodeDetails.current,
setElements,
setError,
};
From e9b3bbb8f8f004a9171bc34ba4054df4ea0bd1d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?=
Date: Thu, 2 Feb 2023 18:04:46 +0100
Subject: [PATCH 26/35] Use all monitors when checking loading state (#150148)
## Summary
Closes #149978
The error and alerts graphs in the monitor overview page only consider
the enabled monitors to handle their internal "loading" state. This made
the graphs show a permanent "loading" state if no monitors were enabled,
instead of showing zero values for those monitors.
This PR uses all the monitor IDs, enabled or not, to determine if the
monitors have been loaded. It still uses the enabled monitor IDs for the
graphs data so the data shown is correct.
---
.../monitors_page/overview/overview/overview_alerts.tsx | 2 +-
.../overview/overview/overview_errors/overview_errors.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx
index 0d2cbe7e3bfc..d6b3d642b177 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_alerts.tsx
@@ -33,7 +33,7 @@ export const OverviewAlerts = () => {
const { status } = useSelector(selectOverviewStatus);
- const loading = !status?.enabledIds || status?.enabledIds.length === 0;
+ const loading = !status?.allIds || status?.allIds.length === 0;
return (
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx
index 28f0c9f9284b..b2f040011594 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_errors/overview_errors.tsx
@@ -25,7 +25,7 @@ import { selectOverviewStatus } from '../../../../../state';
export function OverviewErrors() {
const { status } = useSelector(selectOverviewStatus);
- const loading = !status?.enabledIds || status?.enabledIds.length === 0;
+ const loading = !status?.allIds || status?.allIds.length === 0;
const { from, to } = useAbsoluteDate({ from: 'now-6h', to: 'now' });
From b9488a099c1e23d33dd87639d3383dd5df3a58fd Mon Sep 17 00:00:00 2001
From: Khristinin Nikita
Date: Thu, 2 Feb 2023 18:11:38 +0100
Subject: [PATCH 27/35] Terms query for Indicator Match rule (#144511)
## Terms query for Indicator Match rule
TODO: [] need more unit/integrations tests, but ready for review
The indicator match rule will use terms query when it is possible to
search for matches for threat-first-search and for events-first-search.
## How the match query worked:
Example for threat-first-search.
If we have matching conditions like:
`host.ip ==== indicator.host.ip` or (`source.name ===
indicator.source.name` AND `host.name === indicator.host.name`)
It will generate queries like:
```
match: {host.ip: "1"},
or
match: {host.ip: "2"}
or
match: {host.ip: "3"}
or
(match: {source.name: "1"} and match: {host.name: "1"})
or
(match: {source.name: "2"} and match: {host.name: "2"})
or
(match: {source.name: "3"} and match: {host.name: "3"})
```
Each match will also have `_name` fields like:
`${threatId}_${threatIndex}_${threatFields}_${sourceField}`
So and because it's 1:1 relation between match and response, later at
enrichment stage will be clear which threat matches which event.
## Terms query.
We do fetch info about mapping for fields which use for match conditions
of the IM rule.
Terms query doesn't support all field types, this is why there is some
allowed list which field types.
Terms query not applied for AND conditions.
For example:
Fields types
host.ip - `ip`
user.name - `keyword`
user.description - `text`
indicator.host.ip_range - `ip_range`
`host.ip === indicator.host.ip` or `host.ip_range === indicator.host.ip`
or (`source.name === indicator.source.name` AND `host.name ===
indicator.host.name`)
It will generate queries like:
```
terms: {host.ip: ["1","2","3"]},
or
match: {host.ip_range: "1"} // terms query support range fields, but it will be difficult later to understand which threat match which event, because we can have more than 1 response for this condition
or
match: {host.ip_range: "2"}
or
(match: {source.name: "1"} and match: {host.name: "1"})
or
(match: {source.name: "2"} and match: {host.name: "2"})
or
(match: {source.name: "3"} and match: {host.name: "3"})
```
For terms query, we don't know which response matches with events, this
is why we do match it back in the code.
## Other changes
Threat-first-search - will do one extra request to have all matched
threats.
For example:
The threat index has 1.000.000 documents.
IM rule gets the first batch of 9.000 threats and builds a query to the
events index.
It returns 100 events (max_signal = 100).
Then it tries to enrich those 100 events with threat info.
The problem is that the original implementation will enrich with the
only threats from this 9.000 batch.
And it will ignore other matches in 1.000.000 threats.
This way we do one extra request in the end from potential alerts to
threat index.
# Tests performance
In the best case, it can improve performance by around 3x times.
[Base](https://github.com/elastic/kibana/pull/149113)
Threat Indicators - 1.500.000 documents
Source - 1.000.000 documents.
1 field for match condition
This PR:
---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../threat_mapping/build_threat_enrichment.ts | 56 ++--
.../build_threat_mapping_filter.test.ts | 64 ++++-
.../build_threat_mapping_filter.ts | 103 ++++++--
.../threat_mapping/create_event_signal.ts | 19 +-
.../threat_mapping/create_threat_signal.ts | 28 +-
.../threat_mapping/create_threat_signals.ts | 44 ++--
.../enrich_signal_threat_matches.mock.ts | 1 +
.../enrich_signal_threat_matches.test.ts | 248 ++++++++++++++++--
.../enrich_signal_threat_matches.ts | 102 ++++---
...get_allowed_fields_for_terms_query.test.ts | 144 ++++++++++
.../get_allowed_fields_for_terms_query.ts | 76 ++++++
.../signals/threat_mapping/get_event_count.ts | 2 +-
.../signals/threat_mapping/types.ts | 72 ++++-
.../signals/threat_mapping/utils.test.ts | 155 ++++++++++-
.../signals/threat_mapping/utils.ts | 89 ++++++-
.../rule_execution_logic/threat_match.ts | 190 +++++++++++++-
16 files changed, 1232 insertions(+), 161 deletions(-)
create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts
create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
index 92ed93b8802b..32fe9b8dbe8c 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_enrichment.ts
@@ -6,10 +6,16 @@
*/
import type { SignalsEnrichment } from '../types';
-import { enrichSignalThreatMatches } from './enrich_signal_threat_matches';
-import type { BuildThreatEnrichmentOptions, GetMatchedThreats } from './types';
-import { getThreatList } from './get_threat_list';
+import {
+ enrichSignalThreatMatches,
+ getSignalMatchesFromThreatList,
+} from './enrich_signal_threat_matches';
+import type { BuildThreatEnrichmentOptions } from './types';
+import { buildThreatMappingFilter } from './build_threat_mapping_filter';
+import { getAllThreatListHits } from './get_threat_list';
+// we do want to make extra requests to the threat index to get enrichments from all threats
+// previously we were enriched alerts only from `currentThreatList` but not all threats
export const buildThreatEnrichment = ({
ruleExecutionLogger,
services,
@@ -22,39 +28,45 @@ export const buildThreatEnrichment = ({
reassignPitId,
listClient,
exceptionFilter,
+ threatMapping,
+ runtimeMappings,
}: BuildThreatEnrichmentOptions): SignalsEnrichment => {
- const getMatchedThreats: GetMatchedThreats = async (ids) => {
- const matchedThreatsFilter = {
- query: {
- bool: {
- filter: {
- ids: { values: ids },
- },
- },
+ return async (signals) => {
+ const threatFiltersFromEvents = buildThreatMappingFilter({
+ threatMapping,
+ threatList: signals,
+ entryKey: 'field',
+ allowedFieldsForTermsQuery: {
+ source: {},
+ threat: {},
},
- };
- const threatResponse = await getThreatList({
+ });
+
+ const threatListHits = await getAllThreatListHits({
esClient: services.scopedClusterClient.asCurrentUser,
- index: threatIndex,
- language: threatLanguage,
- perPage: undefined,
+ threatFilters: [...threatFilters, threatFiltersFromEvents],
query: threatQuery,
+ language: threatLanguage,
+ index: threatIndex,
ruleExecutionLogger,
- searchAfter: undefined,
- threatFilters: [...threatFilters, matchedThreatsFilter],
threatListConfig: {
_source: [`${threatIndicatorPath}.*`, 'threat.feed.*'],
fields: undefined,
},
pitId,
reassignPitId,
- runtimeMappings: undefined,
+ runtimeMappings,
listClient,
exceptionFilter,
});
- return threatResponse.hits.hits;
- };
+ const signalMatches = getSignalMatchesFromThreatList(threatListHits);
- return (signals) => enrichSignalThreatMatches(signals, getMatchedThreats, threatIndicatorPath);
+ return enrichSignalThreatMatches(
+ signals,
+ () => Promise.resolve(threatListHits),
+ threatIndicatorPath,
+ signalMatches
+ );
+ };
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts
index ea896dbb2130..a8076498327c 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.test.ts
@@ -332,7 +332,7 @@ describe('build_threat_mapping_filter', () => {
const threatMapping = getThreatMappingMock();
const threatListItem = getThreatListSearchResponseMock().hits.hits[0];
const innerClause = createAndOrClauses({ threatMapping, threatListItem, entryKey: 'value' });
- expect(innerClause).toEqual(getThreatMappingFilterShouldMock());
+ expect(innerClause).toEqual(getThreatMappingFilterShouldMock().bool.should);
});
test('it should filter out data from entries that do not have mappings', () => {
@@ -343,7 +343,7 @@ describe('build_threat_mapping_filter', () => {
foo: 'bar',
};
const innerClause = createAndOrClauses({ threatMapping, threatListItem, entryKey: 'value' });
- expect(innerClause).toEqual(getThreatMappingFilterShouldMock());
+ expect(innerClause).toEqual(getThreatMappingFilterShouldMock().bool.should);
});
test('it should return an empty boolean given an empty array', () => {
@@ -353,7 +353,7 @@ describe('build_threat_mapping_filter', () => {
threatListItem,
entryKey: 'value',
});
- expect(innerClause).toEqual({ bool: { minimum_should_match: 1, should: [] } });
+ expect(innerClause).toEqual([]);
});
test('it should return an empty boolean clause given an empty object for a threat list item', () => {
@@ -363,7 +363,7 @@ describe('build_threat_mapping_filter', () => {
threatListItem: getThreatListItemMock({ _source: {}, fields: {} }),
entryKey: 'value',
});
- expect(innerClause).toEqual({ bool: { minimum_should_match: 1, should: [] } });
+ expect(innerClause).toEqual([]);
});
});
@@ -446,6 +446,62 @@ describe('build_threat_mapping_filter', () => {
};
expect(mapping).toEqual(expected);
});
+
+ test('it should use terms query if allowedFieldsForTermsQuery provided', () => {
+ const threatMapping = getThreatMappingMock();
+ const threatList = getThreatListSearchResponseMock().hits.hits;
+ const mapping = buildEntriesMappingFilter({
+ threatMapping,
+ threatList,
+ chunkSize: 1024,
+ entryKey: 'value',
+ allowedFieldsForTermsQuery: {
+ source: { 'source.ip': true },
+ threat: { 'source.ip': true },
+ },
+ });
+ const mock = { ...getThreatMappingFilterShouldMock() };
+ mock.bool.should.pop();
+
+ const expected: BooleanFilter = {
+ bool: {
+ should: [
+ mock,
+ {
+ terms: {
+ _name: '__SEP____SEP__source.ip__SEP__source.ip__SEP__tq',
+ 'source.ip': ['127.0.0.1'],
+ },
+ },
+ ],
+ minimum_should_match: 1,
+ },
+ };
+ expect(mapping).toEqual(expected);
+ });
+
+ test('it should use match query if allowedFieldsForTermsQuery provided, but it is AND', () => {
+ const threatMapping = getThreatMappingMock();
+ const threatList = getThreatListSearchResponseMock().hits.hits;
+ const mapping = buildEntriesMappingFilter({
+ threatMapping,
+ threatList,
+ chunkSize: 1024,
+ entryKey: 'value',
+ allowedFieldsForTermsQuery: {
+ source: { 'host.name': true, 'host.ip': true },
+ threat: { 'host.name': true, 'host.ip': true },
+ },
+ });
+
+ const expected: BooleanFilter = {
+ bool: {
+ should: [getThreatMappingFilterShouldMock()],
+ minimum_should_match: 1,
+ },
+ };
+ expect(mapping).toEqual(expected);
+ });
});
describe('splitShouldClauses', () => {
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts
index bc59d490bc7f..0d2691760028 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/build_threat_mapping_filter.ts
@@ -7,7 +7,11 @@
import get from 'lodash/fp/get';
import type { Filter } from '@kbn/es-query';
-import type { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
+import type {
+ ThreatMapping,
+ ThreatMappingEntries,
+} from '@kbn/securitysolution-io-ts-alerting-types';
+import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import type {
BooleanFilter,
BuildEntriesMappingFilterOptions,
@@ -16,7 +20,9 @@ import type {
CreateInnerAndClausesOptions,
FilterThreatMappingOptions,
SplitShouldClausesOptions,
+ TermQuery,
} from './types';
+import { ThreatMatchQueryType } from './types';
import { encodeThreatMatchNamedQuery } from './utils';
export const MAX_CHUNK_SIZE = 1024;
@@ -26,6 +32,7 @@ export const buildThreatMappingFilter = ({
threatList,
chunkSize,
entryKey = 'value',
+ allowedFieldsForTermsQuery,
}: BuildThreatMappingFilterOptions): Filter => {
const computedChunkSize = chunkSize ?? MAX_CHUNK_SIZE;
if (computedChunkSize > 1024) {
@@ -36,6 +43,7 @@ export const buildThreatMappingFilter = ({
threatList,
chunkSize: computedChunkSize,
entryKey,
+ allowedFieldsForTermsQuery,
});
const filterChunk: Filter = {
meta: {
@@ -45,6 +53,7 @@ export const buildThreatMappingFilter = ({
},
query,
};
+
return filterChunk;
};
@@ -91,6 +100,7 @@ export const createInnerAndClauses = ({
index: threatListItem._index,
field: threatMappingEntry.field,
value: threatMappingEntry.value,
+ queryType: ThreatMatchQueryType.match,
}),
},
},
@@ -108,8 +118,8 @@ export const createAndOrClauses = ({
threatMapping,
threatListItem,
entryKey,
-}: CreateAndOrClausesOptions): BooleanFilter => {
- const should = threatMapping.reduce((accum, threatMap) => {
+}: CreateAndOrClausesOptions): QueryDslQueryContainer[] => {
+ const should = threatMapping.reduce((accum, threatMap) => {
const innerAndClauses = createInnerAndClauses({
threatMappingEntries: threatMap.entries,
threatListItem,
@@ -123,7 +133,7 @@ export const createAndOrClauses = ({
}
return accum;
}, []);
- return { bool: { should, minimum_should_match: 1 } };
+ return should;
};
export const buildEntriesMappingFilter = ({
@@ -131,26 +141,68 @@ export const buildEntriesMappingFilter = ({
threatList,
chunkSize,
entryKey,
+ allowedFieldsForTermsQuery,
}: BuildEntriesMappingFilterOptions): BooleanFilter => {
- const combinedShould = threatList.reduce((accum, threatListSearchItem) => {
- const filteredEntries = filterThreatMapping({
- threatMapping,
- threatListItem: threatListSearchItem,
- entryKey,
- });
- const queryWithAndOrClause = createAndOrClauses({
- threatMapping: filteredEntries,
- threatListItem: threatListSearchItem,
- entryKey,
- });
- if (queryWithAndOrClause.bool.should.length !== 0) {
- // These values can be 10k+ large, so using a push here for performance
- accum.push(queryWithAndOrClause);
- }
- return accum;
- }, []);
- const should = splitShouldClauses({ should: combinedShould, chunkSize });
- return { bool: { should, minimum_should_match: 1 } };
+ const allFieldAllowedForTermQuery = (entries: ThreatMappingEntries) =>
+ entries.every(
+ (entry) =>
+ allowedFieldsForTermsQuery?.source?.[entry.field] &&
+ allowedFieldsForTermsQuery?.threat?.[entry.value]
+ );
+ const combinedShould = threatMapping.reduce<{
+ match: QueryDslQueryContainer[];
+ term: TermQuery[];
+ }>(
+ (acc, threatMap) => {
+ if (threatMap.entries.length > 1 || !allFieldAllowedForTermQuery(threatMap.entries)) {
+ threatList.forEach((threatListSearchItem) => {
+ const filteredEntries = filterThreatMapping({
+ threatMapping: [threatMap],
+ threatListItem: threatListSearchItem,
+ entryKey,
+ });
+ const queryWithAndOrClause = createAndOrClauses({
+ threatMapping: filteredEntries,
+ threatListItem: threatListSearchItem,
+ entryKey,
+ });
+ if (queryWithAndOrClause.length !== 0) {
+ // These values can be 10k+ large, so using a push here for performance
+ acc.match.push(...queryWithAndOrClause);
+ }
+ });
+ } else {
+ const threatMappingEntry = threatMap.entries[0];
+ const threats: string[] = threatList
+ .map((threatListItem) => get(threatMappingEntry[entryKey], threatListItem.fields))
+ .filter((val) => val)
+ .map((val) => val[0]);
+ if (threats.length > 0) {
+ acc.term.push({
+ terms: {
+ _name: encodeThreatMatchNamedQuery({
+ field: threatMappingEntry.field,
+ value: threatMappingEntry.value,
+ queryType: ThreatMatchQueryType.term,
+ }),
+ [threatMappingEntry[entryKey === 'field' ? 'value' : 'field']]: threats,
+ },
+ });
+ }
+ }
+ return acc;
+ },
+ { match: [], term: [] }
+ );
+
+ const matchShould = splitShouldClauses({
+ should:
+ combinedShould.match.length > 0
+ ? [{ bool: { should: combinedShould.match, minimum_should_match: 1 } }]
+ : [],
+ chunkSize,
+ });
+ return { bool: { should: [...matchShould, ...combinedShould.term], minimum_should_match: 1 } };
};
export const splitShouldClauses = ({
@@ -168,7 +220,10 @@ export const splitShouldClauses = ({
accum[chunkIndex] = { bool: { should: [], minimum_should_match: 1 } };
}
// Add to the existing array element. Using mutatious push here since these arrays can get very large such as 10k+ and this is going to be a hot code spot.
- accum[chunkIndex].bool.should.push(item);
+ if (Array.isArray(accum[chunkIndex].bool?.should)) {
+ (accum[chunkIndex].bool?.should as QueryDslQueryContainer[]).push(item);
+ }
+
return accum;
}, []);
}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts
index d626d8ea069c..598730c62718 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts
@@ -16,6 +16,7 @@ import {
enrichSignalThreatMatches,
getSignalMatchesFromThreatList,
} from './enrich_signal_threat_matches';
+import { getSignalValueMap } from './utils';
export const createEventSignal = async ({
alertId,
@@ -50,14 +51,17 @@ export const createEventSignal = async ({
secondaryTimestamp,
exceptionFilter,
unprocessedExceptions,
+ allowedFieldsForTermsQuery,
+ threatMatchedFields,
}: CreateEventSignalOptions): Promise => {
- const threatFilter = buildThreatMappingFilter({
+ const threatFiltersFromEvents = buildThreatMappingFilter({
threatMapping,
threatList: currentEventList,
entryKey: 'field',
+ allowedFieldsForTermsQuery,
});
- if (!threatFilter.query || threatFilter.query?.bool.should.length === 0) {
+ if (!threatFiltersFromEvents.query || threatFiltersFromEvents.query?.bool.should.length === 0) {
// empty event list and we do not want to return everything as being
// a hit so opt to return the existing result.
ruleExecutionLogger.debug(
@@ -67,13 +71,13 @@ export const createEventSignal = async ({
} else {
const threatListHits = await getAllThreatListHits({
esClient: services.scopedClusterClient.asCurrentUser,
- threatFilters: [...threatFilters, threatFilter],
+ threatFilters: [...threatFilters, threatFiltersFromEvents],
query: threatQuery,
language: threatLanguage,
index: threatIndex,
ruleExecutionLogger,
threatListConfig: {
- _source: [`${threatIndicatorPath}.*`, 'threat.feed.*'],
+ _source: [`${threatIndicatorPath}.*`, 'threat.feed.*', ...threatMatchedFields.threat],
fields: undefined,
},
pitId: threatPitId,
@@ -83,7 +87,10 @@ export const createEventSignal = async ({
exceptionFilter,
});
- const signalMatches = getSignalMatchesFromThreatList(threatListHits);
+ const signalMatches = getSignalMatchesFromThreatList(
+ threatListHits,
+ getSignalValueMap({ eventList: currentEventList, threatMatchedFields })
+ );
const ids = signalMatches.map((item) => item.signalId);
@@ -143,7 +150,7 @@ export const createEventSignal = async ({
ruleExecutionLogger.debug(
`${
- threatFilter.query?.bool.should.length
+ threatFiltersFromEvents.query?.bool.should.length
} items have completed match checks and the total times to search were ${
result.searchAfterTimes.length !== 0 ? result.searchAfterTimes : '(unknown) '
}ms`
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts
index f4985db71818..8773abbdae84 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts
@@ -6,13 +6,13 @@
*/
import { buildThreatMappingFilter } from './build_threat_mapping_filter';
-
import { getFilter } from '../get_filter';
import { searchAfterAndBulkCreate } from '../search_after_bulk_create';
import { buildReasonMessageForThreatMatchAlert } from '../reason_formatters';
import type { CreateThreatSignalOptions } from './types';
import type { SearchAfterAndBulkCreateReturnType } from '../types';
+import { buildThreatEnrichment } from './build_threat_enrichment';
export const createThreatSignal = async ({
alertId,
bulkCreate,
@@ -30,7 +30,6 @@ export const createThreatSignal = async ({
savedId,
searchAfterSize,
services,
- threatEnrichment,
threatMapping,
tuple,
type,
@@ -40,11 +39,20 @@ export const createThreatSignal = async ({
secondaryTimestamp,
exceptionFilter,
unprocessedExceptions,
+ threatFilters,
+ threatIndex,
+ threatIndicatorPath,
+ threatLanguage,
+ threatPitId,
+ threatQuery,
+ reassignThreatPitId,
+ allowedFieldsForTermsQuery,
}: CreateThreatSignalOptions): Promise => {
const threatFilter = buildThreatMappingFilter({
threatMapping,
threatList: currentThreatList,
entryKey: 'value',
+ allowedFieldsForTermsQuery,
});
if (!threatFilter.query || threatFilter.query?.bool.should.length === 0) {
@@ -70,6 +78,22 @@ export const createThreatSignal = async ({
`${threatFilter.query?.bool.should.length} indicator items are being checked for existence of matches`
);
+ const threatEnrichment = buildThreatEnrichment({
+ ruleExecutionLogger,
+ services,
+ threatFilters,
+ threatIndex,
+ threatIndicatorPath,
+ threatLanguage,
+ threatQuery,
+ pitId: threatPitId,
+ reassignPitId: reassignThreatPitId,
+ listClient,
+ exceptionFilter,
+ threatMapping,
+ runtimeMappings,
+ });
+
const result = await searchAfterAndBulkCreate({
buildReasonMessage: buildReasonMessageForThreatMatchAlert,
bulkCreate,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
index 2bdd9533f1ea..a73485a4fd37 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts
@@ -17,8 +17,13 @@ import type {
import { createThreatSignal } from './create_threat_signal';
import { createEventSignal } from './create_event_signal';
import type { SearchAfterAndBulkCreateReturnType } from '../types';
-import { buildExecutionIntervalValidator, combineConcurrentResults } from './utils';
-import { buildThreatEnrichment } from './build_threat_enrichment';
+import {
+ buildExecutionIntervalValidator,
+ combineConcurrentResults,
+ getMatchedFields,
+} from './utils';
+import { getAllowedFieldsForTermQuery } from './get_allowed_fields_for_terms_query';
+
import { getEventCount, getEventList } from './get_event_count';
import { getMappingFilters } from './get_mapping_filters';
import { THREAT_PIT_KEEP_ALIVE } from '../../../../../common/cti/constants';
@@ -55,6 +60,15 @@ export const createThreatSignals = async ({
exceptionFilter,
unprocessedExceptions,
}: CreateThreatSignalsOptions): Promise => {
+ const threatMatchedFields = getMatchedFields(threatMapping);
+ const allowedFieldsForTermsQuery = await getAllowedFieldsForTermQuery({
+ services,
+ threatMatchedFields,
+ inputIndex,
+ threatIndex,
+ ruleExecutionLogger,
+ });
+
const params = completeRule.ruleParams;
ruleExecutionLogger.debug('Indicator matching rule starting');
const perPage = concurrentSearches * itemsPerSearch;
@@ -129,20 +143,6 @@ export const createThreatSignals = async ({
_source: false,
};
- const threatEnrichment = buildThreatEnrichment({
- ruleExecutionLogger,
- services,
- threatFilters: allThreatFilters,
- threatIndex,
- threatIndicatorPath,
- threatLanguage,
- threatQuery,
- pitId: threatPitId,
- reassignPitId: reassignThreatPitId,
- listClient,
- exceptionFilter,
- });
-
const createSignals = async ({
getDocumentList,
createSignal,
@@ -224,7 +224,6 @@ export const createThreatSignals = async ({
savedId,
searchAfterSize,
services,
- threatEnrichment,
threatFilters: allThreatFilters,
threatIndex,
threatIndicatorPath,
@@ -240,6 +239,8 @@ export const createThreatSignals = async ({
secondaryTimestamp,
exceptionFilter,
unprocessedExceptions,
+ allowedFieldsForTermsQuery,
+ threatMatchedFields,
}),
});
} else {
@@ -281,7 +282,6 @@ export const createThreatSignals = async ({
savedId,
searchAfterSize,
services,
- threatEnrichment,
threatMapping,
tuple,
type,
@@ -291,6 +291,14 @@ export const createThreatSignals = async ({
secondaryTimestamp,
exceptionFilter,
unprocessedExceptions,
+ threatFilters: allThreatFilters,
+ threatIndex,
+ threatIndicatorPath,
+ threatLanguage,
+ threatPitId,
+ threatQuery,
+ reassignThreatPitId,
+ allowedFieldsForTermsQuery,
}),
});
}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts
index 738eba89fe22..ef68285669de 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.mock.ts
@@ -15,6 +15,7 @@ export const getNamedQueryMock = (
index: 'index',
field: 'field',
value: 'value',
+ queryType: 'mq',
...overrides,
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
index bb341d7e5423..974b0e00a7ce 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.test.ts
@@ -19,7 +19,12 @@ import {
MAX_NUMBER_OF_SIGNAL_MATCHES,
} from './enrich_signal_threat_matches';
import { getNamedQueryMock, getSignalHitMock } from './enrich_signal_threat_matches.mock';
-import type { GetMatchedThreats, ThreatListItem, ThreatMatchNamedQuery } from './types';
+import type {
+ GetMatchedThreats,
+ ThreatListItem,
+ ThreatMatchNamedQuery,
+ SignalMatch,
+} from './types';
import { encodeThreatMatchNamedQuery } from './utils';
describe('groupAndMergeSignalMatches', () => {
@@ -480,6 +485,7 @@ describe('enrichSignalThreatMatches', () => {
let getMatchedThreats: GetMatchedThreats;
let matchedQuery: string;
let indicatorPath: string;
+ let signalMatches: SignalMatch[];
beforeEach(() => {
indicatorPath = 'threat.indicator';
@@ -502,6 +508,19 @@ describe('enrichSignalThreatMatches', () => {
value: 'threat.indicator.domain',
})
);
+ signalMatches = [
+ {
+ signalId: '_id',
+ queries: [
+ getNamedQueryMock({
+ id: '123',
+ index: 'indicator_index',
+ field: 'event.domain',
+ value: 'threat.indicator.domain',
+ }),
+ ],
+ },
+ ];
});
it('performs no enrichment if there are no signals', async () => {
@@ -509,7 +528,8 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- indicatorPath
+ indicatorPath,
+ []
);
expect(enrichedSignals).toEqual([]);
@@ -528,7 +548,8 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- indicatorPath
+ indicatorPath,
+ signalMatches
);
const [enrichedHit] = enrichedSignals;
const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH);
@@ -562,7 +583,8 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- indicatorPath
+ indicatorPath,
+ signalMatches
);
const [enrichedHit] = enrichedSignals;
const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH);
@@ -600,7 +622,8 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- indicatorPath
+ indicatorPath,
+ signalMatches
);
const [enrichedHit] = enrichedSignals;
const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH);
@@ -637,7 +660,7 @@ describe('enrichSignalThreatMatches', () => {
});
const signals: SignalSourceHit[] = [signalHit];
await expect(() =>
- enrichSignalThreatMatches(signals, getMatchedThreats, indicatorPath)
+ enrichSignalThreatMatches(signals, getMatchedThreats, indicatorPath, signalMatches)
).rejects.toThrowError('Expected threat field to be an object, but found: whoops');
});
@@ -656,14 +679,13 @@ describe('enrichSignalThreatMatches', () => {
},
}),
];
- matchedQuery = encodeThreatMatchNamedQuery(
- getNamedQueryMock({
- id: '123',
- index: 'custom_index',
- field: 'event.domain',
- value: 'custom_threat.custom_indicator.domain',
- })
- );
+ const namedQuery = getNamedQueryMock({
+ id: '123',
+ index: 'custom_index',
+ field: 'event.domain',
+ value: 'custom_threat.custom_indicator.domain',
+ });
+ matchedQuery = encodeThreatMatchNamedQuery(namedQuery);
const signalHit = getSignalHitMock({
_source: {
event: {
@@ -676,7 +698,8 @@ describe('enrichSignalThreatMatches', () => {
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- 'custom_threat.custom_indicator'
+ 'custom_threat.custom_indicator',
+ [{ signalId: '_id', queries: [namedQuery] }]
);
const [enrichedHit] = enrichedSignals;
const enrichments = get(enrichedHit._source, ENRICHMENT_DESTINATION_PATH);
@@ -727,6 +750,12 @@ describe('enrichSignalThreatMatches', () => {
},
matched_queries: [matchedQuery],
});
+ const otherMatchQuery = getNamedQueryMock({
+ id: '456',
+ index: 'other_custom_index',
+ field: 'event.other',
+ value: 'threat.indicator.domain',
+ });
const otherSignalHit = getSignalHitMock({
_id: 'signal123',
_source: {
@@ -735,22 +764,27 @@ describe('enrichSignalThreatMatches', () => {
other: 'test_val',
},
},
- matched_queries: [
- encodeThreatMatchNamedQuery(
- getNamedQueryMock({
- id: '456',
- index: 'other_custom_index',
- field: 'event.other',
- value: 'threat.indicator.domain',
- })
- ),
- ],
+ matched_queries: [encodeThreatMatchNamedQuery(otherMatchQuery)],
});
const signals: SignalSourceHit[] = [signalHit, otherSignalHit];
const enrichedSignals = await enrichSignalThreatMatches(
signals,
getMatchedThreats,
- indicatorPath
+ indicatorPath,
+ [
+ {
+ signalId: 'signal123',
+ queries: [
+ getNamedQueryMock({
+ id: '123',
+ index: 'indicator_index',
+ field: 'event.domain',
+ value: 'threat.indicator.domain',
+ }),
+ otherMatchQuery,
+ ],
+ },
+ ]
);
expect(enrichedSignals).toHaveLength(1);
@@ -834,6 +868,167 @@ describe('getSignalMatchesFromThreatList', () => {
value: 'threat.indicator.domain',
index: 'threat_index',
id: 'threatId',
+ queryType: 'mq',
+ },
+ ];
+
+ expect(signalMatches).toEqual([
+ {
+ signalId: 'signalId1',
+ queries,
+ },
+ {
+ signalId: 'signalId2',
+ queries,
+ },
+ ]);
+ });
+
+ it('return empty array for terms query if there no signalValueMap', () => {
+ const signalMatches = getSignalMatchesFromThreatList([
+ getThreatListItemMock({
+ _id: 'threatId',
+ matched_queries: [
+ encodeThreatMatchNamedQuery(
+ getNamedQueryMock({
+ value: 'threat.indicator.domain',
+ field: 'event.domain',
+ queryType: 'tq',
+ })
+ ),
+ ],
+ }),
+ ]);
+
+ expect(signalMatches).toEqual([]);
+ });
+
+ it('return empty array for terms query if there wrong value in threat indicator', () => {
+ const threat = getThreatListItemMock({
+ _id: 'threatId',
+ matched_queries: [
+ encodeThreatMatchNamedQuery(
+ getNamedQueryMock({
+ value: 'threat.indicator.domain',
+ field: 'event.domain',
+ queryType: 'tq',
+ })
+ ),
+ ],
+ });
+
+ threat._source = {
+ ...threat._source,
+ threat: {
+ indicator: {
+ domain: { a: 'b' },
+ },
+ },
+ };
+
+ const signalValueMap = {
+ 'event.domain': {
+ domain_1: ['signalId1', 'signalId2'],
+ },
+ };
+
+ const signalMatches = getSignalMatchesFromThreatList([threat], signalValueMap);
+
+ expect(signalMatches).toEqual([]);
+ });
+
+ it('return signal matches from threat indicators for termsQuery', () => {
+ const threat = getThreatListItemMock({
+ _id: 'threatId',
+ matched_queries: [
+ encodeThreatMatchNamedQuery(
+ getNamedQueryMock({
+ value: 'threat.indicator.domain',
+ field: 'event.domain',
+ queryType: 'tq',
+ })
+ ),
+ ],
+ });
+
+ threat._source = {
+ ...threat._source,
+ threat: {
+ indicator: {
+ domain: 'domain_1',
+ },
+ },
+ };
+
+ const signalValueMap = {
+ 'event.domain': {
+ domain_1: ['signalId1', 'signalId2'],
+ },
+ };
+
+ const signalMatches = getSignalMatchesFromThreatList([threat], signalValueMap);
+
+ const queries = [
+ {
+ field: 'event.domain',
+ value: 'threat.indicator.domain',
+ index: 'threat_index',
+ id: 'threatId',
+ queryType: 'tq',
+ },
+ ];
+
+ expect(signalMatches).toEqual([
+ {
+ signalId: 'signalId1',
+ queries,
+ },
+ {
+ signalId: 'signalId2',
+ queries,
+ },
+ ]);
+ });
+
+ it('return signal matches from threat indicators which has array values for termsQuery', () => {
+ const threat = getThreatListItemMock({
+ _id: 'threatId',
+ matched_queries: [
+ encodeThreatMatchNamedQuery(
+ getNamedQueryMock({
+ value: 'threat.indicator.domain',
+ field: 'event.domain',
+ queryType: 'tq',
+ })
+ ),
+ ],
+ });
+
+ threat._source = {
+ ...threat._source,
+ threat: {
+ indicator: {
+ domain: ['domain_3', 'domain_1', 'domain_2'],
+ },
+ },
+ };
+
+ const signalValueMap = {
+ 'event.domain': {
+ domain_1: ['signalId1'],
+ domain_2: ['signalId2'],
+ },
+ };
+
+ const signalMatches = getSignalMatchesFromThreatList([threat], signalValueMap);
+
+ const queries = [
+ {
+ field: 'event.domain',
+ value: 'threat.indicator.domain',
+ index: 'threat_index',
+ id: 'threatId',
+ queryType: 'tq',
},
];
@@ -876,6 +1071,7 @@ describe('getSignalMatchesFromThreatList', () => {
value: 'threat.indicator.domain',
index: 'threat_index',
id: 'threatId',
+ queryType: 'mq',
};
expect(signalMatches).toEqual([
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
index e0b9d4fb6dee..3ce871283219 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts
@@ -15,42 +15,86 @@ import type {
ThreatListItem,
ThreatMatchNamedQuery,
SignalMatch,
+ SignalValuesMap,
+ ThreatTermNamedQuery,
} from './types';
+import { ThreatMatchQueryType } from './types';
import { extractNamedQueries } from './utils';
export const MAX_NUMBER_OF_SIGNAL_MATCHES = 1000;
export const getSignalMatchesFromThreatList = (
- threatList: ThreatListItem[] = []
+ threatList: ThreatListItem[] = [],
+ signalValueMap?: SignalValuesMap
): SignalMatch[] => {
const signalMap: { [key: string]: ThreatMatchNamedQuery[] } = {};
+ const addSignalValueToMap = ({
+ id,
+ threatHit,
+ query,
+ }: {
+ id: string;
+ threatHit: ThreatListItem;
+ query: ThreatMatchNamedQuery | ThreatTermNamedQuery;
+ }) => {
+ if (!signalMap[id]) {
+ signalMap[id] = [];
+ }
- threatList.forEach((threatHit) =>
- extractNamedQueries(threatHit).forEach((item) => {
- const signalId = item.id;
- if (!signalId) {
- return;
- }
-
- if (!signalMap[signalId]) {
- signalMap[signalId] = [];
- }
+ // creating map of signal with large number of threats could lead to out of memory Kibana crash
+ // large number of threats also can cause signals bulk create failure due too large payload (413)
+ // large number of threats significantly slower alert details page render
+ // so, its number is limited to MAX_NUMBER_OF_SIGNAL_MATCHES
+ // more details https://github.com/elastic/kibana/issues/143595#issuecomment-1335433592
+ if (signalMap[id].length >= MAX_NUMBER_OF_SIGNAL_MATCHES) {
+ return;
+ }
- // creating map of signal with large number of threats could lead to out of memory Kibana crash
- // large number of threats also can cause signals bulk create failure due too large payload (413)
- // large number of threats significantly slower alert details page render
- // so, its number is limited to MAX_NUMBER_OF_SIGNAL_MATCHES
- // more details https://github.com/elastic/kibana/issues/143595#issuecomment-1335433592
- if (signalMap[signalId].length >= MAX_NUMBER_OF_SIGNAL_MATCHES) {
- return;
+ signalMap[id].push({
+ id: threatHit._id,
+ index: threatHit._index,
+ field: query.field,
+ value: query.value,
+ queryType: query.queryType,
+ });
+ };
+ threatList.forEach((threatHit) =>
+ extractNamedQueries(threatHit).forEach((query) => {
+ const signalId = query.id;
+
+ if (query.queryType === ThreatMatchQueryType.term) {
+ const threatValue = get(threatHit?._source, query.value);
+ let values;
+ if (Array.isArray(threatValue)) {
+ values = threatValue;
+ } else {
+ values = [threatValue];
+ }
+
+ values.forEach((value) => {
+ if (value && signalValueMap) {
+ const ids = signalValueMap[query.field][value?.toString()];
+
+ ids?.forEach((id: string) => {
+ addSignalValueToMap({
+ id,
+ threatHit,
+ query,
+ });
+ });
+ }
+ });
+ } else {
+ if (!signalId) {
+ return;
+ }
+
+ addSignalValueToMap({
+ id: signalId,
+ threatHit,
+ query,
+ });
}
-
- signalMap[signalId].push({
- id: threatHit._id,
- index: threatHit._index,
- field: item.field,
- value: item.value,
- });
})
);
@@ -123,19 +167,13 @@ export const enrichSignalThreatMatches = async (
signals: SignalSourceHit[],
getMatchedThreats: GetMatchedThreats,
indicatorPath: string,
- signalMatchesArg?: SignalMatch[]
+ signalMatches: SignalMatch[]
): Promise => {
if (signals.length === 0) {
return signals;
}
const uniqueHits = groupAndMergeSignalMatches(signals);
- const signalMatches: SignalMatch[] = signalMatchesArg
- ? signalMatchesArg
- : uniqueHits.map((signalHit) => ({
- signalId: signalHit._id,
- queries: extractNamedQueries(signalHit),
- }));
const matchedThreatIds = [
...new Set(
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts
new file mode 100644
index 000000000000..b13b5d23278b
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.test.ts
@@ -0,0 +1,144 @@
+/*
+ * 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 { IndicesGetFieldMappingResponse } from '@elastic/elasticsearch/lib/api/types';
+import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks';
+import { alertsMock } from '@kbn/alerting-plugin/server/mocks';
+import { ruleExecutionLogMock } from '../../rule_monitoring/mocks';
+
+import {
+ getAllowedFieldForTermQueryFromMapping,
+ getAllowedFieldsForTermQuery,
+} from './get_allowed_fields_for_terms_query';
+
+const indexMapping = {
+ 'source-index': {
+ mappings: {
+ 'host.name': {
+ full_name: 'host.name',
+ mapping: {
+ name: {
+ type: 'keyword',
+ },
+ },
+ },
+ 'url.full': {
+ full_name: 'url.full',
+ mapping: {
+ full: {
+ type: 'keyword',
+ },
+ },
+ },
+ 'source.range': {
+ full_name: 'source.range',
+ mapping: {
+ range: {
+ type: 'ip_range',
+ },
+ },
+ },
+ },
+ },
+ 'other-source-index': {
+ mappings: {
+ 'host.name': {
+ full_name: 'host.name',
+ mapping: {
+ name: {
+ type: 'keyword',
+ },
+ },
+ },
+ 'host.ip': {
+ full_name: 'host.ip',
+ mapping: {
+ name: {
+ type: 'ip',
+ },
+ },
+ },
+ },
+ },
+};
+
+describe('get_allowed_fields_for_terms_query copy', () => {
+ describe('getAllowedFieldForTermQueryFromMapping', () => {
+ it('should return map of fields allowed for term query', () => {
+ const result = getAllowedFieldForTermQueryFromMapping(
+ indexMapping as IndicesGetFieldMappingResponse
+ );
+ expect(result).toEqual({
+ 'host.ip': true,
+ 'url.full': true,
+ 'host.name': true,
+ });
+ });
+ it('should disable fields if in one index type not supported', () => {
+ const result = getAllowedFieldForTermQueryFromMapping({
+ 'new-source-index': {
+ mappings: {
+ 'host.name': {
+ full_name: 'host.name',
+ mapping: {
+ name: {
+ type: 'text',
+ },
+ },
+ },
+ },
+ },
+ ...indexMapping,
+ } as IndicesGetFieldMappingResponse);
+ expect(result).toEqual({
+ 'host.ip': true,
+ 'url.full': true,
+ });
+ });
+ });
+
+ describe('getlAllowedFieldsForTermQuery', () => {
+ let alertServices: RuleExecutorServicesMock;
+ let ruleExecutionLogger: ReturnType;
+
+ beforeEach(() => {
+ alertServices = alertsMock.createRuleExecutorServices();
+ alertServices.scopedClusterClient.asCurrentUser.indices.getFieldMapping.mockResolvedValue(
+ indexMapping as IndicesGetFieldMappingResponse
+ );
+ ruleExecutionLogger = ruleExecutionLogMock.forExecutors.create();
+ });
+
+ it('should return map of fields allowed for term query for source and threat indices', async () => {
+ const threatMatchedFields = {
+ source: ['host.name', 'url.full'],
+ threat: ['host.name', 'url.full'],
+ };
+ const threatIndex = ['threat-index'];
+ const inputIndex = ['source-index'];
+
+ const result = await getAllowedFieldsForTermQuery({
+ threatMatchedFields,
+ services: alertServices,
+ threatIndex,
+ inputIndex,
+ ruleExecutionLogger,
+ });
+ expect(result).toEqual({
+ source: {
+ 'host.ip': true,
+ 'url.full': true,
+ 'host.name': true,
+ },
+ threat: {
+ 'host.ip': true,
+ 'url.full': true,
+ 'host.name': true,
+ },
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts
new file mode 100644
index 000000000000..bde234e2bdc2
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_allowed_fields_for_terms_query.ts
@@ -0,0 +1,76 @@
+/*
+ * 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 { IndicesGetFieldMappingResponse } from '@elastic/elasticsearch/lib/api/types';
+import type { AllowedFieldsForTermsQuery, GetAllowedFieldsForTermQuery } from './types';
+
+const allowedFieldTypes = ['keyword', 'constant_keyword', 'wildcard', 'ip'];
+
+/*
+ * Return map of fields allowed for term query
+ */
+export const getAllowedFieldForTermQueryFromMapping = (
+ indexMapping: IndicesGetFieldMappingResponse
+): Record => {
+ const result: Record = {};
+ const notAllowedFields: string[] = [];
+
+ const indices = Object.values(indexMapping);
+ indices.forEach((index) => {
+ Object.entries(index.mappings).forEach(([field, fieldValue]) => {
+ Object.values(fieldValue.mapping).forEach((mapping) => {
+ const fieldType = mapping?.type;
+ if (!fieldType) return;
+
+ if (allowedFieldTypes.includes(fieldType) && !notAllowedFields.includes(field)) {
+ result[field] = true;
+ } else {
+ notAllowedFields.push(field);
+ // if we the field allowed in one index, but not allowed in another, we should delete it from result
+ delete result[field];
+ }
+ });
+ });
+ });
+
+ return result;
+};
+
+/**
+ * Return map of fields allowed for term query for source and threat indices
+ */
+export const getAllowedFieldsForTermQuery = async ({
+ threatMatchedFields,
+ services,
+ threatIndex,
+ inputIndex,
+ ruleExecutionLogger,
+}: GetAllowedFieldsForTermQuery): Promise => {
+ let allowedFieldsForTermsQuery = { source: {}, threat: {} };
+ try {
+ const [sourceFieldsMapping, threatFieldsMapping] = await Promise.all([
+ services.scopedClusterClient.asCurrentUser.indices.getFieldMapping({
+ index: inputIndex,
+ fields: threatMatchedFields.source,
+ }),
+ services.scopedClusterClient.asCurrentUser.indices.getFieldMapping({
+ index: threatIndex,
+ fields: threatMatchedFields.threat,
+ }),
+ ]);
+
+ allowedFieldsForTermsQuery = {
+ source: getAllowedFieldForTermQueryFromMapping(sourceFieldsMapping),
+ threat: getAllowedFieldForTermQueryFromMapping(threatFieldsMapping),
+ };
+ } catch (e) {
+ ruleExecutionLogger.debug(`Can't get allowed fields for terms query: ${e}`);
+ return allowedFieldsForTermsQuery;
+ }
+
+ return allowedFieldsForTermsQuery;
+};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts
index 32c2f00af64e..c17696fbddf2 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts
@@ -11,7 +11,7 @@ import { getQueryFilter } from '../get_query_filter';
import { singleSearchAfter } from '../single_search_after';
import { buildEventsSearchQuery } from '../build_events_query';
-export const MAX_PER_PAGE = 3000;
+export const MAX_PER_PAGE = 9000;
export const getEventList = async ({
services,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts
index 84cf1142e9e9..63cf588ae43a 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts
@@ -17,6 +17,7 @@ import type {
LanguageOrUndefined,
Type,
} from '@kbn/securitysolution-io-ts-alerting-types';
+import type { QueryDslBoolQuery } from '@elastic/elasticsearch/lib/api/types';
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import type { OpenPointInTimeResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { ListClient } from '@kbn/lists-plugin/server';
@@ -32,7 +33,6 @@ import type {
BulkCreate,
RuleRangeTuple,
SearchAfterAndBulkCreateReturnType,
- SignalsEnrichment,
WrapHits,
OverrideBodyQuery,
} from '../types';
@@ -91,7 +91,6 @@ export interface CreateThreatSignalOptions {
savedId: string | undefined;
searchAfterSize: number;
services: RuleExecutorServices;
- threatEnrichment: SignalsEnrichment;
threatMapping: ThreatMapping;
tuple: RuleRangeTuple;
type: Type;
@@ -101,6 +100,15 @@ export interface CreateThreatSignalOptions {
secondaryTimestamp?: string;
exceptionFilter: Filter | undefined;
unprocessedExceptions: ExceptionListItemSchema[];
+ threatFilters: unknown[];
+ threatIndex: ThreatIndex;
+ threatIndicatorPath: ThreatIndicatorPath;
+ threatLanguage: ThreatLanguageOrUndefined;
+ threatQuery: ThreatQuery;
+ perPage?: number;
+ threatPitId: OpenPointInTimeResponse['id'];
+ reassignThreatPitId: (newPitId: OpenPointInTimeResponse['id'] | undefined) => void;
+ allowedFieldsForTermsQuery: AllowedFieldsForTermsQuery;
}
export interface CreateEventSignalOptions {
@@ -120,7 +128,6 @@ export interface CreateEventSignalOptions {
savedId: string | undefined;
searchAfterSize: number;
services: RuleExecutorServices;
- threatEnrichment: SignalsEnrichment;
tuple: RuleRangeTuple;
type: Type;
wrapHits: WrapHits;
@@ -138,6 +145,8 @@ export interface CreateEventSignalOptions {
secondaryTimestamp?: string;
exceptionFilter: Filter | undefined;
unprocessedExceptions: ExceptionListItemSchema[];
+ allowedFieldsForTermsQuery: AllowedFieldsForTermsQuery;
+ threatMatchedFields: ThreatMatchedFields;
}
type EntryKey = 'field' | 'value';
@@ -146,6 +155,7 @@ export interface BuildThreatMappingFilterOptions {
threatList: ThreatListItem[];
threatMapping: ThreatMapping;
entryKey: EntryKey;
+ allowedFieldsForTermsQuery?: AllowedFieldsForTermsQuery;
}
export interface FilterThreatMappingOptions {
@@ -171,6 +181,7 @@ export interface BuildEntriesMappingFilterOptions {
threatList: ThreatListItem[];
threatMapping: ThreatMapping;
entryKey: EntryKey;
+ allowedFieldsForTermsQuery?: AllowedFieldsForTermsQuery;
}
export interface SplitShouldClausesOptions {
@@ -179,7 +190,11 @@ export interface SplitShouldClausesOptions {
}
export interface BooleanFilter {
- bool: { should: unknown[]; minimum_should_match: number };
+ bool: QueryDslBoolQuery;
+}
+
+export interface TermQuery {
+ terms: Record;
}
interface ThreatListConfig {
@@ -229,12 +244,19 @@ export interface ThreatEnrichment {
matched: { id: string; index: string; field: string; atomic?: string; type: string };
}
-export interface ThreatMatchNamedQuery {
- id: string;
- index: string;
+interface BaseThreatNamedQuery {
field: string;
value: string;
+ queryType: string;
}
+export interface ThreatMatchNamedQuery extends BaseThreatNamedQuery {
+ id: string;
+ index: string;
+}
+
+export type ThreatTermNamedQuery = BaseThreatNamedQuery;
+
+export type DecodedThreatNamedQuery = BaseThreatNamedQuery & { id?: string; index?: string };
export type GetMatchedThreats = (ids: string[]) => Promise;
@@ -250,6 +272,8 @@ export interface BuildThreatEnrichmentOptions {
reassignPitId: (newPitId: OpenPointInTimeResponse['id'] | undefined) => void;
listClient: ListClient;
exceptionFilter: Filter | undefined;
+ threatMapping: ThreatMapping;
+ runtimeMappings: estypes.MappingRuntimeFields | undefined;
}
export interface EventsOptions {
@@ -303,3 +327,37 @@ export interface GetSortForThreatList {
index: string[];
listItemIndex: string;
}
+
+export enum ThreatMatchQueryType {
+ match = 'mq',
+ term = 'tq',
+}
+
+export interface ThreatMatchedFields {
+ source: string[];
+ threat: string[];
+}
+
+export interface AllowedFieldsForTermsQuery {
+ source: Record;
+ threat: Record;
+}
+
+export interface SignalValuesMap {
+ [field: string]: {
+ [fieldValue: string]: string[];
+ };
+}
+
+export interface GetAllowedFieldsForTermQuery {
+ services: RuleExecutorServices;
+ inputIndex: string[];
+ threatIndex: ThreatIndex;
+ threatMatchedFields: ThreatMatchedFields;
+ ruleExecutionLogger: IRuleExecutionLogForExecutors;
+}
+
+export interface GetSignalValuesMap {
+ eventList: EventItem[];
+ threatMatchedFields: ThreatMatchedFields;
+}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
index 0bcdc8450a83..57eae75ba4f5 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts
@@ -7,7 +7,7 @@
import type { SearchAfterAndBulkCreateReturnType } from '../types';
import { sampleSignalHit } from '../__mocks__/es_results';
-import type { ThreatMatchNamedQuery } from './types';
+import type { ThreatMatchNamedQuery, ThreatTermNamedQuery } from './types';
import {
buildExecutionIntervalValidator,
@@ -18,6 +18,8 @@ import {
combineResults,
decodeThreatMatchNamedQuery,
encodeThreatMatchNamedQuery,
+ getMatchedFields,
+ getSignalValueMap,
} from './utils';
describe('utils', () => {
@@ -705,6 +707,7 @@ describe('utils', () => {
index: 'index',
field: 'field',
value: 'value',
+ queryType: 'mq',
});
expect(typeof encoded).toEqual('string');
@@ -718,6 +721,7 @@ describe('utils', () => {
index: 'index',
field: 'threat.indicator.domain',
value: 'host.name',
+ queryType: 'mq',
};
const encoded = encodeThreatMatchNamedQuery(query);
@@ -727,6 +731,20 @@ describe('utils', () => {
expect(decoded).toEqual(query);
});
+ it('can decode if some parameters not passed', () => {
+ const query: ThreatTermNamedQuery = {
+ field: 'threat.indicator.domain',
+ value: 'host.name',
+ queryType: 'tq',
+ };
+
+ const encoded = encodeThreatMatchNamedQuery(query);
+ const decoded = decodeThreatMatchNamedQuery(encoded);
+
+ expect(decoded).not.toBe(query);
+ expect(decoded).toEqual({ ...query, id: '', index: '' });
+ });
+
it('raises an error if the input is invalid', () => {
const badInput = 'nope';
@@ -735,18 +753,33 @@ describe('utils', () => {
);
});
- it('raises an error if the query is missing a value', () => {
+ it('raises an error if the query is missing a value for match query', () => {
const badQuery: ThreatMatchNamedQuery = {
id: 'my_id',
index: 'index',
// @ts-expect-error field intentionally undefined
field: undefined,
value: 'host.name',
+ queryType: 'mq',
+ };
+ const badInput = encodeThreatMatchNamedQuery(badQuery);
+
+ expect(() => decodeThreatMatchNamedQuery(badInput)).toThrowError(
+ 'Decoded query is invalid. Decoded value: {"id":"my_id","index":"index","field":"","value":"host.name","queryType":"mq"}'
+ );
+ });
+
+ it('raises an error if the query is invalid a value for term query', () => {
+ const badQuery: ThreatTermNamedQuery = {
+ // @ts-expect-error field intentionally undefined
+ field: undefined,
+ value: 'host.name',
+ queryType: 'tq',
};
const badInput = encodeThreatMatchNamedQuery(badQuery);
expect(() => decodeThreatMatchNamedQuery(badInput)).toThrowError(
- 'Decoded query is invalid. Decoded value: {"id":"my_id","index":"index","field":"","value":"host.name"}'
+ 'Decoded query is invalid. Decoded value: {"id":"","index":"","field":"","value":"host.name","queryType":"tq"}'
);
});
});
@@ -773,4 +806,120 @@ describe('utils', () => {
);
});
});
+
+ describe('getMatchedFields', () => {
+ it('return empty fields if there no mappings', () => {
+ const fields = getMatchedFields([]);
+ expect(fields).toEqual({
+ source: [],
+ threat: [],
+ });
+ });
+
+ it('return fields for source and threat indecies', () => {
+ const fields = getMatchedFields([
+ {
+ entries: [
+ {
+ field: 'host.name',
+ type: 'mapping',
+ value: 'threat.indicator.host.name',
+ },
+ ],
+ },
+ {
+ entries: [
+ {
+ field: 'source.ip',
+ type: 'mapping',
+ value: 'threat.indicator.source.ip',
+ },
+ {
+ field: 'url.full',
+ type: 'mapping',
+ value: 'threat.indicator.url.full',
+ },
+ ],
+ },
+ ]);
+
+ expect(fields).toEqual({
+ source: ['host.name', 'source.ip', 'url.full'],
+ threat: [
+ 'threat.indicator.host.name',
+ 'threat.indicator.source.ip',
+ 'threat.indicator.url.full',
+ ],
+ });
+ });
+ });
+
+ describe('getSignalValueMap', () => {
+ it('return empty object if there no events', () => {
+ const valueMap = getSignalValueMap({
+ eventList: [],
+ threatMatchedFields: {
+ source: [],
+ threat: [],
+ },
+ });
+ expect(valueMap).toEqual({});
+ });
+
+ it('return empty object if there some events but no fields', () => {
+ const valueMap = getSignalValueMap({
+ eventList: [
+ {
+ _id: '1',
+ _index: 'index-1',
+ fields: {
+ 'host.name': ['host-1'],
+ },
+ },
+ ],
+ threatMatchedFields: {
+ source: [],
+ threat: [],
+ },
+ });
+ expect(valueMap).toEqual({});
+ });
+ it('return value map for event list and coresponding fields', () => {
+ const createEvent = (id: string, fields: Record) => ({
+ _id: id,
+ _index: `index`,
+ fields,
+ });
+ const valueMap = getSignalValueMap({
+ eventList: [
+ createEvent('1', {
+ 'host.name': ['host-1'],
+ 'source.ip': ['source-1'],
+ }),
+ createEvent('2', {
+ 'host.name': ['host-2'],
+ 'source.ip': ['source-2'],
+ }),
+ createEvent('3', {
+ 'host.name': ['host-1'],
+ 'source.ip': ['source-2'],
+ }),
+ ],
+ threatMatchedFields: {
+ source: ['host.name', 'source.ip', 'url.full'],
+ threat: [],
+ },
+ });
+ expect(valueMap).toEqual({
+ 'host.name': {
+ 'host-1': ['1', '3'],
+ 'host-2': ['2'],
+ },
+ 'source.ip': {
+ 'source-1': ['1'],
+ 'source-2': ['2', '3'],
+ },
+ });
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
index a73ead0bf946..4e97fd36033d 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts
@@ -7,9 +7,20 @@
import moment from 'moment';
+import type { ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
+import { get } from 'lodash';
import type { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../types';
import { parseInterval } from '../utils';
-import type { ThreatMatchNamedQuery, ThreatListItem } from './types';
+import { ThreatMatchQueryType } from './types';
+import type {
+ ThreatListItem,
+ ThreatMatchedFields,
+ ThreatTermNamedQuery,
+ DecodedThreatNamedQuery,
+ SignalValuesMap,
+ GetSignalValuesMap,
+ ThreatMatchNamedQuery,
+} from './types';
/**
* Given two timers this will take the max of each and add them to each other and return that addition.
@@ -129,21 +140,32 @@ export const combineConcurrentResults = (
};
const separator = '__SEP__';
-export const encodeThreatMatchNamedQuery = ({
- id,
- index,
- field,
- value,
-}: ThreatMatchNamedQuery): string => {
- return [id, index, field, value].join(separator);
+export const encodeThreatMatchNamedQuery = (
+ query: ThreatMatchNamedQuery | ThreatTermNamedQuery
+): string => {
+ const { field, value, queryType } = query;
+ let id;
+ let index;
+ if ('id' in query) {
+ id = query.id;
+ index = query.index;
+ }
+
+ return [id, index, field, value, queryType].join(separator);
};
-export const decodeThreatMatchNamedQuery = (encoded: string): ThreatMatchNamedQuery => {
+export const decodeThreatMatchNamedQuery = (encoded: string): DecodedThreatNamedQuery => {
const queryValues = encoded.split(separator);
- const [id, index, field, value] = queryValues;
- const query = { id, index, field, value };
-
- if (queryValues.length !== 4 || !queryValues.every(Boolean)) {
+ const [id, index, field, value, queryType] = queryValues;
+ const query = { id, index, field, value, queryType };
+ let isValidQuery = false;
+ if (queryType === ThreatMatchQueryType.match) {
+ isValidQuery = queryValues.length === 5 && queryValues.every(Boolean);
+ }
+ if (queryType === ThreatMatchQueryType.term) {
+ isValidQuery = Boolean(field && value);
+ }
+ if (!isValidQuery) {
const queryString = JSON.stringify(query);
throw new Error(`Decoded query is invalid. Decoded value: ${queryString}`);
}
@@ -153,7 +175,7 @@ export const decodeThreatMatchNamedQuery = (encoded: string): ThreatMatchNamedQu
export const extractNamedQueries = (
hit: SignalSourceHit | ThreatListItem
-): ThreatMatchNamedQuery[] =>
+): DecodedThreatNamedQuery[] =>
hit.matched_queries?.map((match) => decodeThreatMatchNamedQuery(match)) ?? [];
export const buildExecutionIntervalValidator: (interval: string) => () => void = (interval) => {
@@ -173,3 +195,42 @@ export const buildExecutionIntervalValidator: (interval: string) => () => void =
}
};
};
+
+/*
+ * Return list of fields by type used for matching in IM rule
+ */
+export const getMatchedFields = (threatMapping: ThreatMapping): ThreatMatchedFields =>
+ threatMapping.reduce(
+ (acc: ThreatMatchedFields, val) => {
+ val.entries.forEach((mapping) => {
+ if (!acc.source.includes(mapping.field)) {
+ acc.source.push(mapping.field);
+ }
+ if (!acc.threat.includes(mapping.value)) {
+ acc.threat.push(mapping.value);
+ }
+ });
+ return acc;
+ },
+ { source: [], threat: [] }
+ );
+
+export const getSignalValueMap = ({
+ eventList,
+ threatMatchedFields,
+}: GetSignalValuesMap): SignalValuesMap =>
+ eventList.reduce((acc, event) => {
+ threatMatchedFields.source.forEach((field) => {
+ const fieldValue = get(event.fields, field)?.[0];
+ if (!fieldValue) return;
+
+ if (!acc[field]) {
+ acc[field] = {};
+ }
+ if (!acc[field][fieldValue]) {
+ acc[field][fieldValue] = [];
+ }
+ acc[field][fieldValue].push(event._id);
+ });
+ return acc;
+ }, {});
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts
index dfa1f81f6c5d..dba116e46a75 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/threat_match.ts
@@ -75,8 +75,8 @@ export default ({ getService }: FtrProviderContext) => {
await deleteAllAlerts(supertest, log);
});
- // First test creates a real rule - remaining tests use preview API
- it('should be able to execute and get 10 signals when doing a specific query', async () => {
+ // First 2 test creates a real rule - remaining tests use preview API
+ it('should be able to execute and get 10 signals when doing a specific query (terms query)', async () => {
const rule: ThreatMatchRuleCreateProps = {
description: 'Detecting root and admin users',
name: 'Query with a rule id',
@@ -257,6 +257,192 @@ export default ({ getService }: FtrProviderContext) => {
}),
});
});
+ it('should be able to execute and get 10 signals when doing a specific query (match query)', async () => {
+ const rule: ThreatMatchRuleCreateProps = {
+ description: 'Detecting root and admin users',
+ name: 'Query with a rule id',
+ severity: 'high',
+ index: ['auditbeat-*'],
+ type: 'threat_match',
+ risk_score: 55,
+ language: 'kuery',
+ rule_id: 'rule-1',
+ from: '1900-01-01T00:00:00.000Z',
+ query: '*:*',
+ threat_query: 'source.ip: "188.166.120.93"', // narrow things down with a query to a specific source ip
+ threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity
+ threat_mapping: [
+ // We match host.name against host.name
+ {
+ entries: [
+ {
+ field: 'host.name',
+ value: 'host.name',
+ type: 'mapping',
+ },
+ {
+ field: 'host.name',
+ value: 'host.name',
+ type: 'mapping',
+ },
+ ],
+ },
+ ],
+ threat_filters: [],
+ };
+
+ const createdRule = await createRule(supertest, log, rule);
+ const alerts = await getOpenSignals(supertest, log, es, createdRule);
+ expect(alerts.hits.hits.length).equal(10);
+ const fullSource = alerts.hits.hits.find(
+ (signal) =>
+ (signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
+ );
+ const fullSignal = fullSource?._source;
+ if (!fullSignal) {
+ return expect(fullSignal).to.be.ok();
+ }
+ expect(fullSignal).eql({
+ ...fullSignal,
+ '@timestamp': fullSignal['@timestamp'],
+ agent: {
+ ephemeral_id: '1b4978a0-48be-49b1-ac96-323425b389ab',
+ hostname: 'zeek-sensor-amsterdam',
+ id: 'e52588e6-7aa3-4c89-a2c4-d6bc5c286db1',
+ type: 'auditbeat',
+ version: '8.0.0',
+ },
+ auditd: {
+ data: {
+ hostname: '46.101.47.213',
+ op: 'PAM:bad_ident',
+ terminal: 'ssh',
+ },
+ message_type: 'user_err',
+ result: 'fail',
+ sequence: 2267,
+ session: 'unset',
+ summary: {
+ actor: {
+ primary: 'unset',
+ secondary: 'root',
+ },
+ how: '/usr/sbin/sshd',
+ object: {
+ primary: 'ssh',
+ secondary: '46.101.47.213',
+ type: 'user-session',
+ },
+ },
+ },
+ cloud: {
+ instance: {
+ id: '133551048',
+ },
+ provider: 'digitalocean',
+ region: 'ams3',
+ },
+ ecs: {
+ version: '1.0.0-beta2',
+ },
+ ...flattenWithPrefix('event', {
+ action: 'error',
+ category: 'user-login',
+ module: 'auditd',
+ kind: 'signal',
+ }),
+ host: {
+ architecture: 'x86_64',
+ containerized: false,
+ hostname: 'zeek-sensor-amsterdam',
+ id: '2ce8b1e7d69e4a1d9c6bcddc473da9d9',
+ name: 'zeek-sensor-amsterdam',
+ os: {
+ codename: 'bionic',
+ family: 'debian',
+ kernel: '4.15.0-45-generic',
+ name: 'Ubuntu',
+ platform: 'ubuntu',
+ version: '18.04.2 LTS (Bionic Beaver)',
+ },
+ },
+ network: {
+ direction: 'incoming',
+ },
+ process: {
+ executable: '/usr/sbin/sshd',
+ pid: 32739,
+ },
+ service: {
+ type: 'auditd',
+ },
+ source: {
+ ip: '46.101.47.213',
+ },
+ user: {
+ audit: {
+ id: 'unset',
+ },
+ id: '0',
+ name: 'root',
+ },
+ [ALERT_ANCESTORS]: [
+ {
+ id: '7yJ-B2kBR346wHgnhlMn',
+ type: 'event',
+ index: 'auditbeat-8.0.0-2019.02.19-000001',
+ depth: 0,
+ },
+ ],
+ [ALERT_DEPTH]: 1,
+ [ALERT_ORIGINAL_EVENT_ACTION]: 'error',
+ [ALERT_ORIGINAL_EVENT_CATEGORY]: 'user-login',
+ [ALERT_ORIGINAL_EVENT_MODULE]: 'auditd',
+ [ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
+ [ALERT_REASON]:
+ 'user-login event with source 46.101.47.213 by root on zeek-sensor-amsterdam created high alert Query with a rule id.',
+ [ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
+ [ALERT_STATUS]: 'active',
+ [ALERT_UUID]: fullSignal[ALERT_UUID],
+ [ALERT_WORKFLOW_STATUS]: 'open',
+ [SPACE_IDS]: ['default'],
+ [VERSION]: fullSignal[VERSION],
+ threat: {
+ enrichments: get(fullSignal, 'threat.enrichments'),
+ },
+ ...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
+ actions: [],
+ author: [],
+ category: 'Indicator Match Rule',
+ consumer: 'siem',
+ created_by: 'elastic',
+ description: 'Detecting root and admin users',
+ enabled: true,
+ exceptions_list: [],
+ false_positives: [],
+ from: '1900-01-01T00:00:00.000Z',
+ immutable: false,
+ interval: '5m',
+ max_signals: 100,
+ name: 'Query with a rule id',
+ producer: 'siem',
+ references: [],
+ risk_score: 55,
+ risk_score_mapping: [],
+ rule_type_id: 'siem.indicatorRule',
+ severity: 'high',
+ severity_mapping: [],
+ tags: [],
+ threat: [],
+ to: 'now',
+ type: 'threat_match',
+ updated_at: fullSignal[ALERT_RULE_UPDATED_AT],
+ updated_by: 'elastic',
+ uuid: fullSignal[ALERT_RULE_UUID],
+ version: 1,
+ }),
+ });
+ });
it('should return 0 matches if the mapping does not match against anything in the mapping', async () => {
const rule: ThreatMatchRuleCreateProps = {
From 7b842280b8a6bbf540db27480a40ae899779aa79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
<55978943+cauemarcondes@users.noreply.github.com>
Date: Thu, 2 Feb 2023 12:12:36 -0500
Subject: [PATCH 28/35] [Obs] fixing news feed by using dynamic kibana version
(#150053)
closes https://github.com/elastic/kibana/issues/149986
---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../public/application/application.test.tsx | 1 +
.../public/application/index.tsx | 10 ++++++++-
.../observability/public/application/types.ts | 1 +
.../overview_page/overview_page.tsx | 6 ++++-
x-pack/plugins/observability/public/plugin.ts | 2 ++
.../public/services/get_news_feed.test.ts | 4 ++--
.../public/services/get_news_feed.ts | 22 +++++++++++++++++--
7 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx
index 5c5c6a24a03d..3320022c12b8 100644
--- a/x-pack/plugins/observability/public/application/application.test.tsx
+++ b/x-pack/plugins/observability/public/application/application.test.tsx
@@ -91,6 +91,7 @@ describe('renderApp', () => {
},
reportUiCounter: jest.fn(),
},
+ kibanaVersion: '8.7.0',
});
unmount();
}).not.toThrowError();
diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx
index 283cc195089a..7edbb74b80c8 100644
--- a/x-pack/plugins/observability/public/application/index.tsx
+++ b/x-pack/plugins/observability/public/application/index.tsx
@@ -53,6 +53,7 @@ export const renderApp = ({
ObservabilityPageTemplate,
usageCollection,
isDev,
+ kibanaVersion,
}: {
core: CoreStart;
config: ConfigSchema;
@@ -62,6 +63,7 @@ export const renderApp = ({
ObservabilityPageTemplate: React.ComponentType;
usageCollection: UsageCollectionSetup;
isDev?: boolean;
+ kibanaVersion: string;
}) => {
const { element, history, theme$ } = appMountParameters;
const i18nCore = core.i18n;
@@ -83,7 +85,13 @@ export const renderApp = ({
().services;
const { ObservabilityPageTemplate } = usePluginContext();
@@ -66,7 +67,10 @@ export function OverviewPage() {
},
]);
- const { data: newsFeed } = useFetcher(() => getNewsFeed({ http }), [http]);
+ const { data: newsFeed } = useFetcher(
+ () => getNewsFeed({ http, kibanaVersion }),
+ [http, kibanaVersion]
+ );
const { hasAnyData, isAllRequestsComplete } = useHasData();
const { trackMetric } = useOverviewMetrics({ hasAnyData });
diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts
index 17a21f41301b..332e481f0ec5 100644
--- a/x-pack/plugins/observability/public/plugin.ts
+++ b/x-pack/plugins/observability/public/plugin.ts
@@ -187,6 +187,7 @@ export class Plugin
const category = DEFAULT_APP_CATEGORIES.observability;
const euiIconType = 'logoObservability';
const config = this.initContext.config.get();
+ const kibanaVersion = this.initContext.env.packageInfo.version;
createCallObservabilityApi(coreSetup.http);
@@ -211,6 +212,7 @@ export class Plugin
ObservabilityPageTemplate: navigation.PageTemplate,
usageCollection: pluginsSetup.usageCollection,
isDev: this.initContext.env.mode.dev,
+ kibanaVersion,
});
};
diff --git a/x-pack/plugins/observability/public/services/get_news_feed.test.ts b/x-pack/plugins/observability/public/services/get_news_feed.test.ts
index 3a35e8716672..59178bd841be 100644
--- a/x-pack/plugins/observability/public/services/get_news_feed.test.ts
+++ b/x-pack/plugins/observability/public/services/get_news_feed.test.ts
@@ -25,7 +25,7 @@ describe('getNewsFeed', () => {
},
} as unknown as HttpSetup;
- const newsFeed = await getNewsFeed({ http });
+ const newsFeed = await getNewsFeed({ http, kibanaVersion: '8.7.0' });
expect(newsFeed.items).toEqual([]);
});
it('Returns array with the news feed', async () => {
@@ -92,7 +92,7 @@ describe('getNewsFeed', () => {
},
} as unknown as HttpSetup;
- const newsFeed = await getNewsFeed({ http });
+ const newsFeed = await getNewsFeed({ http, kibanaVersion: '8.7.0' });
expect(newsFeed.items.length).toEqual(3);
});
});
diff --git a/x-pack/plugins/observability/public/services/get_news_feed.ts b/x-pack/plugins/observability/public/services/get_news_feed.ts
index 31c7c6cd30ea..8916cffbd149 100644
--- a/x-pack/plugins/observability/public/services/get_news_feed.ts
+++ b/x-pack/plugins/observability/public/services/get_news_feed.ts
@@ -6,6 +6,7 @@
*/
import type { HttpSetup } from '@kbn/core/public';
+import semverCoerce from 'semver/functions/coerce';
export interface NewsItem {
title: { en: string };
@@ -17,10 +18,27 @@ export interface NewsItem {
interface NewsFeed {
items: NewsItem[];
}
+/**
+ * Removes the suffix that is sometimes appended to the Kibana version,
+ * (e.g. `8.0.0-SNAPSHOT-rc1`), which is typically only seen in non-production
+ * environments
+ */
+const removeSuffixFromVersion = (kibanaVersion?: string) =>
+ semverCoerce(kibanaVersion)?.version ?? kibanaVersion;
-export async function getNewsFeed({ http }: { http: HttpSetup }): Promise {
+export async function getNewsFeed({
+ http,
+ kibanaVersion,
+}: {
+ http: HttpSetup;
+ kibanaVersion: string;
+}): Promise {
try {
- return await http.get('https://feeds.elastic.co/observability-solution/v8.0.0.json');
+ return await http.get(
+ `https://feeds.elastic.co/observability-solution/v${removeSuffixFromVersion(
+ kibanaVersion
+ )}.json`
+ );
} catch (e) {
console.error('Error while fetching news feed', e);
return { items: [] };
From f5af84f3818ad13729aca2d1a6d81fc182e4a7bb Mon Sep 17 00:00:00 2001
From: Nav <13634519+navarone-feekery@users.noreply.github.com>
Date: Thu, 2 Feb 2023 18:24:16 +0100
Subject: [PATCH 29/35] [Enterprise Search] Add preferences to connectors
(#150165)
## Summary
Add support for new field `preferences` in `.elastic-connectors`.
---
x-pack/plugins/enterprise_search/common/types/connectors.ts | 5 +++++
.../__mocks__/search_indices.mock.ts | 2 ++
.../enterprise_search_content/__mocks__/view_index.mock.ts | 2 ++
.../server/index_management/setup_indices.test.ts | 5 +++++
.../server/index_management/setup_indices.ts | 5 +++++
.../server/lib/connectors/add_connector.test.ts | 3 +++
.../enterprise_search/server/lib/connectors/add_connector.ts | 1 +
.../server/lib/connectors/start_sync.test.ts | 2 ++
.../lib/connectors/update_connector_scheduling.test.ts | 2 ++
9 files changed, 27 insertions(+)
diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts
index 87ccbad824e1..2913423b4628 100644
--- a/x-pack/plugins/enterprise_search/common/types/connectors.ts
+++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts
@@ -27,6 +27,10 @@ export interface CustomScheduling {
export type ConnectorCustomScheduling = Record;
+export interface ConnectorPreferences extends Record {
+ extract_full_html?: boolean | null;
+}
+
export enum ConnectorStatus {
CREATED = 'created',
NEEDS_CONFIGURATION = 'needs_configuration',
@@ -150,6 +154,7 @@ export interface Connector {
last_synced: string | null;
name: string;
pipeline?: IngestPipelineParams | null;
+ preferences: ConnectorPreferences;
scheduling: {
enabled: boolean;
interval: string; // crontab syntax
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts
index 955cf219a801..c9ccc1c0e7e6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts
@@ -105,6 +105,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [
last_sync_status: SyncStatus.COMPLETED,
last_synced: null,
name: 'connector',
+ preferences: { extract_full_html: false },
scheduling: {
enabled: false,
interval: '',
@@ -200,6 +201,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [
last_sync_status: SyncStatus.COMPLETED,
last_synced: null,
name: 'crawler',
+ preferences: { extract_full_html: false },
scheduling: {
enabled: false,
interval: '',
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts
index 78ef10664abc..05211e48bf13 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts
@@ -115,6 +115,7 @@ export const connectorIndex: ConnectorViewIndex = {
last_sync_status: SyncStatus.COMPLETED,
last_synced: null,
name: 'connector',
+ preferences: { extract_full_html: false },
scheduling: {
enabled: false,
interval: '',
@@ -214,6 +215,7 @@ export const crawlerIndex: CrawlerViewIndex = {
last_sync_status: SyncStatus.COMPLETED,
last_synced: null,
name: 'crawler',
+ preferences: { extract_full_html: false },
scheduling: {
enabled: false,
interval: '',
diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts
index 6ba4d9484ea8..8c8c16e9291f 100644
--- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts
+++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts
@@ -140,6 +140,11 @@ describe('Setup Indices', () => {
run_ml_inference: { type: 'boolean' },
},
},
+ preferences: {
+ properties: {
+ extract_full_html: { type: 'boolean' },
+ },
+ },
scheduling: {
properties: {
enabled: { type: 'boolean' },
diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts
index 10ff75fcb566..488b0fc10930 100644
--- a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts
+++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts
@@ -130,6 +130,11 @@ const connectorMappingsProperties: Record = {
run_ml_inference: { type: 'boolean' },
},
},
+ preferences: {
+ properties: {
+ extract_full_html: { type: 'boolean' },
+ },
+ },
scheduling: {
properties: {
enabled: { type: 'boolean' },
diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts
index e6584c0a8b20..c3b52f53857d 100644
--- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts
@@ -154,6 +154,7 @@ describe('addConnector lib function', () => {
reduce_whitespace: true,
run_ml_inference: false,
},
+ preferences: {},
scheduling: { enabled: false, interval: '0 0 0 * * ?' },
service_type: null,
status: ConnectorStatus.CREATED,
@@ -339,6 +340,7 @@ describe('addConnector lib function', () => {
reduce_whitespace: true,
run_ml_inference: false,
},
+ preferences: {},
scheduling: { enabled: false, interval: '0 0 0 * * ?' },
service_type: null,
status: ConnectorStatus.CREATED,
@@ -446,6 +448,7 @@ describe('addConnector lib function', () => {
reduce_whitespace: true,
run_ml_inference: false,
},
+ preferences: {},
scheduling: { enabled: false, interval: '0 0 0 * * ?' },
service_type: null,
status: ConnectorStatus.CREATED,
diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts
index 507ec3fd0bb4..abe52caf453d 100644
--- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts
@@ -164,6 +164,7 @@ export const addConnector = async (
run_ml_inference: connectorsPipelineMeta.default_run_ml_inference,
}
: null,
+ preferences: {},
scheduling: { enabled: false, interval: '0 0 0 * * ?' },
service_type: input.service_type || null,
status: ConnectorStatus.CREATED,
diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts
index 25956b271245..2ea8a875bd3b 100644
--- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts
@@ -42,6 +42,7 @@ describe('addConnector lib function', () => {
last_sync_error: null,
last_sync_status: null,
last_synced: null,
+ preferences: {},
scheduling: { enabled: true, interval: '1 2 3 4 5' },
service_type: null,
status: 'not connected',
@@ -67,6 +68,7 @@ describe('addConnector lib function', () => {
last_sync_error: null,
last_sync_status: null,
last_synced: null,
+ preferences: {},
scheduling: { enabled: true, interval: '1 2 3 4 5' },
service_type: null,
status: 'not connected',
diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts
index 2fa4c0954b24..89b936a41a88 100644
--- a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts
@@ -41,6 +41,7 @@ describe('addConnector lib function', () => {
last_sync_error: null,
last_sync_status: null,
last_synced: null,
+ preferences: {},
scheduling: { enabled: false, interval: '* * * * *' },
service_type: null,
status: 'not connected',
@@ -69,6 +70,7 @@ describe('addConnector lib function', () => {
last_sync_error: null,
last_sync_status: null,
last_synced: null,
+ preferences: {},
scheduling: { enabled: true, interval: '1 2 3 4 5' },
service_type: null,
status: 'not connected',
From 6f79227f50de023262706070d2de65e676a8b1e2 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez
Date: Thu, 2 Feb 2023 10:39:45 -0700
Subject: [PATCH 30/35] [ML] Data Frame Analytics creation wizard: ensure
includes table is populated correctly on job type change (#150112)
## Summary
Fixes https://github.com/elastic/kibana/issues/147824
Fixes the request body sent to the explain api - no longer sends
unnecessary `analyzed_fields` property. This ensures the explain api
call is successful and the includes table is populated correctly with
the new job type and dependent variable.
Before:
![dfa_wizard_fields_error](https://user-images.githubusercontent.com/6446462/208556732-d1ae37f0-c3eb-4983-945b-62de0af45833.gif)
After:
https://user-images.githubusercontent.com/6446462/216168208-a68a590d-5a45-4b4b-80eb-18b811218a00.mp4
### Checklist
Delete any items that are not applicable to this PR.
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---
.../analytics_creation/components/shared/fetch_explain_data.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts
index ca334a58b36c..ebf5d2dce270 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts
@@ -28,6 +28,7 @@ export const fetchExplainData = async (formState: State['form']) => {
try {
delete jobConfig.dest;
delete jobConfig.model_memory_limit;
+ delete jobConfig.analyzed_fields;
const resp: DfAnalyticsExplainResponse = await ml.dataFrameAnalytics.explainDataFrameAnalytics(
jobConfig
);
From c61ff60835d4f700ae02514d0755381be199f892 Mon Sep 17 00:00:00 2001
From: Mark Hopkin
Date: Thu, 2 Feb 2023 17:50:43 +0000
Subject: [PATCH 31/35] [Fleet] Handle package registry with no categories
(#150182)
## Summary
When testing locally with an EPR with no categories/subcategories,
`items` is null, not a great API response but we should be able to
tolerate it without breaking the page
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../epm/screens/home/hooks/use_available_packages.tsx | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx
index f13fd592dacc..b8d54b45759d 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx
@@ -182,22 +182,23 @@ export const useAvailablePackages = () => {
);
const {
- data: eprCategories,
+ data: eprCategoriesRes,
isLoading: isLoadingCategories,
error: eprCategoryLoadingError,
} = useCategories(prereleaseIntegrationsEnabled);
+ const eprCategories = useMemo(() => eprCategoriesRes?.items || [], [eprCategoriesRes]);
// Subcategories
const subCategories = useMemo(() => {
- return eprCategories?.items.filter((item) => item.parent_id !== undefined);
- }, [eprCategories?.items]);
+ return eprCategories?.filter((item) => item.parent_id !== undefined);
+ }, [eprCategories]);
const allCategories: CategoryFacet[] = useMemo(() => {
const eprAndCustomCategories: CategoryFacet[] = isLoadingCategories
? []
: mergeCategoriesAndCount(
eprCategories
- ? (eprCategories.items as Array<{ id: string; title: string; count: number }>)
+ ? (eprCategories as Array<{ id: string; title: string; count: number }>)
: [],
cards
);
From 66e7bba497f5cf66b49c153341e419fbcc416ef5 Mon Sep 17 00:00:00 2001
From: Mark Hopkin
Date: Thu, 2 Feb 2023 17:53:07 +0000
Subject: [PATCH 32/35] [Fleet] Fix discard changes link taking user to "page
not found" (#150174)
The history blocker which we use to display the discard changes modal on
the policy editor was not adding the base path to the URL.
This is because state.pathname doesnt have the basePath infront of it,
I'm not sure why! In the unit tests it does which makes it a bit odd.
I also noticed we had duplicated the useHistoryBlock hook.
---
.../hooks/index.test.tsx | 151 ------------------
.../edit_package_policy_page/hooks/index.tsx | 45 +-----
.../hooks/use_history_block.test.tsx | 8 +-
.../hooks/use_history_block.tsx | 9 +-
.../edit_package_policy_page/index.tsx | 4 +-
5 files changed, 12 insertions(+), 205 deletions(-)
delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.test.tsx
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.test.tsx
deleted file mode 100644
index daf8177b9e73..000000000000
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.test.tsx
+++ /dev/null
@@ -1,151 +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 { act } from '@testing-library/react-hooks';
-
-import { createFleetTestRendererMock } from '../../../../../../mock';
-
-import { useHistoryBlock } from '.';
-
-describe('useHistoryBlock', () => {
- describe('without search params', () => {
- it('should not block if not edited', () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.renderHook(() => useHistoryBlock(false));
-
- act(() => renderer.mountHistory.push('/test'));
-
- const { location } = renderer.mountHistory;
- expect(location.pathname).toBe('/test');
- expect(location.search).toBe('');
- expect(renderer.startServices.overlays.openConfirm).not.toBeCalled();
- });
-
- it('should block if edited', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(true);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test',
- expect.anything()
- );
- });
-
- it('should block if edited and not navigate on cancel', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(false);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).not.toBeCalled();
- });
- });
- describe('with search params', () => {
- it('should not block if not edited', () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.renderHook(() => useHistoryBlock(false));
-
- act(() => renderer.mountHistory.push('/test?param=test'));
-
- const { location } = renderer.mountHistory;
- expect(location.pathname).toBe('/test');
- expect(location.search).toBe('?param=test');
- expect(renderer.startServices.overlays.openConfirm).not.toBeCalled();
- });
-
- it('should block if edited and navigate on confirm', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(true);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test?param=test'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test?param=test',
- expect.anything()
- );
- });
-
- it('should block if edited and not navigate on cancel', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(false);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test?param=test'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).not.toBeCalled();
- });
- });
-
- describe('with hash params', () => {
- it('should not block if not edited', () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.renderHook(() => useHistoryBlock(false));
-
- act(() => renderer.mountHistory.push('/test#/hash'));
-
- const { location } = renderer.mountHistory;
- expect(location.pathname).toBe('/test');
- expect(location.hash).toBe('#/hash');
- expect(renderer.startServices.overlays.openConfirm).not.toBeCalled();
- });
-
- it('should block if edited and navigate on confirm', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(true);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test#/hash'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test#/hash',
- expect.anything()
- );
- });
-
- it('should block if edited and not navigate on cancel', async () => {
- const renderer = createFleetTestRendererMock();
-
- renderer.startServices.overlays.openConfirm.mockResolvedValue(false);
- renderer.renderHook(() => useHistoryBlock(true));
-
- act(() => renderer.mountHistory.push('/test#/hash'));
- // needed because we have an async useEffect
- await act(() => new Promise((resolve) => resolve()));
-
- expect(renderer.startServices.overlays.openConfirm).toBeCalled();
- expect(renderer.startServices.application.navigateToUrl).not.toBeCalled();
- });
- });
-});
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.tsx
index edf04f8733ad..7b295c9f53d5 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/index.tsx
@@ -5,46 +5,5 @@
* 2.0.
*/
-import { useEffect } from 'react';
-import { useHistory } from 'react-router-dom';
-import { i18n } from '@kbn/i18n';
-
-import { useStartServices } from '../../../../hooks';
-
-export function useHistoryBlock(isEdited: boolean) {
- const history = useHistory();
- const { overlays, application } = useStartServices();
-
- useEffect(() => {
- if (!isEdited) {
- return;
- }
-
- const unblock = history.block((state) => {
- async function confirmAsync() {
- const confirmRes = await overlays.openConfirm(
- i18n.translate('xpack.fleet.editPackagePolicy.historyBlockDescription', {
- defaultMessage: `Unsaved changes will be discarded. Are you sure you would like to continue?`,
- }),
- {
- title: i18n.translate('xpack.fleet.editPackagePolicy.historyBlockTitle', {
- defaultMessage: 'Discard Changes?',
- }),
- }
- );
-
- if (confirmRes) {
- unblock();
-
- application.navigateToUrl(state.pathname + state.hash + state.search, {
- state: state.state,
- });
- }
- }
- confirmAsync();
- return false;
- });
-
- return unblock;
- }, [history, isEdited, overlays, application]);
-}
+export { useHistoryBlock } from './use_history_block';
+export { usePackagePolicyWithRelatedData } from './use_package_policy';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.test.tsx
index 91a4afbda62e..491c5f627694 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.test.tsx
@@ -11,6 +11,8 @@ import { createFleetTestRendererMock } from '../../../../../../mock';
import { useHistoryBlock } from './use_history_block';
+// our test mountHistory prepends the basePath to URLs, however useHistory state doesnt have the basePath
+// in production, so we have to prepend it to the state.pathname, this results in /mock/mock in the assertions
describe('useHistoryBlock', () => {
describe('without search params', () => {
it('should not block if not edited', () => {
@@ -38,7 +40,7 @@ describe('useHistoryBlock', () => {
expect(renderer.startServices.overlays.openConfirm).toBeCalled();
expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test',
+ '/mock/mock/test',
expect.anything()
);
});
@@ -83,7 +85,7 @@ describe('useHistoryBlock', () => {
expect(renderer.startServices.overlays.openConfirm).toBeCalled();
expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test?param=test',
+ '/mock/mock/test?param=test',
expect.anything()
);
});
@@ -129,7 +131,7 @@ describe('useHistoryBlock', () => {
expect(renderer.startServices.overlays.openConfirm).toBeCalled();
expect(renderer.startServices.application.navigateToUrl).toBeCalledWith(
- '/mock/test#/hash',
+ '/mock/mock/test#/hash',
expect.anything()
);
});
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.tsx
index edf04f8733ad..abdd287c7577 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_history_block.tsx
@@ -13,7 +13,7 @@ import { useStartServices } from '../../../../hooks';
export function useHistoryBlock(isEdited: boolean) {
const history = useHistory();
- const { overlays, application } = useStartServices();
+ const { overlays, application, http } = useStartServices();
useEffect(() => {
if (!isEdited) {
@@ -32,11 +32,10 @@ export function useHistoryBlock(isEdited: boolean) {
}),
}
);
-
if (confirmRes) {
+ const url = http.basePath.prepend(state.pathname) + state.hash + state.search;
unblock();
-
- application.navigateToUrl(state.pathname + state.hash + state.search, {
+ application.navigateToUrl(url, {
state: state.state,
});
}
@@ -46,5 +45,5 @@ export function useHistoryBlock(isEdited: boolean) {
});
return unblock;
- }, [history, isEdited, overlays, application]);
+ }, [history, isEdited, overlays, application, http.basePath]);
}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
index b066457b0aa2..8b87f64d7b6c 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx
@@ -53,9 +53,8 @@ import type { PackagePolicyEditExtensionComponentProps } from '../../../types';
import { ExperimentalFeaturesService, pkgKeyFromPackageInfo } from '../../../services';
import { generateUpdatePackagePolicyDevToolsRequest } from '../services';
-import { useHistoryBlock } from './hooks';
import { UpgradeStatusCallout } from './components';
-import { usePackagePolicyWithRelatedData } from './hooks/use_package_policy';
+import { usePackagePolicyWithRelatedData, useHistoryBlock } from './hooks';
export const EditPackagePolicyPage = memo(() => {
const {
@@ -163,7 +162,6 @@ export const EditPackagePolicyForm = memo<{
}
return '/';
}, [from, getHref, packageInfo, policyId]);
-
const successRedirectPath = useMemo(() => {
if (packageInfo && policyId) {
return from === 'package-edit' || from === 'upgrade-from-integrations-policy-list'
From 3f4f1cb8c7b622043fe8769661416c3e86261696 Mon Sep 17 00:00:00 2001
From: "Christiane (Tina) Heiligers"
Date: Thu, 2 Feb 2023 11:13:45 -0700
Subject: [PATCH 33/35] Adds deprecation notice to saved objects API docs
(#150124)
Fix https://github.com/elastic/kibana/issues/149988
---
docs/api/saved-objects.asciidoc | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/docs/api/saved-objects.asciidoc b/docs/api/saved-objects.asciidoc
index 610f18c38d62..8a598fec9c47 100644
--- a/docs/api/saved-objects.asciidoc
+++ b/docs/api/saved-objects.asciidoc
@@ -6,9 +6,16 @@ Manage {kib} saved objects, including dashboards, visualizations, and more.
WARNING: Do not write documents directly to the `.kibana` index. When you write directly
to the `.kibana` index, the data becomes corrupted and permanently breaks future {kib} versions.
-NOTE: For managing {data-sources}, use the <>.
-
The following saved objects APIs are available:
+* <> to retrieve sets of saved objects that you want to import into {kib}
+
+* <> to create sets of {kib} saved objects from a file created by the export API
+
+* <> to resolve errors from the import API
+
+* <> to rotate the encryption key for encrypted saved objects
+
+deprecated::[8.7.0,Use <> for managing data views]
* <> to retrieve a single {kib} saved object by ID
@@ -32,13 +39,10 @@ The following saved objects APIs are available:
* <> to remove multiple {kib} saved objects
-* <> to retrieve sets of saved objects that you want to import into {kib}
-
-* <> to create sets of {kib} saved objects from a file created by the export API
-
-* <> to resolve errors from the import API
-
-* <> to rotate the encryption key for encrypted saved objects
+include::saved-objects/export.asciidoc[]
+include::saved-objects/import.asciidoc[]
+include::saved-objects/resolve_import_errors.asciidoc[]
+include::saved-objects/rotate_encryption_key.asciidoc[]
include::saved-objects/get.asciidoc[]
include::saved-objects/bulk_get.asciidoc[]
@@ -49,9 +53,6 @@ include::saved-objects/update.asciidoc[]
include::saved-objects/bulk_update.asciidoc[]
include::saved-objects/delete.asciidoc[]
include::saved-objects/bulk_delete.asciidoc[]
-include::saved-objects/export.asciidoc[]
-include::saved-objects/import.asciidoc[]
-include::saved-objects/resolve_import_errors.asciidoc[]
include::saved-objects/resolve.asciidoc[]
include::saved-objects/bulk_resolve.asciidoc[]
-include::saved-objects/rotate_encryption_key.asciidoc[]
+
From eee58fa7f282a3f4dbd956a85e307ad7ac6d3843 Mon Sep 17 00:00:00 2001
From: Jordan <51442161+JordanSh@users.noreply.github.com>
Date: Thu, 2 Feb 2023 20:24:25 +0200
Subject: [PATCH 34/35] [Cloud Posture] CIS AWS - Cloud dashboard supports new
findings fields (#150086)
---
.../common/schemas/csp_finding.ts | 21 ++++++--
.../schemas/csp_rule_template_metadata.ts | 5 +-
.../cloud_security_posture/common/types.ts | 11 ++--
.../public/common/api/use_stats_api.ts | 14 ++---
.../compliance_dashboard.tsx | 3 +-
.../benchmarks_section.test.tsx | 6 +--
.../dashboard_sections/benchmarks_section.tsx | 44 +++++++++++-----
.../cluster_details_box.tsx | 20 ++++---
.../dashboard_sections/summary_section.tsx | 7 +--
.../public/pages/compliance_dashboard/mock.ts | 25 +++++++--
.../public/test/fixtures/findings_fixture.ts | 2 +
.../create_indices/benchmark_score_mapping.ts | 3 ++
.../lib/get_identifier_runtime_mapping.ts | 52 +++++++++++++++++++
.../collectors/accounts_stats_collector.ts | 44 +---------------
.../collectors/resources_stats_collector.ts | 2 +-
.../compliance_dashboard.ts | 18 ++++---
.../compliance_dashboard/get_clusters.test.ts | 9 ++--
.../compliance_dashboard/get_clusters.ts | 31 ++++++-----
.../server/tasks/findings_stats_task.ts | 7 +--
19 files changed, 205 insertions(+), 119 deletions(-)
create mode 100644 x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts
diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts
index 27690c714c0c..c95962dd0218 100644
--- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts
+++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts
@@ -12,11 +12,8 @@ import type { CspRuleTemplateMetadata } from './csp_rule_template_metadata';
export interface CspFinding {
'@timestamp': string;
cluster_id: string;
- orchestrator?: {
- cluster?: {
- name?: string;
- };
- };
+ orchestrator?: CspFindingOrchestrator;
+ cloud?: CspFindingCloud; // only available on CSPM findings
result: CspFindingResult;
resource: CspFindingResource;
rule: CspRuleTemplateMetadata;
@@ -28,6 +25,20 @@ export interface CspFinding {
};
}
+interface CspFindingOrchestrator {
+ cluster?: {
+ name?: string;
+ };
+}
+
+interface CspFindingCloud {
+ provider: 'aws';
+ account: {
+ name: string;
+ id: string;
+ };
+}
+
interface CspFindingResult {
evaluation: 'passed' | 'failed';
expected?: Record;
diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts
index 567c763f5d84..b466ed7e70d2 100644
--- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts
+++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
import { schema as rt, TypeOf } from '@kbn/config-schema';
+import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../constants';
export const cspRuleTemplateMetadataSchemaV840 = rt.object({
audit: rt.string(),
@@ -32,10 +33,12 @@ export const cspRuleTemplateMetadataSchemaV870 = rt.object({
audit: rt.string(),
benchmark: rt.object({
name: rt.string(),
+ posture_type: rt.maybe(
+ rt.oneOf([rt.literal(CSPM_POLICY_TEMPLATE), rt.literal(KSPM_POLICY_TEMPLATE)])
+ ),
id: rt.string(),
version: rt.string(),
rule_number: rt.maybe(rt.string()),
- posture_type: rt.maybe(rt.string()),
}),
default_value: rt.maybe(rt.string()),
description: rt.string(),
diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts
index a922d363d9db..4bf34b201333 100644
--- a/x-pack/plugins/cloud_security_posture/common/types.ts
+++ b/x-pack/plugins/cloud_security_posture/common/types.ts
@@ -6,8 +6,9 @@
*/
import type { PackagePolicy, AgentPolicy } from '@kbn/fleet-plugin/common';
+import { CspFinding } from './schemas/csp_finding';
import { SUPPORTED_CLOUDBEAT_INPUTS, SUPPORTED_POLICY_TEMPLATES } from './constants';
-import type { CspRuleTemplateMetadata } from './schemas/csp_rule_template_metadata';
+import { CspRuleTemplateMetadata } from './schemas/csp_rule_template_metadata';
export type Evaluation = 'passed' | 'failed' | 'NA';
/** number between 1-100 */
@@ -34,10 +35,10 @@ export interface PostureTrend extends Stats {
export interface Cluster {
meta: {
- clusterId: string;
- clusterName?: string;
- benchmarkName: string;
- benchmarkId: BenchmarkId;
+ assetIdentifierId: string;
+ cloud: CspFinding['cloud'];
+ benchmark: CspFinding['rule']['benchmark'];
+ cluster: NonNullable['cluster'];
lastUpdate: string;
};
stats: Stats;
diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts
index 14c48b7a1720..4225c6a67c0b 100644
--- a/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts
+++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts
@@ -7,8 +7,12 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { useKibana } from '../hooks/use_kibana';
-import { PosturePolicyTemplate, ComplianceDashboardData } from '../../../common/types';
-import { STATS_ROUTE_PATH } from '../../../common/constants';
+import { ComplianceDashboardData, PosturePolicyTemplate } from '../../../common/types';
+import {
+ CSPM_POLICY_TEMPLATE,
+ KSPM_POLICY_TEMPLATE,
+ STATS_ROUTE_PATH,
+} from '../../../common/constants';
// TODO: consolidate both hooks into one hook with a dynamic key
const getCspmStatsKey = ['csp_cspm_dashboard_stats'];
@@ -24,8 +28,7 @@ export const useCspmStatsApi = (
const { http } = useKibana().services;
return useQuery(
getCspmStatsKey,
- // TODO: CIS AWS - remove casting and use actual policy template instead of benchmark_id
- () => http.get(getStatsRoute('cis_aws' as PosturePolicyTemplate)),
+ () => http.get(getStatsRoute(CSPM_POLICY_TEMPLATE)),
options
);
};
@@ -36,8 +39,7 @@ export const useKspmStatsApi = (
const { http } = useKibana().services;
return useQuery(
getKspmStatsKey,
- // TODO: CIS AWS - remove casting and use actual policy template
- () => http.get(getStatsRoute('cis_k8s' as PosturePolicyTemplate)),
+ () => http.get(getStatsRoute(KSPM_POLICY_TEMPLATE)),
options
);
};
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
index 18ad2d498a69..946d4cd2a53d 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx
@@ -26,7 +26,7 @@ import {
DASHBOARD_CONTAINER,
KUBERNETES_DASHBOARD_CONTAINER,
} from './test_subjects';
-import { useCspmStatsApi, useKspmStatsApi } from '../../common/api';
+import { useCspmStatsApi, useKspmStatsApi } from '../../common/api/use_stats_api';
import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api';
import { NoFindingsStates } from '../../components/no_findings_states';
import { SummarySection } from './dashboard_sections/summary_section';
@@ -302,7 +302,6 @@ export const ComplianceDashboard = () => {
', () => {
const mockDashboardDataCopy = getMockDashboardData();
const clusterMockDataCopy = getClusterMockData();
clusterMockDataCopy.stats.postureScore = 50;
- clusterMockDataCopy.meta.clusterId = '1';
+ clusterMockDataCopy.meta.assetIdentifierId = '1';
const clusterMockDataCopy1 = getClusterMockData();
clusterMockDataCopy1.stats.postureScore = 95;
- clusterMockDataCopy1.meta.clusterId = '2';
+ clusterMockDataCopy1.meta.assetIdentifierId = '2';
const clusterMockDataCopy2 = getClusterMockData();
clusterMockDataCopy2.stats.postureScore = 45;
- clusterMockDataCopy2.meta.clusterId = '3';
+ clusterMockDataCopy2.meta.assetIdentifierId = '3';
mockDashboardDataCopy.clusters = [
clusterMockDataCopy,
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
index bef98ce3cbb8..ba3f16dad46c 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx
@@ -7,20 +7,25 @@
import React, { useMemo } from 'react';
import useLocalStorage from 'react-use/lib/useLocalStorage';
-import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, useEuiTheme } from '@elastic/eui';
import type { EuiIconProps } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, useEuiTheme } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { CloudPostureScoreChart } from '../compliance_charts/cloud_posture_score_chart';
import type {
+ Cluster,
ComplianceDashboardData,
Evaluation,
PosturePolicyTemplate,
} from '../../../../common/types';
import { LOCAL_STORAGE_DASHBOARD_CLUSTER_SORT_KEY } from '../../../common/constants';
import { RisksTable } from '../compliance_charts/risks_table';
-import { KSPM_POLICY_TEMPLATE, RULE_FAILED } from '../../../../common/constants';
+import {
+ CSPM_POLICY_TEMPLATE,
+ KSPM_POLICY_TEMPLATE,
+ RULE_FAILED,
+} from '../../../../common/constants';
import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings';
import { ClusterDetailsBox } from './cluster_details_box';
import { dashboardColumnsGrow, getPolicyTemplateQuery } from './summary_section';
@@ -31,6 +36,17 @@ import {
const CLUSTER_DEFAULT_SORT_ORDER = 'asc';
+export const getClusterIdQuery = (cluster: Cluster) => {
+ if (cluster.meta.benchmark.posture_type === CSPM_POLICY_TEMPLATE) {
+ return { 'cloud.account.name': cluster.meta.cloud?.account.name };
+ }
+ if (cluster.meta.benchmark.posture_type === 'kspm') {
+ return { cluster_id: cluster.meta.assetIdentifierId };
+ }
+
+ return {};
+};
+
export const BenchmarksSection = ({
complianceData,
dashboardType,
@@ -50,25 +66,25 @@ export const BenchmarksSection = ({
const clusterSortingIcon: EuiIconProps['type'] = isClusterSortingAsc ? 'sortUp' : 'sortDown';
- const navToFindingsByClusterAndEvaluation = (clusterId: string, evaluation: Evaluation) => {
+ const navToFindingsByClusterAndEvaluation = (cluster: Cluster, evaluation: Evaluation) => {
navToFindings({
...getPolicyTemplateQuery(dashboardType),
- cluster_id: clusterId,
+ ...getClusterIdQuery(cluster),
'result.evaluation': evaluation,
});
};
- const navToFailedFindingsByClusterAndSection = (clusterId: string, ruleSection: string) => {
+ const navToFailedFindingsByClusterAndSection = (cluster: Cluster, ruleSection: string) => {
navToFindings({
...getPolicyTemplateQuery(dashboardType),
- cluster_id: clusterId,
+ ...getClusterIdQuery(cluster),
'rule.section': ruleSection,
'result.evaluation': RULE_FAILED,
});
};
- const navToFailedFindingsByCluster = (clusterId: string) => {
- navToFindingsByClusterAndEvaluation(clusterId, RULE_FAILED);
+ const navToFailedFindingsByCluster = (cluster: Cluster) => {
+ navToFindingsByClusterAndEvaluation(cluster, RULE_FAILED);
};
const toggleClustersSortingDirection = () => {
@@ -144,8 +160,10 @@ export const BenchmarksSection = ({
{clusters.map((cluster) => (
- navToFindingsByClusterAndEvaluation(cluster.meta.clusterId, evaluation)
+ navToFindingsByClusterAndEvaluation(cluster, evaluation)
}
/>
@@ -173,13 +191,13 @@ export const BenchmarksSection = ({
data={cluster.groupedFindingsEvaluation}
maxItems={3}
onCellClick={(resourceTypeName) =>
- navToFailedFindingsByClusterAndSection(cluster.meta.clusterId, resourceTypeName)
+ navToFailedFindingsByClusterAndSection(cluster, resourceTypeName)
}
viewAllButtonTitle={i18n.translate(
'xpack.csp.dashboard.risksTable.clusterCardViewAllButtonTitle',
{ defaultMessage: 'View all failed findings for this cluster' }
)}
- onViewAllClick={() => navToFailedFindingsByCluster(cluster.meta.clusterId)}
+ onViewAllClick={() => navToFailedFindingsByCluster(cluster)}
/>
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx
index bdb21f12883b..8e8bfc2ac094 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/cluster_details_box.tsx
@@ -19,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import moment from 'moment';
import React from 'react';
import { i18n } from '@kbn/i18n';
+import { getClusterIdQuery } from './benchmarks_section';
import { INTERNAL_FEATURE_FLAGS } from '../../../../common/constants';
import { Cluster } from '../../../../common/types';
import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings';
@@ -26,18 +27,23 @@ import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon';
const defaultClusterTitle = i18n.translate(
'xpack.csp.dashboard.benchmarkSection.defaultClusterTitle',
- { defaultMessage: 'Cluster ID' }
+ { defaultMessage: 'ID' }
);
+const getClusterTitle = (cluster: Cluster) => {
+ if (cluster.meta.benchmark.posture_type === 'cspm') return cluster.meta.cloud?.account.name;
+ if (cluster.meta.benchmark.posture_type === 'kspm') return cluster.meta.cluster?.name;
+};
+
export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
const { euiTheme } = useEuiTheme();
const navToFindings = useNavigateFindings();
- const shortId = cluster.meta.clusterId.slice(0, 6);
- const title = cluster.meta.clusterName || defaultClusterTitle;
+ const shortId = cluster.meta.assetIdentifierId.slice(0, 6);
+ const title = getClusterTitle(cluster) || defaultClusterTitle;
- const handleClusterTitleClick = (clusterId: string) => {
- navToFindings({ cluster_id: clusterId });
+ const handleClusterTitleClick = () => {
+ return navToFindings(getClusterIdQuery(cluster));
};
return (
@@ -64,7 +70,7 @@ export const ClusterDetailsBox = ({ cluster }: { cluster: Cluster }) => {
}
>
- handleClusterTitleClick(cluster.meta.clusterId)} color="text">
+
{
grow={true}
style={{ justifyContent: 'flex-end', paddingBottom: euiTheme.size.m }}
>
-
+
{INTERNAL_FEATURE_FLAGS.showManageRulesMock && (
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
index f7ef39cb00f9..7a4b3b3d7fd9 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
@@ -36,10 +36,11 @@ export const dashboardColumnsGrow: Record = {
third: 8,
};
-// TODO: CIS AWS - replace query to use policy_template field when available
export const getPolicyTemplateQuery = (policyTemplate: PosturePolicyTemplate) => {
- if (policyTemplate === CSPM_POLICY_TEMPLATE) return { 'rule.benchmark.id': 'cis_aws' };
- if (policyTemplate === KSPM_POLICY_TEMPLATE) return { 'rule.benchmark.id': 'cis_k8s' };
+ if (policyTemplate === CSPM_POLICY_TEMPLATE)
+ return { 'rule.benchmark.posture_type': CSPM_POLICY_TEMPLATE };
+ if (policyTemplate === KSPM_POLICY_TEMPLATE)
+ return { 'rule.benchmark.posture_type': KSPM_POLICY_TEMPLATE };
return {};
};
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/mock.ts b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/mock.ts
index f81b52473338..19040892e7e6 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/mock.ts
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/mock.ts
@@ -5,14 +5,29 @@
* 2.0.
*/
-import { ComplianceDashboardData } from '../../../common/types';
+import { Cluster, ComplianceDashboardData } from '../../../common/types';
-export const getClusterMockData = () => ({
+export const getClusterMockData = (): Cluster => ({
meta: {
- clusterId: '8f9c5b98-cc02-4827-8c82-316e2cc25870',
- benchmarkName: 'CIS Kubernetes V1.20',
+ assetIdentifierId: '8f9c5b98-cc02-4827-8c82-316e2cc25870',
lastUpdate: '2022-11-07T13:14:34.990Z',
- benchmarkId: 'cis_k8s',
+ cloud: {
+ provider: 'aws',
+ account: {
+ name: 'build-security-dev',
+ id: '704479110758',
+ },
+ },
+ benchmark: {
+ name: 'CIS Amazon Web Services Foundations',
+ rule_number: '1.4',
+ id: 'cis_aws',
+ posture_type: 'cspm',
+ version: 'v1.5.0',
+ },
+ cluster: {
+ name: '8f9c5b98-cc02-4827-8c82-316e2cc25870',
+ },
},
stats: {
totalFailed: 17,
diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts
index f2c325787f9f..62921cd534e6 100644
--- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts
+++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts
@@ -29,6 +29,8 @@ export const getFindingsFixture = (): CspFinding & { id: string } => ({
name: 'CIS Kubernetes',
version: '1.6.0',
id: 'cis_k8s',
+ rule_number: '1.1.1',
+ posture_type: 'kspm',
},
default_value: chance.sentence(),
description: chance.paragraph(),
diff --git a/x-pack/plugins/cloud_security_posture/server/create_indices/benchmark_score_mapping.ts b/x-pack/plugins/cloud_security_posture/server/create_indices/benchmark_score_mapping.ts
index 584b23652769..b1f3e64521a3 100644
--- a/x-pack/plugins/cloud_security_posture/server/create_indices/benchmark_score_mapping.ts
+++ b/x-pack/plugins/cloud_security_posture/server/create_indices/benchmark_score_mapping.ts
@@ -26,6 +26,9 @@ export const benchmarkScoreMapping: MappingTypeMapping = {
cluster_id: {
type: 'keyword',
},
+ 'cloud.account.id': {
+ type: 'keyword',
+ },
'rule.benchmark.name': {
type: 'keyword',
},
diff --git a/x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts
new file mode 100644
index 000000000000..2e0bb0a04b5d
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/server/lib/get_identifier_runtime_mapping.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
+
+/**
+ * Creates the `asset_identifier` runtime field with the value of either
+ * `account.cloud.id` or `cluster.id` based on the value of `rule.benchmark.posture_type`
+ */
+export const getIdentifierRuntimeMapping = (): MappingRuntimeFields => ({
+ asset_identifier: {
+ type: 'keyword',
+ script: {
+ source: `
+ if (!doc.containsKey('rule.benchmark.posture_type'))
+ {
+ def identifier = doc["cluster_id"].value;
+ emit(identifier);
+ return
+ }
+ else
+ {
+ if(doc["rule.benchmark.posture_type"].size() > 0)
+ {
+ def policy_template_type = doc["rule.benchmark.posture_type"].value;
+ if (policy_template_type == "cspm")
+ {
+ def identifier = doc["cloud.account.id"].value;
+ emit(identifier);
+ return
+ }
+
+ if (policy_template_type == "kspm")
+ {
+ def identifier = doc["cluster_id"].value;
+ emit(identifier);
+ return
+ }
+ }
+
+ def identifier = doc["cluster_id"].value;
+ emit(identifier);
+ return
+ }
+ `,
+ },
+ },
+});
diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts
index 1f4f8aeaa659..1402b45f3d22 100644
--- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts
+++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/accounts_stats_collector.ts
@@ -6,7 +6,8 @@
*/
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { Logger } from '@kbn/core/server';
-import type { MappingRuntimeFields, SearchRequest } from '@elastic/elasticsearch/lib/api/types';
+import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
+import { getIdentifierRuntimeMapping } from '../../get_identifier_runtime_mapping';
import { calculatePostureScore } from '../../../../common/utils/helpers';
import type { CspmAccountsStats } from './types';
import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants';
@@ -52,47 +53,6 @@ interface AccountEntity {
};
}
-// The runtime field help to have unique identifier for CSPM and KSPM
-export const getIdentifierRuntimeMapping = (): MappingRuntimeFields => ({
- asset_identifier: {
- type: 'keyword',
- script: {
- source: `
- if (!doc.containsKey('rule.benchmark.posture_type'))
- {
- def identifier = doc["cluster_id"].value;
- emit(identifier);
- return
- }
- else
- {
- if(doc["rule.benchmark.posture_type"].size() > 0)
- {
- def policy_template_type = doc["rule.benchmark.posture_type"].value;
- if (policy_template_type == "cspm")
- {
- def identifier = doc["cloud.account.id"].value;
- emit(identifier);
- return
- }
-
- if (policy_template_type == "kspm")
- {
- def identifier = doc["cluster_id"].value;
- emit(identifier);
- return
- }
- }
-
- def identifier = doc["cluster_id"].value;
- emit(identifier);
- return
- }
- `,
- },
- },
-});
-
const getAccountsStatsQuery = (index: string): SearchRequest => ({
index,
runtime_mappings: getIdentifierRuntimeMapping(),
diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts
index 89250a55d16d..7308ab7e9159 100644
--- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts
+++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/resources_stats_collector.ts
@@ -7,9 +7,9 @@
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { Logger } from '@kbn/core/server';
import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
+import { getIdentifierRuntimeMapping } from '../../get_identifier_runtime_mapping';
import type { CspmResourcesStats } from './types';
import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants';
-import { getIdentifierRuntimeMapping } from './accounts_stats_collector';
interface ResourcesStats {
accounts: {
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts
index b59ad93d8f25..6789b8674499 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts
@@ -9,7 +9,12 @@ import { transformError } from '@kbn/securitysolution-es-utils';
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { schema } from '@kbn/config-schema';
import type { PosturePolicyTemplate, ComplianceDashboardData } from '../../../common/types';
-import { LATEST_FINDINGS_INDEX_DEFAULT_NS, STATS_ROUTE_PATH } from '../../../common/constants';
+import {
+ CSPM_POLICY_TEMPLATE,
+ KSPM_POLICY_TEMPLATE,
+ LATEST_FINDINGS_INDEX_DEFAULT_NS,
+ STATS_ROUTE_PATH,
+} from '../../../common/constants';
import { getGroupedFindingsEvaluation } from './get_grouped_findings_evaluation';
import { ClusterWithoutTrend, getClusters } from './get_clusters';
import { getStats } from './get_stats';
@@ -26,7 +31,7 @@ const getClustersTrends = (clustersWithoutTrends: ClusterWithoutTrend[], trends:
...cluster,
trend: trends.map(({ timestamp, clusters: clustersTrendData }) => ({
timestamp,
- ...clustersTrendData[cluster.meta.clusterId],
+ ...clustersTrendData[cluster.meta.assetIdentifierId],
})),
}));
@@ -35,8 +40,10 @@ const getSummaryTrend = (trends: Trends) =>
const queryParamsSchema = {
params: schema.object({
- // TODO: CIS AWS - replace with strict policy template values once available
- policy_template: schema.string(),
+ policy_template: schema.oneOf([
+ schema.literal(CSPM_POLICY_TEMPLATE),
+ schema.literal(KSPM_POLICY_TEMPLATE),
+ ]),
}),
};
@@ -64,8 +71,7 @@ export const defineGetComplianceDashboardRoute = (router: CspRouter): void =>
const query: QueryDslQueryContainer = {
bool: {
- // TODO: CIS AWS - replace filtered field to `policy_template` when available
- filter: [{ term: { 'rule.benchmark.id': policyTemplate } }],
+ filter: [{ term: { 'rule.benchmark.posture_type': policyTemplate } }],
},
};
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts
index ba97be21ad71..78bd29910cd4 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.test.ts
@@ -76,10 +76,13 @@ describe('getClustersFromAggs', () => {
{
meta: {
lastUpdate: '123',
- clusterName: 'cluster_name',
clusterId: 'cluster_id',
- benchmarkName: 'CIS Kubernetes',
- benchmarkId: 'cis_k8s',
+ assetIdentifierId: 'cluster_id',
+ benchmark: { name: 'CIS Kubernetes', id: 'cis_k8s' },
+ cloud: undefined,
+ cluster: {
+ name: 'cluster_name',
+ },
},
stats: {
totalFindings: 12,
diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts
index 388672f25409..eb1f21efed65 100644
--- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts
+++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts
@@ -22,6 +22,7 @@ import {
import type { FailedFindingsQueryResult } from './get_grouped_findings_evaluation';
import { findingsEvaluationAggsQuery, getStatsFromFindingsEvaluationsAggs } from './get_stats';
import { KeyDocCount } from './compliance_dashboard';
+import { getIdentifierRuntimeMapping } from '../../lib/get_identifier_runtime_mapping';
export interface ClusterBucket extends FailedFindingsQueryResult, KeyDocCount {
failed_findings: {
@@ -34,18 +35,19 @@ export interface ClusterBucket extends FailedFindingsQueryResult, KeyDocCount {
}
interface ClustersQueryResult {
- aggs_by_cluster_id: Aggregation;
+ aggs_by_asset_identifier: Aggregation;
}
export type ClusterWithoutTrend = Omit;
export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string): SearchRequest => ({
size: 0,
+ runtime_mappings: getIdentifierRuntimeMapping(),
query,
aggs: {
- aggs_by_cluster_id: {
+ aggs_by_asset_identifier: {
terms: {
- field: 'cluster_id',
+ field: 'asset_identifier',
},
aggs: {
latestFindingTopHit: {
@@ -65,25 +67,26 @@ export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string):
});
export const getClustersFromAggs = (clusters: ClusterBucket[]): ClusterWithoutTrend[] =>
- clusters.map((cluster) => {
- const latestFindingHit: SearchHit = cluster.latestFindingTopHit.hits.hits[0];
+ clusters.map((clusterBucket) => {
+ const latestFindingHit: SearchHit = clusterBucket.latestFindingTopHit.hits.hits[0];
if (!latestFindingHit._source) throw new Error('Missing findings top hits');
const meta = {
- clusterId: cluster.key,
- clusterName: latestFindingHit._source.orchestrator?.cluster?.name,
- benchmarkName: latestFindingHit._source.rule.benchmark.name,
- benchmarkId: latestFindingHit._source.rule.benchmark.id,
+ clusterId: clusterBucket.key,
+ assetIdentifierId: clusterBucket.key,
lastUpdate: latestFindingHit._source['@timestamp'],
+ benchmark: latestFindingHit._source.rule.benchmark,
+ cloud: latestFindingHit._source.cloud, // only available on CSPM findings
+ cluster: latestFindingHit._source.orchestrator?.cluster, // only available on KSPM findings
};
// get cluster's stats
- if (!cluster.failed_findings || !cluster.passed_findings)
- throw new Error('missing findings evaluations per cluster');
- const stats = getStatsFromFindingsEvaluationsAggs(cluster);
+ if (!clusterBucket.failed_findings || !clusterBucket.passed_findings)
+ throw new Error('missing findings evaluations per cluster bucket');
+ const stats = getStatsFromFindingsEvaluationsAggs(clusterBucket);
// get cluster's resource types aggs
- const resourcesTypesAggs = cluster.aggs_by_resource_type.buckets;
+ const resourcesTypesAggs = clusterBucket.aggs_by_resource_type.buckets;
if (!Array.isArray(resourcesTypesAggs))
throw new Error('missing aggs by resource type per cluster');
const groupedFindingsEvaluation = getFailedFindingsFromAggs(resourcesTypesAggs);
@@ -104,7 +107,7 @@ export const getClusters = async (
getClustersQuery(query, pitId)
);
- const clusters = queryResult.aggregations?.aggs_by_cluster_id.buckets;
+ const clusters = queryResult.aggregations?.aggs_by_asset_identifier.buckets;
if (!Array.isArray(clusters)) throw new Error('missing aggs by cluster id');
return getClustersFromAggs(clusters);
diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts
index 96a410d3cdae..2895fb43d0b5 100644
--- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts
+++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts
@@ -14,6 +14,7 @@ import {
import { SearchRequest } from '@kbn/data-plugin/common';
import { ElasticsearchClient } from '@kbn/core/server';
import type { Logger } from '@kbn/core/server';
+import { getIdentifierRuntimeMapping } from '../lib/get_identifier_runtime_mapping';
import { FindingsStatsTaskResult, TaskHealthStatus, ScoreByPolicyTemplateBucket } from './types';
import {
BENCHMARK_SCORE_INDEX_DEFAULT_NS,
@@ -107,14 +108,14 @@ export function taskRunner(coreStartServices: CspServerPluginStartServices, logg
const getScoreQuery = (): SearchRequest => ({
index: LATEST_FINDINGS_INDEX_DEFAULT_NS,
size: 0,
+ runtime_mappings: getIdentifierRuntimeMapping(),
query: {
match_all: {},
},
aggs: {
score_by_policy_template: {
terms: {
- // TODO: CIS AWS - replace with policy_template when available
- field: 'rule.benchmark.id',
+ field: 'rule.benchmark.posture_type',
},
aggs: {
total_findings: {
@@ -138,7 +139,7 @@ const getScoreQuery = (): SearchRequest => ({
},
score_by_cluster_id: {
terms: {
- field: 'cluster_id',
+ field: 'asset_identifier',
},
aggregations: {
total_findings: {
From 5465ac725ce5f7f14aa10538538dd3498c657698 Mon Sep 17 00:00:00 2001
From: Sander Philipse <94373878+sphilipse@users.noreply.github.com>
Date: Thu, 2 Feb 2023 19:33:31 +0100
Subject: [PATCH 35/35] [Enterprise Search] Fix bug with pagination on fetch
sync jobs (#150206)
## Summary
Fixes an API validation bug I missed when updating pagination.
---
.../server/routes/enterprise_search/connectors.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
index 382b67d7a32f..8861722205ea 100644
--- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
@@ -159,7 +159,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
connectorId: schema.string(),
}),
query: schema.object({
- page: schema.number({ defaultValue: 0, min: 0 }),
+ from: schema.number({ defaultValue: 0, min: 0 }),
size: schema.number({ defaultValue: 10, min: 0 }),
}),
},
@@ -169,7 +169,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
const result = await fetchSyncJobsByConnectorId(
client,
request.params.connectorId,
- request.query.page,
+ request.query.from,
request.query.size
);
return response.ok({ body: result });