From 3fcdc062fa0867ffa6502823e2b31f8f2ad99ac9 Mon Sep 17 00:00:00 2001 From: Brijesh Khunt <123942796+brijesh-elastic@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:20:07 +0530 Subject: [PATCH] XSOAR Connector (#212049) ## Summary XSOAR action connector, enabling users to send alerts generated by the rule detection engine to Palo Alto XSOAR for automation and remediation. ### **create connector** ![xsoar-connector](https://github.com/user-attachments/assets/14d9791b-0242-42b5-b9e4-975d7f6826cc) ### **test connector** 1. **test page** ![xsoar-params-test](https://github.com/user-attachments/assets/2bdd3b79-7f5f-4d52-836b-f458c390e55c) 2. **select playbook** ![xsoar-select-playbook](https://github.com/user-attachments/assets/23787b24-31b0-4f56-b451-0e8b42c79797) ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] 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/src/platform/packages/shared/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 - [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 - [x] 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) - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Sergi Massaneda Co-authored-by: Nastasha Solomon <79124755+nastasha-solomon@users.noreply.github.com> Co-authored-by: Elastic Machine --- docs/docset.yml | 1 + docs/reference/connectors-kibana.md | 1 + .../connectors-kibana/xsoar-action-type.md | 80 +++ docs/reference/images/xsoar-connector.png | Bin 0 -> 77720 bytes docs/reference/images/xsoar-params-test.png | Bin 0 -> 47282 bytes docs/reference/toc.yml | 3 +- .../connector_types.test.ts.snap | 291 ++++++++++ .../mocks/connector_types.ts | 1 + .../common/xsoar/constants.ts | 28 + .../stack_connectors/common/xsoar/schema.ts | 54 ++ .../stack_connectors/common/xsoar/types.ts | 26 + .../public/connector_types/index.ts | 2 + .../connector_types/xsoar/connector.test.tsx | 113 ++++ .../connector_types/xsoar/connector.tsx | 45 ++ .../public/connector_types/xsoar/constants.ts | 66 +++ .../public/connector_types/xsoar/index.ts | 8 + .../public/connector_types/xsoar/logo.tsx | 45 ++ .../connector_types/xsoar/params.test.tsx | 364 ++++++++++++ .../public/connector_types/xsoar/params.tsx | 298 ++++++++++ .../connector_types/xsoar/translations.ts | 139 +++++ .../public/connector_types/xsoar/types.ts | 11 + .../connector_types/xsoar/xsoar.test.tsx | 72 +++ .../public/connector_types/xsoar/xsoar.tsx | 45 ++ .../server/connector_types/index.ts | 2 + .../connector_types/xsoar/index.test.ts | 21 + .../server/connector_types/xsoar/index.ts | 34 ++ .../connector_types/xsoar/render.test.ts | 78 +++ .../server/connector_types/xsoar/render.ts | 47 ++ .../connector_types/xsoar/xsoar.test.ts | 527 ++++++++++++++++++ .../server/connector_types/xsoar/xsoar.ts | 143 +++++ .../stack_connectors/server/plugin.test.ts | 11 +- .../task_cost_check.test.ts.snap | 4 + .../alerting_api_integration/common/config.ts | 1 + .../server/xsoar_simulation.ts | 423 ++++++++++++++ .../tests/actions/connector_types/xsoar.ts | 384 +++++++++++++ .../group2/tests/actions/index.ts | 1 + .../check_registered_connector_types.ts | 1 + .../check_registered_task_types.ts | 1 + 38 files changed, 3368 insertions(+), 3 deletions(-) create mode 100644 docs/reference/connectors-kibana/xsoar-action-type.md create mode 100644 docs/reference/images/xsoar-connector.png create mode 100644 docs/reference/images/xsoar-params-test.png create mode 100644 x-pack/platform/plugins/shared/stack_connectors/common/xsoar/constants.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/common/xsoar/schema.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/common/xsoar/types.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.test.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/constants.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/index.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/logo.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.test.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/translations.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/types.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.test.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.tsx create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.test.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.test.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.test.ts create mode 100644 x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.ts create mode 100644 x-pack/platform/test/alerting_api_integration/common/plugins/actions_simulators/server/xsoar_simulation.ts create mode 100644 x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xsoar.ts diff --git a/docs/docset.yml b/docs/docset.yml index fc5aaacec943..4e0ce27fa936 100644 --- a/docs/docset.yml +++ b/docs/docset.yml @@ -54,6 +54,7 @@ subs: bedrock: "Amazon Bedrock" gemini: "Google Gemini" hive: "TheHive" + xsoar: "XSOAR" report-features: "reporting features" ml: "machine learning" ccs: "cross-cluster search" diff --git a/docs/reference/connectors-kibana.md b/docs/reference/connectors-kibana.md index 3bd1f2784f2c..412114428212 100644 --- a/docs/reference/connectors-kibana.md +++ b/docs/reference/connectors-kibana.md @@ -41,6 +41,7 @@ Actions are instantiations of a connector that are linked to rules and run as ba * [{{webhook}}](/reference/connectors-kibana/webhook-action-type.md): Send a request to a web service. * [{{webhook-cm}}](/reference/connectors-kibana/cases-webhook-action-type.md): Send a request to a Case Management web service. * [xMatters](/reference/connectors-kibana/xmatters-action-type.md): Send actionable alerts to on-call xMatters resources. +* [{{xsoar}}](/reference/connectors-kibana/xsoar-action-type.md): Create an incident in Cortex {{xsoar}}. ::::{note} Some connector types are paid commercial features, while others are free. For a comparison of the Elastic subscription levels, go to [the subscription page](https://www.elastic.co/subscriptions). diff --git a/docs/reference/connectors-kibana/xsoar-action-type.md b/docs/reference/connectors-kibana/xsoar-action-type.md new file mode 100644 index 000000000000..7cd890618e42 --- /dev/null +++ b/docs/reference/connectors-kibana/xsoar-action-type.md @@ -0,0 +1,80 @@ +--- +navigation_title: "{{xsoar}}" +mapped_pages: + - https://www.elastic.co/guide/en/kibana/current/xsoar-action-type.html +--- + +# {{xsoar}} connector and action [xsoar-action-type] + + +{{xsoar}} connector uses the [{{xsoar}} REST API](https://cortex-panw.stoplight.io/docs/cortex-xsoar-8/m0qlgh9inh4vk-create-or-update-an-incident) to create Cortex {{xsoar}} incidents. + + +## Create connectors in {{kib}} [define-xsoar-ui] + +You can create connectors in **{{stack-manage-app}} > {{connectors-ui}}** or as needed when you’re creating a rule. For example: + +% TO DO: Use `:class: screenshot` +![XSOAR connector](../images/xsoar-connector.png) + + +### Connector configuration [xsoar-connector-configuration] + +{{xsoar}} connectors have the following configuration properties: + +Name +: The name of the connector. + +URL +: The {{xsoar}} instance URL. + +API key +: The {{xsoar}} API key for authentication. + + ::::{note} + If you do not have an API key, refer to [Create a new API key](https://cortex-panw.stoplight.io/docs/cortex-xsoar-8/t09y7hrb5d14m-create-a-new-api-key) to make one for your {{xsoar}} instance. + :::: + +API key id +: The {{xsoar}} API key ID for authentication. (Mandatory for cloud instance users.) + + +## Test connectors [xsoar-action-configuration] + +You can test connectors as you’re creating or editing the connector in {{kib}}. For example: + +% TO DO: Use `:class: screenshot` +![XSOAR params test](../images/xsoar-params-test.png) + +{{xsoar}} actions have the following configuration properties. + +Name +: The incident name. + +Playbook +: The playbook to associate with the incident. + +Start investigation +: If turned on, will automatically start the investigation process after the incident is created. + +Severity +: The severity of the incident. Can be `Unknown`, `Informational`, `Low`, `Medium`, `High` or `Critical`. + + ::::{note} + Turn on `Keep severity from rule` to create an incident that inherits the rule's severity. + :::: + +Body +: A JSON payload that includes additional parameters to be included in the API request. + + ```json + { + "details": "This is an example incident", + "type": "Unclassified" + } + ``` + + +## Connector networking configuration [xsoar-connector-networking-configuration] + +Use the [Action configuration settings](/reference/configuration-reference/alerting-settings.md#action-settings) to customize connector networking configurations, such as proxies, certificates, or TLS settings. You can set configurations that apply to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations. diff --git a/docs/reference/images/xsoar-connector.png b/docs/reference/images/xsoar-connector.png new file mode 100644 index 0000000000000000000000000000000000000000..d72b22b8a4b75ab7fba2a37ba1ecf1d74ee0991f GIT binary patch literal 77720 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/xsoar-params-test.png b/docs/reference/images/xsoar-params-test.png new file mode 100644 index 0000000000000000000000000000000000000000..c52129ee0912db6766a80b870c8d71eaabb23867 GIT binary patch literal 47282 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 literal 0 HcmV?d00001 diff --git a/docs/reference/toc.yml b/docs/reference/toc.yml index 54499176121f..f58238c091c0 100644 --- a/docs/reference/toc.yml +++ b/docs/reference/toc.yml @@ -58,6 +58,7 @@ toc: - file: connectors-kibana/webhook-action-type.md - file: connectors-kibana/cases-webhook-action-type.md - file: connectors-kibana/xmatters-action-type.md + - file: connectors-kibana/xsoar-action-type.md - file: connectors-kibana/pre-configured-connectors.md - file: kibana-plugins.md - file: commands.md @@ -65,4 +66,4 @@ toc: - file: commands/kibana-encryption-keys.md - file: commands/kibana-verification-code.md - file: osquery-exported-fields.md - - file: osquery-manager-prebuilt-packs.md \ No newline at end of file + - file: osquery-manager-prebuilt-packs.md diff --git a/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap b/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap index 2d3a80816366..97c069717a1e 100644 --- a/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap +++ b/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap @@ -39559,3 +39559,294 @@ Object { "type": "object", } `; + +exports[`Connector type config checks detect connector type changes for: .xsoar 1`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "body": Object { + "flags": Object { + "default": null, + "error": [Function], + "presence": "optional", + }, + "matches": Array [ + Object { + "schema": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + Object { + "schema": Object { + "allow": Array [ + null, + ], + "flags": Object { + "error": [Function], + "only": true, + }, + "type": "any", + }, + }, + ], + "type": "alternatives", + }, + "createInvestigation": Object { + "flags": Object { + "error": [Function], + }, + "type": "boolean", + }, + "isRuleSeverity": Object { + "flags": Object { + "default": null, + "error": [Function], + "presence": "optional", + }, + "matches": Array [ + Object { + "schema": Object { + "flags": Object { + "default": false, + "error": [Function], + "presence": "optional", + }, + "type": "boolean", + }, + }, + Object { + "schema": Object { + "allow": Array [ + null, + ], + "flags": Object { + "error": [Function], + "only": true, + }, + "type": "any", + }, + }, + ], + "type": "alternatives", + }, + "name": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "playbookId": Object { + "flags": Object { + "default": null, + "error": [Function], + "presence": "optional", + }, + "matches": Array [ + Object { + "schema": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + Object { + "schema": Object { + "allow": Array [ + null, + ], + "flags": Object { + "error": [Function], + "only": true, + }, + "type": "any", + }, + }, + ], + "type": "alternatives", + }, + "severity": Object { + "flags": Object { + "error": [Function], + }, + "type": "number", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .xsoar 2`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "url": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .xsoar 3`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "apiKey": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "apiKeyID": Object { + "flags": Object { + "default": null, + "error": [Function], + "presence": "optional", + }, + "matches": Array [ + Object { + "schema": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + Object { + "schema": Object { + "allow": Array [ + null, + ], + "flags": Object { + "error": [Function], + "only": true, + }, + "type": "any", + }, + }, + ], + "type": "alternatives", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .xsoar 4`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "subAction": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "subActionParams": Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + "unknown": true, + }, + "keys": Object {}, + "preferences": Object { + "stripUnknown": Object { + "objects": false, + }, + }, + "type": "object", + }, + }, + "type": "object", +} +`; diff --git a/x-pack/platform/plugins/shared/actions/server/integration_tests/mocks/connector_types.ts b/x-pack/platform/plugins/shared/actions/server/integration_tests/mocks/connector_types.ts index fa0bd3f460b9..0d5c1a64590e 100644 --- a/x-pack/platform/plugins/shared/actions/server/integration_tests/mocks/connector_types.ts +++ b/x-pack/platform/plugins/shared/actions/server/integration_tests/mocks/connector_types.ts @@ -30,6 +30,7 @@ export const connectorTypes: string[] = [ '.d3security', '.resilient', '.thehive', + '.xsoar', '.sentinelone', '.crowdstrike', '.inference', diff --git a/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/constants.ts b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/constants.ts new file mode 100644 index 000000000000..87ca55b11f6b --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/constants.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const XSOAR_TITLE = i18n.translate( + 'xpack.stackConnectors.components.xsoar.connectorTypeTitle', + { + defaultMessage: 'XSOAR', + } +); +export const XSOAR_CONNECTOR_ID = '.xsoar'; +export enum SUB_ACTION { + PLAYBOOKS = 'getPlaybooks', + RUN = 'run', +} +export enum XSOARSeverity { + INFORMATIONAL = 0.5, + UNKNOWN = 0, + LOW = 1, + MEDIUM = 2, + HIGH = 3, + CRITICAL = 4, +} diff --git a/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/schema.ts b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/schema.ts new file mode 100644 index 000000000000..154b707c0d29 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/schema.ts @@ -0,0 +1,54 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { SUB_ACTION } from './constants'; + +export const ConfigSchema = schema.object({ + url: schema.string(), +}); + +export const SecretsSchema = schema.object({ + apiKey: schema.string(), + apiKeyID: schema.nullable(schema.string()), +}); + +export const XSOARPlaybooksActionParamsSchema = null; +export const XSOARPlaybooksObjectSchema = schema.object( + { + id: schema.string(), + name: schema.string(), + }, + { unknowns: 'ignore' } +); +export const XSOARPlaybooksActionResponseSchema = schema.object( + { + playbooks: schema.arrayOf(XSOARPlaybooksObjectSchema), + }, + { unknowns: 'ignore' } +); + +export const XSOARRunActionParamsSchema = schema.object({ + name: schema.string(), + playbookId: schema.nullable(schema.string()), + createInvestigation: schema.boolean(), + severity: schema.number(), + isRuleSeverity: schema.nullable(schema.boolean({ defaultValue: false })), + body: schema.nullable(schema.string()), +}); +export const XSOARRunActionResponseSchema = schema.object({}, { unknowns: 'ignore' }); + +export const ExecutorParamsSchema = schema.oneOf([ + schema.object({ + subAction: schema.literal(SUB_ACTION.PLAYBOOKS), + subActionParams: schema.literal(null), // this subaction not required any value as params + }), + schema.object({ + subAction: schema.literal(SUB_ACTION.RUN), + subActionParams: XSOARRunActionParamsSchema, + }), +]); diff --git a/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/types.ts b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/types.ts new file mode 100644 index 000000000000..58f2239dd7a5 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/common/xsoar/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TypeOf } from '@kbn/config-schema'; +import type { + ConfigSchema, + SecretsSchema, + XSOARRunActionParamsSchema, + XSOARRunActionResponseSchema, + XSOARPlaybooksObjectSchema, + XSOARPlaybooksActionResponseSchema, + ExecutorParamsSchema, +} from './schema'; + +export type Config = TypeOf; +export type Secrets = TypeOf; +export type XSOARRunActionParams = TypeOf; +export type XSOARRunActionResponse = TypeOf; +export type XSOARPlaybooksActionParams = void; +export type XSOARPlaybooksObject = TypeOf; +export type XSOARPlaybooksActionResponse = TypeOf; +export type ExecutorParams = TypeOf; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/index.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/index.ts index 1d5dec2223a3..fdf3ed516567 100644 --- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/index.ts +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/index.ts @@ -36,6 +36,7 @@ import { ExperimentalFeaturesService } from '../common/experimental_features_ser import { getSentinelOneConnectorType } from './sentinelone'; import { getTheHiveConnectorType } from './thehive'; import { getCrowdStrikeConnectorType } from './crowdstrike'; +import { getXSOARConnectorType } from './xsoar'; export interface RegistrationServices { validateEmailAddresses: ( @@ -75,6 +76,7 @@ export function registerConnectorTypes({ connectorTypeRegistry.register(getTinesConnectorType()); connectorTypeRegistry.register(getD3SecurityConnectorType()); connectorTypeRegistry.register(getTheHiveConnectorType()); + connectorTypeRegistry.register(getXSOARConnectorType()); if (ExperimentalFeaturesService.get().sentinelOneConnectorOn) { connectorTypeRegistry.register(getSentinelOneConnectorType()); diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.test.tsx new file mode 100644 index 000000000000..c7239b6b1189 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.test.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import XSOARConnectorFields from './connector'; +import { ConnectorFormTestProvider } from '../lib/test_utils'; +import { act, render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); + +describe('XSOARActionConnectorFields renders', () => { + const actionConnector = { + actionTypeId: '.xsoar', + name: 'XSOAR', + config: { + url: 'https://test.com', + }, + secrets: { + apiKey: 'apiKey', + }, + isDeprecated: false, + }; + + it('XSOAR connector fields are rendered', () => { + const { getByTestId } = render( + + {}} + /> + + ); + + expect(getByTestId('config.url-input')).toBeInTheDocument(); + expect(getByTestId('secrets.apiKey-input')).toBeInTheDocument(); + expect(getByTestId('secrets.apiKeyID-input')).toBeInTheDocument(); + }); + + describe('Validation', () => { + const onSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const tests: Array<[string, string]> = [ + ['config.url-input', 'not-valid'], + ['secrets.apiKey-input', ''], + ]; + + it('connector validation succeeds when connector config is valid', async () => { + const { getByTestId } = render( + + {}} + /> + + ); + + await act(async () => { + await userEvent.click(getByTestId('form-test-provide-submit')); + }); + + waitFor(() => { + expect(onSubmit).toBeCalledWith({ + data: { + actionTypeId: '.xsoar', + name: 'XSOAR', + config: { + url: 'https://test.com', + }, + secrets: { + apiKey: 'apiKey', + }, + isDeprecated: false, + }, + isValid: true, + }); + }); + }); + + it.each(tests)('validates correctly %p', async (field, value) => { + const res = render( + + {}} + /> + + ); + + await userEvent.clear(res.getByTestId(field)); + if (value !== '') { + await userEvent.type(res.getByTestId(field), value, { + delay: 10, + }); + } + + await userEvent.click(res.getByTestId('form-test-provide-submit')); + + expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false }); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.tsx new file mode 100644 index 000000000000..14d943c17647 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/connector.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { + ConfigFieldSchema, + SimpleConnectorForm, + SecretsFieldSchema, +} from '@kbn/triggers-actions-ui-plugin/public'; + +import { URL_LABEL, API_KEY_LABEL, API_KEY_ID_LABEL, API_KEY_ID_HELP_TEXT } from './translations'; + +const configFormSchema: ConfigFieldSchema[] = [{ id: 'url', label: URL_LABEL, isUrlField: true }]; + +const secretsFormSchema: SecretsFieldSchema[] = [ + { id: 'apiKey', label: API_KEY_LABEL, isPasswordField: true }, + { + id: 'apiKeyID', + label: API_KEY_ID_LABEL, + isPasswordField: true, + isRequired: false, + helpText: API_KEY_ID_HELP_TEXT, + }, +]; + +const XSOARConnectorFields: React.FC = ({ readOnly, isEdit }) => { + return ( + <> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { XSOARConnectorFields as default }; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/constants.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/constants.ts new file mode 100644 index 000000000000..954c9d3801b1 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/constants.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { XSOARSeverity } from '../../../common/xsoar/constants'; + +export const severityOptions = [ + { + value: XSOARSeverity.UNKNOWN, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityUnknownOptionLabel', + { + defaultMessage: 'Unknown', + } + ), + }, + { + value: XSOARSeverity.INFORMATIONAL, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityInformationalOptionLabel', + { + defaultMessage: 'Informational', + } + ), + }, + { + value: XSOARSeverity.LOW, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityLowOptionLabel', + { + defaultMessage: 'Low', + } + ), + }, + { + value: XSOARSeverity.MEDIUM, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityMediumOptionLabel', + { + defaultMessage: 'Medium', + } + ), + }, + { + value: XSOARSeverity.HIGH, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityHighOptionLabel', + { + defaultMessage: 'High', + } + ), + }, + { + value: XSOARSeverity.CRITICAL, + text: i18n.translate( + 'xpack.stackConnectors.components.xsoar.eventSelectSeverityCriticalOptionLabel', + { + defaultMessage: 'Critical', + } + ), + }, +]; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/index.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/index.ts new file mode 100644 index 000000000000..102596c8f29d --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getConnectorType as getXSOARConnectorType } from './xsoar'; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/logo.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/logo.tsx new file mode 100644 index 000000000000..f267a65fc49a --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/logo.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { LogoProps } from '../types'; + +const Logo = (props: LogoProps) => ( + + + + + + +); + +// eslint-disable-next-line import/no-default-export +export { Logo as default }; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.test.tsx new file mode 100644 index 000000000000..838b019e69f7 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.test.tsx @@ -0,0 +1,364 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { ActionConnector, ActionConnectorMode } from '@kbn/triggers-actions-ui-plugin/public/types'; +import XSOARParamsFields from './params'; +import type { UseSubActionParams } from '@kbn/triggers-actions-ui-plugin/public/application/hooks/use_sub_action'; +import { SUB_ACTION } from '../../../common/xsoar/constants'; +import { ExecutorParams, XSOARRunActionParams } from '../../../common/xsoar/types'; +import * as translations from './translations'; + +interface Result { + isLoading: boolean; + response: Record; + error: null | Error; +} + +const triggersActionsPath = '@kbn/triggers-actions-ui-plugin/public'; + +const response = { + playbooks: [ + { + id: '8db0105c-f674-4d83-8095-f95a9f61e77a', + version: 4, + cacheVersn: 0, + sequenceNumber: 33831652, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.668021556Z', + sizeInBytes: 0, + packID: '', + packName: '', + itemVersion: '', + fromServerVersion: '', + toServerVersion: '', + propagationLabels: ['all'], + definitionId: '', + vcShouldIgnore: false, + vcShouldKeepItemLegacyProdMachine: false, + commitMessage: '', + shouldCommit: false, + name: 'playbook0', + nameRaw: 'playbook0', + prevName: 'aaa', + startTaskId: '0', + tasks: { + '0': { + id: '0', + taskId: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + type: 'start', + task: { + id: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + version: 1, + cacheVersn: 0, + sequenceNumber: 13431901, + primaryTerm: 8, + modified: '2023-05-23T07:16:19.930125981Z', + sizeInBytes: 0, + }, + nextTasks: { + '#none#': ['1'], + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 50, + }, + }, + evidenceData: {}, + }, + '1': { + id: '1', + taskId: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + type: 'regular', + task: { + id: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + version: 1, + cacheVersn: 0, + sequenceNumber: 33831651, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.604271789Z', + sizeInBytes: 0, + name: 'Untitled Task 1', + description: 'commands.local.cmd.set.incident', + scriptId: 'Builtin|||setIncident', + type: 'regular', + isCommand: true, + brand: 'Builtin', + }, + scriptArguments: { + severity: { + simple: '1', + }, + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 200, + }, + }, + evidenceData: {}, + }, + }, + taskIds: ['e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', 'c28b63d3-c860-4e16-82b4-6db6b58bdee3'], + scriptIds: [], + commands: ['setIncident'], + brands: ['Builtin'], + missingScriptsIds: ['Builtin|||setIncident'], + view: { + linkLabelsPosition: {}, + paper: { + dimensions: { + height: 245, + width: 380, + x: 450, + y: 50, + }, + }, + }, + inputs: null, + outputs: null, + quiet: true, + }, + ], + tags: [ + 'Phishing', + 'Sandbox', + 'Severity', + 'Malware', + 'Remediation', + 'Job', + 'Sinkhole', + 'TIM', + 'PAN-OS', + ], + total: 1, +}; + +const mockUseSubActionPlaybooks = jest.fn().mockImplementation(() => ({ + isLoading: false, + response, + error: null, +})); +const mockUseSubAction = jest.fn]>(mockUseSubActionPlaybooks); + +const mockToasts = { danger: jest.fn(), warning: jest.fn() }; +jest.mock(triggersActionsPath, () => { + const original = jest.requireActual(triggersActionsPath); + return { + ...original, + useSubAction: (params: UseSubActionParams) => mockUseSubAction(params), + useKibana: () => ({ + ...original.useKibana(), + notifications: { toasts: mockToasts }, + }), + }; +}); + +describe('XSOARParamsFields renders', () => { + const subActionParams: XSOARRunActionParams = { + name: 'new incident', + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + createInvestigation: false, + severity: 2, + isRuleSeverity: false, + body: '', + }; + + const actionParams: ExecutorParams = { + subAction: SUB_ACTION.RUN, + subActionParams, + }; + const connector: ActionConnector = { + secrets: {}, + config: {}, + id: 'test', + actionTypeId: '.test', + name: 'Test', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false as const, + }; + + const editAction = jest.fn(); + const defaultProps = { + actionConnector: connector, + actionParams, + editAction, + errors: { name: [] }, + index: 0, + messageVariables: [], + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('New connector', () => { + it('should render empty run form', () => { + const props = { ...defaultProps, actionParams: {} }; + const { getByTestId } = render(); + + expect(getByTestId('nameInput')).toBeInTheDocument(); + expect(getByTestId('xsoar-playbookSelector')).toBeInTheDocument(); + expect(getByTestId('rule-severity-toggle')).toBeInTheDocument(); + expect(getByTestId('bodyJsonEditor')).toBeInTheDocument(); + + expect(getByTestId('rule-severity-toggle')).not.toBeChecked(); + expect(getByTestId('bodyJsonEditor')).toHaveProperty('value', ''); + + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.RUN, 0); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { createInvestigation: false, severity: 0 }, + 0 + ); + }); + + it('should render empty test form', () => { + const props = { ...defaultProps, actionParams: {}, executionMode: ActionConnectorMode.Test }; + const { getByTestId } = render(); + + expect(getByTestId('nameInput')).toBeInTheDocument(); + expect(getByTestId('xsoar-playbookSelector')).toBeInTheDocument(); + expect(getByTestId('severitySelectInput')).toBeInTheDocument(); + expect(getByTestId('bodyJsonEditor')).toBeInTheDocument(); + + expect(getByTestId('severitySelectInput')).toHaveValue('0'); + expect(getByTestId('bodyJsonEditor')).toHaveProperty('value', ''); + + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.RUN, 0); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { createInvestigation: false, severity: 0 }, + 0 + ); + }); + + it('should renders playbook selector and start investigation toggle after playbook selection', async () => { + const props = { ...defaultProps, actionParams: {} }; + render(); + + expect(mockUseSubActionPlaybooks).toHaveBeenCalledWith( + expect.objectContaining({ subAction: 'getPlaybooks' }) + ); + + await waitFor(() => { + expect(screen.getByTestId('comboBoxSearchInput')).not.toBeDisabled(); + }); + + await userEvent.click(screen.getByTestId('comboBoxSearchInput')); + expect(screen.getByText('playbook0')).toBeInTheDocument(); + await userEvent.click(screen.getByText('playbook0'), { pointerEventsCheck: 0 }); + + await waitFor(() => { + expect(editAction).toHaveBeenCalledTimes(3); + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.RUN, 0); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { createInvestigation: false, severity: 0 }, + 0 + ); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { + createInvestigation: false, + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + severity: 0, + }, + 0 + ); + }); + + expect(screen.getByTestId('createInvestigation-toggle')).toBeInTheDocument(); + expect(screen.getByTestId('createInvestigation-toggle')).not.toBeChecked(); + }); + }); + + describe('Edit connector', () => { + it('all Params fields is rendered', () => { + const { getByTestId } = render(); + + expect(mockUseSubActionPlaybooks).toHaveBeenCalledWith( + expect.objectContaining({ subAction: 'getPlaybooks' }) + ); + + expect(getByTestId('nameInput')).toBeInTheDocument(); + expect(getByTestId('xsoar-playbookSelector')).toBeInTheDocument(); + expect(getByTestId('createInvestigation-toggle')).toBeInTheDocument(); + expect(getByTestId('rule-severity-toggle')).toBeInTheDocument(); + expect(getByTestId('severitySelectInput')).toBeInTheDocument(); + expect(getByTestId('bodyJsonEditor')).toBeInTheDocument(); + + expect(getByTestId('nameInput')).toHaveValue('new incident'); + expect(getByTestId('comboBoxSearchInput')).toHaveProperty('value', 'playbook0'); + expect(getByTestId('createInvestigation-toggle')).not.toBeChecked(); + expect(getByTestId('rule-severity-toggle')).not.toBeChecked(); + expect(getByTestId('severitySelectInput')).toHaveValue('2'); + expect(getByTestId('bodyJsonEditor')).toHaveProperty('value', ''); + }); + + it('hides the severity select input when rule severity is enabled', () => { + const { getByTestId } = render(); + const ruleSeverityToggleEl = getByTestId('rule-severity-toggle'); + + fireEvent.click(ruleSeverityToggleEl); + expect(getByTestId('rule-severity-toggle')).toBeEnabled(); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { ...subActionParams, severity: 2, isRuleSeverity: true }, + 0 + ); + + expect(screen.queryByTestId('severitySelectInput')).not.toBeInTheDocument(); + }); + + it('should show warning if playbook not found', () => { + const props = { + ...defaultProps, + actionParams: { subActionParams: { ...subActionParams, playbookId: 'wrong-playbookId' } }, + }; + render(); + + expect(mockToasts.warning).toHaveBeenCalledWith({ + title: translations.PLAYBOOK_NOT_FOUND_WARNING, + }); + }); + + it('should show error when playbooks subAction has error', () => { + const errorMessage = 'something broke'; + mockUseSubActionPlaybooks.mockReturnValueOnce({ + isLoading: false, + response, + error: new Error(errorMessage), + }); + + render(); + + expect(mockToasts.danger).toHaveBeenCalledWith({ + title: translations.PLAYBOOKS_ERROR, + body: errorMessage, + }); + }); + + it('handles the case when subAction is undefined', () => { + const props = { + ...defaultProps, + actionParams: { + ...actionParams, + subAction: undefined, + }, + }; + render(); + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.RUN, 0); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.tsx new file mode 100644 index 000000000000..e044ea5c4675 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/params.tsx @@ -0,0 +1,298 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import { + useSubAction, + useKibana, + ActionParamsProps, + JsonEditorWithMessageVariables, + TextFieldWithMessageVariables, + ActionConnectorMode, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { + EuiFormRow, + EuiComboBoxOptionOption, + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiHighlight, + EuiSwitch, + EuiSelect, +} from '@elastic/eui'; +import { SUB_ACTION, XSOARSeverity } from '../../../common/xsoar/constants'; +import { + ExecutorParams, + XSOARRunActionParams, + XSOARPlaybooksActionResponse, + XSOARPlaybooksActionParams, + XSOARPlaybooksObject, +} from '../../../common/xsoar/types'; +import * as translations from './translations'; +import { severityOptions } from './constants'; + +type PlaybookOption = EuiComboBoxOptionOption; + +const createOption = (playbook: XSOARPlaybooksObject): PlaybookOption => ({ + key: playbook.id, + label: playbook.name, +}); + +const renderPlaybook = ( + { label }: PlaybookOption, + searchValue: string, + contentClassName: string +) => ( + + + {label} + + +); + +const XSOARParamsFields: React.FunctionComponent> = ({ + actionConnector, + actionParams, + editAction, + index, + errors, + messageVariables, + executionMode, +}) => { + const { toasts } = useKibana().notifications; + const isTest = executionMode === ActionConnectorMode.Test; + const incident = useMemo( + () => + (actionParams.subActionParams as XSOARRunActionParams) ?? + ({ + severity: XSOARSeverity.UNKNOWN, + createInvestigation: false, + } as unknown as XSOARRunActionParams), + + [actionParams.subActionParams] + ); + + const [connectorId, setConnectorId] = useState(actionConnector?.id); + const [selectedPlaybookOption, setSelectedPlaybookOption] = useState< + PlaybookOption | null | undefined + >(); + const [isRuleSeverity, setIsRuleSeverity] = useState(Boolean(incident.isRuleSeverity)); + const [playbooks, setPlaybooks] = useState(); + + useEffect(() => { + if (actionConnector != null && connectorId !== actionConnector.id) { + setConnectorId(actionConnector?.id); + setSelectedPlaybookOption(null); + setIsRuleSeverity(isTest ? false : true); + editAction( + 'subActionParams', + { + severity: XSOARSeverity.UNKNOWN, + createInvestigation: false, + }, + index + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionConnector]); + + useEffect(() => { + if (!actionParams.subAction) { + editAction('subAction', SUB_ACTION.RUN, index); + } + if (!actionParams.subActionParams) { + editAction('subActionParams', incident, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionParams]); + + const { + response: { playbooks: fetchedPlaybooks } = {}, + isLoading: isLoadingPlaybooks, + error: playbooksError, + } = useSubAction({ + connectorId, + subAction: 'getPlaybooks', + }); + + useEffect(() => { + if (playbooksError) { + toasts.danger({ title: translations.PLAYBOOKS_ERROR, body: playbooksError.message }); + setPlaybooks([]); + } else { + setPlaybooks(fetchedPlaybooks); + } + }, [toasts, playbooksError, fetchedPlaybooks]); + + const playbooksOptions = useMemo(() => playbooks?.map(createOption) ?? [], [playbooks]); + + useEffect(() => { + if (selectedPlaybookOption === undefined && incident.playbookId && playbooks !== undefined) { + const selectedPlaybook = playbooks.find(({ id }) => id === incident.playbookId); + if (selectedPlaybook) { + setSelectedPlaybookOption(createOption(selectedPlaybook)); + } else { + toasts.warning({ title: translations.PLAYBOOK_NOT_FOUND_WARNING }); + editAction( + 'subActionParams', + { ...incident, playbookId: undefined, createInvestigation: false }, + index + ); + } + } + + if ( + selectedPlaybookOption !== undefined && + selectedPlaybookOption?.key !== incident.playbookId + ) { + editAction( + 'subActionParams', + { + ...incident, + playbookId: selectedPlaybookOption?.key, + createInvestigation: + selectedPlaybookOption === null ? false : incident.createInvestigation, + }, + index + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedPlaybookOption, incident.playbookId, playbooks, toasts, editAction, index]); + + const selectedPlaybookOptions = useMemo( + () => (selectedPlaybookOption ? [selectedPlaybookOption] : []), + [selectedPlaybookOption] + ); + + const onChangePlaybook = useCallback(([selected]: PlaybookOption[]) => { + setSelectedPlaybookOption(selected ?? null); + }, []); + + return ( + <> + { + editAction('subActionParams', { ...incident, [key]: value }, index); + }} + messageVariables={messageVariables} + paramsProperty={'name'} + inputTargetValue={incident.name} + wrapField={true} + formRowProps={{ + label: translations.NAME_LABEL, + fullWidth: true, + helpText: '', + isInvalid: + errors.name !== undefined && + Number(errors.name.length) > 0 && + incident.name !== undefined, + error: errors.name as string, + }} + errors={errors.name as string[]} + /> + + + + {selectedPlaybookOption && ( + + { + editAction( + 'subActionParams', + { + ...incident, + createInvestigation: e.target.checked, + }, + index + ); + }} + /> + + )} + {!isTest && ( + + { + setIsRuleSeverity(e.target.checked); + editAction( + 'subActionParams', + { + ...incident, + isRuleSeverity: e.target.checked, + }, + index + ); + }} + /> + + )} + {!Boolean(isRuleSeverity) && ( + + { + editAction( + 'subActionParams', + { ...incident, severity: parseFloat(e.target.value) }, + index + ); + }} + /> + + )} + + editAction('subActionParams', { ...incident, body: json }, index) + } + dataTestSubj="xsoar-body" + onBlur={() => { + if (!incident.body) { + editAction('subActionParams', { ...incident, body: null }, index); + } + }} + /> + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { XSOARParamsFields as default }; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/translations.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/translations.ts new file mode 100644 index 000000000000..d7c096c13fef --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/translations.ts @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const URL_LABEL = i18n.translate('xpack.stackConnectors.components.xsoar.urlFieldLabel', { + defaultMessage: 'URL', +}); + +export const SELECT_MESSAGE = i18n.translate( + 'xpack.stackConnectors.components.xsoar.selectMessageText', + { + defaultMessage: 'Create an incident in XSOAR', + } +); + +export const API_KEY_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.apiKeyFieldLabel', + { + defaultMessage: 'API key', + } +); + +export const API_KEY_ID_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.apiKeyIDFieldLabel', + { + defaultMessage: 'API key ID', + } +); + +export const API_KEY_ID_HELP_TEXT = i18n.translate( + 'xpack.stackConnectors.components.xsoar.apiKeyIDFieldHelpText', + { + defaultMessage: + 'Enter the API key ID (the unique serial number for your API key) to authenticate with your XSOAR cloud instance.', + } +); + +export const NAME_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.nameFieldLabel', + { + defaultMessage: 'Name', + } +); + +export const NAME_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.error.requiredNameText', + { + defaultMessage: 'Incident name is required.', + } +); + +export const BODY_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.error.requiredBodyText', + { + defaultMessage: 'Body is required.', + } +); + +export const START_INVESTIGATION_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.startInvestigationToggleLabel', + { + defaultMessage: 'Start investigation', + } +); + +export const SEVERITY_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.severitySelectInputLabel', + { + defaultMessage: 'Severity', + } +); + +export const IS_RULE_SEVERITY_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.isRuleSeverityToggleLabel', + { + defaultMessage: 'Use severity assigned to the rule', + } +); + +export const PLAYBOOKS_ERROR = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.componentError.playbooksRequestFailed', + { + defaultMessage: 'Unable to retrieve playbooks from XSOAR.', + } +); + +export const PLAYBOOK_NOT_FOUND_WARNING = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.componentWarning.playbookNotFound', + { + defaultMessage: 'Could not find the selected playbook. Choose a different one.', + } +); + +export const BODY_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.bodyFieldLabel', + { + defaultMessage: 'Body', + } +); + +export const BODY_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.bodyCodeEditorAriaLabel', + { + defaultMessage: 'Code editor', + } +); + +export const PLAYBOOK_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.playbookFieldLabel', + { + defaultMessage: 'XSOAR playbooks', + } +); + +export const PLAYBOOK_HELP = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.playbookHelp', + { + defaultMessage: 'The XSOAR playbook to associate with incident', + } +); + +export const PLAYBOOK_PLACEHOLDER = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.playbookPlaceholder', + { + defaultMessage: 'Select a playbook', + } +); + +export const PLAYBOOK_ARIA_LABEL = i18n.translate( + 'xpack.stackConnectors.components.xsoar.params.playbookFieldAriaLabel', + { + defaultMessage: 'Select an XSOAR playbook.', + } +); diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/types.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/types.ts new file mode 100644 index 000000000000..a5db454b163c --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/types.ts @@ -0,0 +1,11 @@ +/* + * 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 { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; +import type { Config, Secrets, ExecutorParams } from '../../../common/xsoar/types'; + +export type XSOARConnector = ConnectorTypeModel; diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.test.tsx new file mode 100644 index 000000000000..eee0077270ce --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '..'; +import { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { experimentalFeaturesMock, registrationServicesMock } from '../../mocks'; +import { SUB_ACTION } from '../../../common/xsoar/constants'; +import { ExperimentalFeaturesService } from '../../common/experimental_features_service'; +import * as translations from './translations'; + +const CONNECTOR_TYPE_ID = '.xsoar'; +let connectorTypeModel: ConnectorTypeModel; +beforeAll(() => { + const connectorTypeRegistry = new TypeRegistry(); + ExperimentalFeaturesService.init({ experimentalFeatures: experimentalFeaturesMock }); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(CONNECTOR_TYPE_ID); + if (getResult !== null) { + connectorTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('action type static data is as expected', () => { + expect(connectorTypeModel.id).toEqual(CONNECTOR_TYPE_ID); + }); +}); + +describe('XSOAR RUN action params validation', () => { + test('RUN action params validation succeeds when action params is valid', async () => { + const actionParams = { + subAction: SUB_ACTION.RUN, + subActionParams: { + name: 'new incident', + playbookId: 'playbook0', + createInvestigation: false, + severity: 1, + body: '', + }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + name: [], + }, + }); + }); + + test('RUN action params validation fails when required fields is not valid', async () => { + const actionParams = { + subAction: SUB_ACTION.RUN, + subActionParams: { + name: '', + playbookId: 'playbook0', + createInvestigation: false, + severity: 1, + body: '', + }, + }; + + expect(await connectorTypeModel.validateParams(actionParams)).toEqual({ + errors: { + name: [translations.NAME_REQUIRED], + }, + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.tsx new file mode 100644 index 000000000000..a7f9d88d85de --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/xsoar/xsoar.tsx @@ -0,0 +1,45 @@ +/* + * 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 { lazy } from 'react'; +import { GenericValidationResult } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { XSOARConnector } from './types'; +import { XSOAR_CONNECTOR_ID, SUB_ACTION, XSOAR_TITLE } from '../../../common/xsoar/constants'; +import { ExecutorParams } from '../../../common/xsoar/types'; +import * as i18n from './translations'; + +interface ValidationErrors { + name: string[]; +} + +export function getConnectorType(): XSOARConnector { + return { + id: XSOAR_CONNECTOR_ID, + hideInUi: true, + iconClass: lazy(() => import('./logo')), + selectMessage: i18n.SELECT_MESSAGE, + actionTypeTitle: XSOAR_TITLE, + validateParams: async ( + actionParams: ExecutorParams + ): Promise> => { + const translations = await import('./translations'); + const errors: ValidationErrors = { + name: [], + }; + const { subAction, subActionParams } = actionParams; + + if (subAction === SUB_ACTION.RUN) { + if (!subActionParams?.name?.length) { + errors.name.push(translations.NAME_REQUIRED); + } + } + return { errors }; + }, + actionConnectorFields: lazy(() => import('./connector')), + actionParamsFields: lazy(() => import('./params')), + }; +} diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/index.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/index.ts index f75e485f47f0..6cba51cfd6d4 100644 --- a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/index.ts +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/index.ts @@ -32,6 +32,7 @@ import { getConnectorType as getXmattersConnectorType } from './xmatters'; import { getConnectorType as getTeamsConnectorType } from './teams'; import { getConnectorType as getD3SecurityConnectorType } from './d3security'; import { getConnectorType as getTheHiveConnectorType } from './thehive'; +import { getConnectorType as getXSOARConnectorType } from './xsoar'; import { getOpsgenieConnectorType } from './opsgenie'; import type { ActionParamsType as ServiceNowITSMActionParams } from './servicenow_itsm'; import type { ActionParamsType as ServiceNowSIRActionParams } from './servicenow_sir'; @@ -114,6 +115,7 @@ export function registerConnectorTypes({ actions.registerSubActionConnectorType(getD3SecurityConnectorType()); actions.registerSubActionConnectorType(getResilientConnectorType()); actions.registerSubActionConnectorType(getTheHiveConnectorType()); + actions.registerSubActionConnectorType(getXSOARConnectorType()); if (experimentalFeatures.sentinelOneConnectorOn) { actions.registerSubActionConnectorType(getSentinelOneConnectorType()); diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.test.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.test.ts new file mode 100644 index 000000000000..2e5f21cc39ab --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.test.ts @@ -0,0 +1,21 @@ +/* + * 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 { XSOARConnectorType } from '.'; +import { getConnectorType } from '.'; + +let connectorType: XSOARConnectorType; + +describe('XSOAR Connector', () => { + beforeEach(() => { + connectorType = getConnectorType(); + }); + test('exposes the connector as `XSOAR` with id `.xsoar`', () => { + expect(connectorType.id).toEqual('.xsoar'); + expect(connectorType.name).toEqual('XSOAR'); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.ts new file mode 100644 index 000000000000..a6efcd704fb2 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/index.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SubActionConnectorType } from '@kbn/actions-plugin/server/sub_action_framework/types'; +import { ValidatorType } from '@kbn/actions-plugin/server/sub_action_framework/types'; +import { SecurityConnectorFeatureId } from '@kbn/actions-plugin/common'; +import { urlAllowListValidator } from '@kbn/actions-plugin/server'; +import { XSOAR_CONNECTOR_ID, XSOAR_TITLE } from '../../../common/xsoar/constants'; +import { ConfigSchema, SecretsSchema } from '../../../common/xsoar/schema'; +import type { Config, Secrets } from '../../../common/xsoar/types'; +import { XSOARConnector } from './xsoar'; +import { renderParameterTemplates } from './render'; + +export type XSOARConnectorType = SubActionConnectorType; + +export function getConnectorType(): XSOARConnectorType { + return { + id: XSOAR_CONNECTOR_ID, + minimumLicenseRequired: 'platinum', + name: XSOAR_TITLE, + getService: (params) => new XSOARConnector(params), + supportedFeatureIds: [SecurityConnectorFeatureId], + schema: { + config: ConfigSchema, + secrets: SecretsSchema, + }, + renderParameterTemplates, + validators: [{ type: ValidatorType.CONFIG, validator: urlAllowListValidator('url') }], + }; +} diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.test.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.test.ts new file mode 100644 index 000000000000..9330f6f7ab00 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { renderParameterTemplates } from './render'; +import { SUB_ACTION } from '../../../common/xsoar/constants'; +import Mustache from 'mustache'; + +const params = { + subAction: SUB_ACTION.RUN, + subActionParams: { + name: 'new incident - {{alert.uuid}}', + playbookId: 'playbook0', + createInvestigation: true, + severity: 0, + isRuleSeverity: true, + body: '', + }, +}; + +const variables = { + url: 'https://example.com', + context: { rule: { severity: 'medium' } }, + alert: { uuid: 'test123' }, +}; +const logger = loggingSystemMock.createLogger(); + +describe('XSOAR - renderParameterTemplates', () => { + it('should rendered subActionParams with variables', () => { + const result = renderParameterTemplates(logger, params, variables); + + expect(result.subActionParams).toEqual({ + name: `new incident - ${variables.alert.uuid}`, + playbookId: 'playbook0', + createInvestigation: true, + severity: 2, + isRuleSeverity: true, + body: '', + }); + }); + + it('should not use rule severity if isRuleSeverity is false', () => { + const paramswithoutRuleSeverity = { + ...params, + subActionParams: { ...params.subActionParams, isRuleSeverity: false }, + }; + const result = renderParameterTemplates(logger, paramswithoutRuleSeverity, variables); + + expect(result.subActionParams).toEqual({ + name: `new incident - ${variables.alert.uuid}`, + playbookId: 'playbook0', + createInvestigation: true, + severity: 0, + isRuleSeverity: false, + body: '', + }); + }); + + it('should render error body', () => { + const errorMessage = 'test error'; + jest.spyOn(Mustache, 'render').mockImplementation(() => { + throw new Error(errorMessage); + }); + const result = renderParameterTemplates(logger, params, variables); + expect(result.subActionParams).toEqual({ + body: 'error rendering mustache template "": test error', + createInvestigation: true, + name: 'error rendering mustache template "new incident - {{alert.uuid}}": test error', + playbookId: 'error rendering mustache template "playbook0": test error', + severity: 0, + isRuleSeverity: true, + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.ts new file mode 100644 index 000000000000..3fd15de45429 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/render.ts @@ -0,0 +1,47 @@ +/* + * 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 { ExecutorParams } from '@kbn/actions-plugin/server/sub_action_framework/types'; +import { + renderMustacheObject, + renderMustacheString, +} from '@kbn/actions-plugin/server/lib/mustache_renderer'; +import type { RenderParameterTemplates } from '@kbn/actions-plugin/server/types'; + +function mapSeverity(severity: string): number { + switch (severity) { + case 'low': + return 1; + case 'medium': + return 2; + case 'high': + return 3; + case 'critical': + return 4; + default: + return 0; + } +} + +export const renderParameterTemplates: RenderParameterTemplates = ( + logger, + params, + variables +) => { + return { + ...params, + subActionParams: { + ...renderMustacheObject(logger, params.subActionParams, variables), + severity: + params.subActionParams.isRuleSeverity === true + ? mapSeverity( + renderMustacheString(logger, '{{context.rule.severity}}', variables, 'json') + ) + : params.subActionParams.severity, + }, + }; +}; diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.test.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.test.ts new file mode 100644 index 000000000000..bff78b07acc2 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.test.ts @@ -0,0 +1,527 @@ +/* + * 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 { XSOARConnector } from './xsoar'; +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { XSOAR_CONNECTOR_ID } from '../../../common/xsoar/constants'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { + XSOARRunActionResponseSchema, + XSOARPlaybooksActionResponseSchema, +} from '../../../common/xsoar/schema'; +import type { XSOARRunActionParams } from '../../../common/xsoar/types'; +import { ConnectorUsageCollector } from '@kbn/actions-plugin/server/types'; + +const mockTime = new Date('2025-02-20T10:10:30.000'); + +describe('XSOARConnector', () => { + const logger = loggingSystemMock.createLogger(); + + const connector = new XSOARConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '1', type: XSOAR_CONNECTOR_ID }, + config: { url: 'https://example.com' }, + secrets: { apiKey: 'test123', apiKeyID: null }, + logger, + services: actionsMock.createServices(), + }); + + const cloudConnector = new XSOARConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '2', type: XSOAR_CONNECTOR_ID }, + config: { url: 'https://test.com' }, + secrets: { apiKey: 'test123', apiKeyID: '123' }, + logger, + services: actionsMock.createServices(), + }); + + let mockRequest: jest.Mock; + let mockCloudRequest: jest.Mock; + let mockError: jest.Mock; + let connectorUsageCollector: ConnectorUsageCollector; + + beforeAll(() => { + jest.useFakeTimers(); + jest.setSystemTime(mockTime); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(() => { + mockError = jest.fn().mockImplementation(() => { + throw new Error('API Error'); + }); + jest.clearAllMocks(); + connectorUsageCollector = new ConnectorUsageCollector({ + logger, + connectorId: 'test-connector-id', + }); + }); + + describe('getPlaybooks', () => { + const mockResponse = { + data: { + playbooks: [ + { + id: '8db0105c-f674-4d83-8095-f95a9f61e77a', + version: 4, + cacheVersn: 0, + sequenceNumber: 33831652, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.668021556Z', + sizeInBytes: 0, + packID: '', + packName: '', + itemVersion: '', + fromServerVersion: '', + toServerVersion: '', + propagationLabels: ['all'], + definitionId: '', + vcShouldIgnore: false, + vcShouldKeepItemLegacyProdMachine: false, + commitMessage: '', + shouldCommit: false, + name: 'aaa', + nameRaw: 'aaa', + prevName: 'aaa', + startTaskId: '0', + tasks: { + '0': { + id: '0', + taskId: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + type: 'start', + task: { + id: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + version: 1, + cacheVersn: 0, + sequenceNumber: 13431901, + primaryTerm: 8, + modified: '2023-05-23T07:16:19.930125981Z', + sizeInBytes: 0, + }, + nextTasks: { + '#none#': ['1'], + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 50, + }, + }, + evidenceData: {}, + }, + '1': { + id: '1', + taskId: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + type: 'regular', + task: { + id: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + version: 1, + cacheVersn: 0, + sequenceNumber: 33831651, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.604271789Z', + sizeInBytes: 0, + name: 'Untitled Task 1', + description: 'commands.local.cmd.set.incident', + scriptId: 'Builtin|||setIncident', + type: 'regular', + isCommand: true, + brand: 'Builtin', + }, + scriptArguments: { + severity: { + simple: '1', + }, + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 200, + }, + }, + evidenceData: {}, + }, + }, + taskIds: [ + 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + ], + scriptIds: [], + commands: ['setIncident'], + brands: ['Builtin'], + missingScriptsIds: ['Builtin|||setIncident'], + view: { + linkLabelsPosition: {}, + paper: { + dimensions: { + height: 245, + width: 380, + x: 450, + y: 50, + }, + }, + }, + inputs: null, + outputs: null, + quiet: true, + }, + ], + tags: [ + 'Endpoint', + 'ITDR', + 'Automated', + 'Phishing', + 'Sandbox', + 'Joe Security', + 'Severity', + 'Malware', + 'Sumo Logic', + 'Remediation', + 'Job', + 'Code42 Incydr', + 'Sinkhole', + 'XDR', + 'TIM', + 'PAN-OS', + 'Vulnerability', + 'Virus', + 'Domaintools', + ], + total: 1, + }, + }; + + beforeEach(() => { + mockRequest = jest.fn().mockResolvedValue(mockResponse); + mockCloudRequest = jest.fn().mockResolvedValue(mockResponse); + // @ts-ignore + connector.request = mockRequest; + // @ts-ignore + cloudConnector.request = mockCloudRequest; + jest.clearAllMocks(); + }); + + it('XSOAR API call is successful with correct parameters', async () => { + const response = await connector.getPlaybooks(undefined, connectorUsageCollector); + expect(mockRequest).toBeCalledTimes(1); + expect(mockRequest).toHaveBeenCalledWith( + { + method: 'post', + url: 'https://example.com/playbook/search', + data: {}, + responseSchema: XSOARPlaybooksActionResponseSchema, + headers: { + Authorization: 'test123', + }, + timeout: 15000, + }, + connectorUsageCollector + ); + expect(response).toEqual(mockResponse.data); + }); + + it('Auth headers are correctly set for cloud instance', async () => { + const response = await cloudConnector.getPlaybooks(undefined, connectorUsageCollector); + expect(mockCloudRequest).toBeCalledTimes(1); + expect(mockCloudRequest).toHaveBeenCalledWith( + { + method: 'post', + url: 'https://test.com/xsoar/public/v1/playbook/search', + data: {}, + responseSchema: XSOARPlaybooksActionResponseSchema, + headers: { + Authorization: 'test123', + 'x-xdr-auth-id': '123', + }, + timeout: 15000, + }, + connectorUsageCollector + ); + expect(response).toEqual(mockResponse.data); + }); + + it('errors during API calls are properly handled', async () => { + // @ts-ignore + connector.request = mockError; + + await expect(connector.getPlaybooks(undefined, connectorUsageCollector)).rejects.toThrow( + 'API Error' + ); + }); + }); + + describe('run', () => { + const mockResponse = { + data: { + id: '178791', + version: 0, + cacheVersn: 0, + modified: '1970-01-01T00:00:00Z', + sizeInBytes: 0, + CustomFields: { + bmcassignee: [{}], + bmccustomer: [{}], + bmcrequester: [{}], + containmentsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 30, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + crowdstrikefalconbehaviourpatterndispositiondetails: [{}, {}, {}], + datadogcloudsiem: [{}, {}, {}], + dataminrpulserelatedterms: [{}, {}, {}], + decyfirdatadetails: [{}, {}, {}], + detectionsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 20, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + domaintoolsirisdetect: [{}, {}, {}], + endpoint: [{}], + externalid: '178791', + extrahoprevealxdetectiondevices: [{}, {}, {}], + extrahoprevealxmitretechniques: [{}, {}, {}], + filerelationships: [{}, {}, {}], + fortisiemattacktactics: [{}, {}], + fortisiemevents: [{}], + incidentduration: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 0, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + incidentrdpachehuntingstringssimilarity: [{}, {}, {}], + incidentrdpcachehuntingstringsifter: [{}, {}, {}], + inventasource: [{}], + microsoftsentinelowner: [], + qintelqwatchexposures: [{}, {}, {}], + remediationsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 7200, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + rsametasevents: [], + rsarawlogslist: [], + securitypolicymatch: [{}], + similarincidentsdbot: [{}], + spycloudcompassdevicedata: [{}, {}, {}], + suspiciousexecutions: [{}, {}, {}], + timetoassignment: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 0, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + triagesla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 30, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + urlsslverification: [], + xdralertsearchresults: [{}, {}, {}], + xdrinvestigationresults: [ + {}, + {}, + {}, + { + columnheader1: '', + }, + {}, + { + columnheader1: '', + }, + {}, + {}, + ], + xpanseserviceclassifications: [{}, {}, {}], + xpanseservicevalidation: [ + { + columnheader1: '', + }, + {}, + {}, + ], + }, + account: '', + autime: 1713700028107000000, + type: 'Unclassified', + rawType: 'Unclassified', + name: 'My test incident', + rawName: 'My test incident', + status: 0, + custom_status: '', + resolution_status: '', + reason: '', + created: '2024-04-21T11:47:08.107Z', + occurred: '2024-04-21T11:47:08.107982676Z', + closed: '0001-01-01T00:00:00Z', + sla: 0, + severity: 2, + investigationId: '', + labels: [ + { + value: '', + type: 'Instance', + }, + { + value: 'Manual', + type: 'Brand', + }, + ], + attachment: null, + details: 'My test incident', + openDuration: 0, + lastOpen: '0001-01-01T00:00:00Z', + closingUserId: '', + owner: '', + activated: '0001-01-01T00:00:00Z', + closeReason: '', + rawCloseReason: '', + closeNotes: '', + playbookId: 'playbook0', + dueDate: '2024-05-01T11:47:08.107988742Z', + reminder: '0001-01-01T00:00:00Z', + runStatus: '', + notifyTime: '0001-01-01T00:00:00Z', + phase: '', + rawPhase: '', + isPlayground: false, + rawJSON: '', + parent: '', + parentXDRIncident: '', + retained: false, + category: '', + rawCategory: '', + linkedIncidents: null, + linkedCount: 0, + droppedCount: 0, + sourceInstance: '', + sourceBrand: 'Manual', + canvases: null, + lastJobRunTime: '0001-01-01T00:00:00Z', + feedBased: false, + dbotMirrorId: '', + dbotMirrorInstance: '', + dbotMirrorDirection: '', + dbotDirtyFields: null, + dbotCurrentDirtyFields: null, + dbotMirrorTags: null, + dbotMirrorLastSync: '0001-01-01T00:00:00Z', + isDebug: false, + }, + }; + + beforeEach(() => { + mockRequest = jest.fn().mockResolvedValue(mockResponse); + mockCloudRequest = jest.fn().mockResolvedValue(mockResponse); + // @ts-ignore + connector.request = mockRequest; + // @ts-ignore + cloudConnector.request = mockCloudRequest; + jest.clearAllMocks(); + }); + + const incident: XSOARRunActionParams = { + name: 'My test incident', + playbookId: 'playbook0', + createInvestigation: false, + severity: 2, + isRuleSeverity: false, + body: JSON.stringify({}), + }; + + const malformedIncident: XSOARRunActionParams = { + name: 'My test incident 2', + playbookId: 'playbook0', + createInvestigation: false, + isRuleSeverity: false, + severity: 2, + body: '{', + }; + + const { body, isRuleSeverity, ...incidentWithoutBody } = incident; + const expectedIncident = { ...JSON.parse(body || '{}'), ...incidentWithoutBody }; + + it('XSOAR API call is successful with correct parameters', async () => { + await connector.run(incident, connectorUsageCollector); + expect(mockRequest).toBeCalledTimes(1); + expect(mockRequest).toHaveBeenCalledWith( + { + url: 'https://example.com/incident', + method: 'post', + responseSchema: XSOARRunActionResponseSchema, + data: expectedIncident, + headers: { + Authorization: 'test123', + }, + }, + connectorUsageCollector + ); + }); + + it('errors during API calls are properly handled', async () => { + // @ts-ignore + connector.request = mockError; + + await expect(connector.run(expectedIncident, connectorUsageCollector)).rejects.toThrow( + 'API Error' + ); + }); + + it('error when malformed incident is passed', async () => { + await expect(connector.run(malformedIncident, connectorUsageCollector)).rejects.toThrowError( + `Error parsing Body: SyntaxError: Expected property name or '}' in JSON at position 1` + ); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.ts new file mode 100644 index 000000000000..6972c6e36cd3 --- /dev/null +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/xsoar/xsoar.ts @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ServiceParams } from '@kbn/actions-plugin/server'; +import { SubActionConnector } from '@kbn/actions-plugin/server'; +import type { ConnectorUsageCollector } from '@kbn/actions-plugin/server/types'; +import type { AxiosError } from 'axios'; + +import type { + Config, + Secrets, + XSOARRunActionParams, + XSOARPlaybooksActionResponse, +} from '../../../common/xsoar/types'; +import { + XSOARRunActionResponseSchema, + XSOARPlaybooksActionResponseSchema, + XSOARPlaybooksActionParamsSchema, + XSOARRunActionParamsSchema, +} from '../../../common/xsoar/schema'; +import { SUB_ACTION } from '../../../common/xsoar/constants'; + +export const CLOUD_API_PATH = '/xsoar/public/v1'; +export const INCIDENT_PATH = '/incident'; +export const PLAYBOOKS_PATH = '/playbook/search'; + +export class XSOARConnector extends SubActionConnector { + private urls: { + playbooks: string; + incident: string; + }; + private isCloud: boolean; + private ConnectorId: string; + + constructor(params: ServiceParams) { + super(params); + + this.isCloud = this.secrets.apiKeyID !== null && this.secrets.apiKeyID !== ''; + this.urls = { + playbooks: this.isCloud + ? `${this.config.url}${CLOUD_API_PATH}${PLAYBOOKS_PATH}` + : `${this.config.url}${PLAYBOOKS_PATH}`, + incident: this.isCloud + ? `${this.config.url}${CLOUD_API_PATH}${INCIDENT_PATH}` + : `${this.config.url}${INCIDENT_PATH}`, + }; + this.ConnectorId = params.connector.id; + this.registerSubActions(); + } + + private registerSubActions() { + this.registerSubAction({ + name: SUB_ACTION.PLAYBOOKS, + method: 'getPlaybooks', + schema: XSOARPlaybooksActionParamsSchema, + }); + + this.registerSubAction({ + name: SUB_ACTION.RUN, + method: 'run', + schema: XSOARRunActionParamsSchema, + }); + } + + private getAuthHeaders() { + return this.isCloud + ? { Authorization: this.secrets.apiKey, 'x-xdr-auth-id': this.secrets.apiKeyID } + : { Authorization: this.secrets.apiKey }; + } + + protected getResponseErrorMessage(error: AxiosError): string { + if (error.response?.statusText) { + return `API Error: ${error.response?.statusText}`; + } + return error.toString(); + } + + private formatIncidentBody(incident: XSOARRunActionParams) { + try { + const { body, isRuleSeverity, ...incidentWithoutBody } = incident; + const bodyJson = JSON.parse(body || '{}'); + const mergedIncident = { ...bodyJson, ...incidentWithoutBody }; + + return mergedIncident; + } catch (err) { + const errMessage = i18n.translate('xpack.stackConnectors.xsoar.BodyParsingErrorMessage', { + defaultMessage: 'error triggering XSOAR workflow, parsing body', + }); + + this.logger.error(`error on ${this.ConnectorId} XSOAR event: ${errMessage}: ${err.message}`); + + throw new Error( + i18n.translate('xpack.stackConnectors.xsoar.incidentBodyParsingError', { + defaultMessage: 'Error parsing Body: {err}', + values: { + err: err.toString(), + }, + }) + ); + } + } + + public async run( + incident: XSOARRunActionParams, + connectorUsageCollector: ConnectorUsageCollector + ) { + const mergedIncident = this.formatIncidentBody(incident); + await this.request( + { + method: 'post', + url: `${this.urls.incident}`, + data: mergedIncident, + headers: this.getAuthHeaders(), + responseSchema: XSOARRunActionResponseSchema, + }, + connectorUsageCollector + ); + } + + public async getPlaybooks( + params: unknown, + connectorUsageCollector: ConnectorUsageCollector + ): Promise { + const res = await this.request( + { + method: 'post', + url: `${this.urls.playbooks}`, + data: {}, + headers: this.getAuthHeaders(), + responseSchema: XSOARPlaybooksActionResponseSchema, + timeout: 15000, + }, + connectorUsageCollector + ); + + return res.data; + } +} diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/plugin.test.ts b/x-pack/platform/plugins/shared/stack_connectors/server/plugin.test.ts index 9aa56f1cf019..5d850ef70c55 100644 --- a/x-pack/platform/plugins/shared/stack_connectors/server/plugin.test.ts +++ b/x-pack/platform/plugins/shared/stack_connectors/server/plugin.test.ts @@ -141,7 +141,7 @@ describe('Stack Connectors Plugin', () => { name: 'Torq', }) ); - expect(actionsSetup.registerSubActionConnectorType).toHaveBeenCalledTimes(12); + expect(actionsSetup.registerSubActionConnectorType).toHaveBeenCalledTimes(13); expect(actionsSetup.registerSubActionConnectorType).toHaveBeenNthCalledWith( 1, expect.objectContaining({ @@ -200,13 +200,20 @@ describe('Stack Connectors Plugin', () => { ); expect(actionsSetup.registerSubActionConnectorType).toHaveBeenNthCalledWith( 9, + expect.objectContaining({ + id: '.xsoar', + name: 'XSOAR', + }) + ); + expect(actionsSetup.registerSubActionConnectorType).toHaveBeenNthCalledWith( + 10, expect.objectContaining({ id: '.sentinelone', name: 'Sentinel One', }) ); expect(actionsSetup.registerSubActionConnectorType).toHaveBeenNthCalledWith( - 10, + 11, expect.objectContaining({ id: '.crowdstrike', name: 'CrowdStrike', diff --git a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap index 3ee56a75faf3..1b8c968d3d1a 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap +++ b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap @@ -122,6 +122,10 @@ Array [ "cost": 1, "taskType": "actions:.xmatters", }, + Object { + "cost": 1, + "taskType": "actions:.xsoar", + }, Object { "cost": 10, "taskType": "alerting:siem.indicatorRule", diff --git a/x-pack/platform/test/alerting_api_integration/common/config.ts b/x-pack/platform/test/alerting_api_integration/common/config.ts index 8d60b7885500..45439923be7e 100644 --- a/x-pack/platform/test/alerting_api_integration/common/config.ts +++ b/x-pack/platform/test/alerting_api_integration/common/config.ts @@ -68,6 +68,7 @@ const enabledActionTypes = [ '.tines', '.webhook', '.xmatters', + '.xsoar', '.torq', 'test.sub-action-connector', 'test.sub-action-connector-without-sub-actions', diff --git a/x-pack/platform/test/alerting_api_integration/common/plugins/actions_simulators/server/xsoar_simulation.ts b/x-pack/platform/test/alerting_api_integration/common/plugins/actions_simulators/server/xsoar_simulation.ts new file mode 100644 index 000000000000..fa1490252f40 --- /dev/null +++ b/x-pack/platform/test/alerting_api_integration/common/plugins/actions_simulators/server/xsoar_simulation.ts @@ -0,0 +1,423 @@ +/* + * 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 http from 'http'; +import type { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, +} from '@kbn/core/server'; +import type { ProxyArgs } from './simulator'; +import { Simulator } from './simulator'; + +export const XSOARPlaybook0 = { + id: '8db0105c-f674-4d83-8095-f95a9f61e77a', + version: 4, + cacheVersn: 0, + sequenceNumber: 33831652, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.668021556Z', + sizeInBytes: 0, + packID: '', + packName: '', + itemVersion: '', + fromServerVersion: '', + toServerVersion: '', + propagationLabels: ['all'], + definitionId: '', + vcShouldIgnore: false, + vcShouldKeepItemLegacyProdMachine: false, + commitMessage: '', + shouldCommit: false, + name: 'playbook0', + nameRaw: 'playbook0', + prevName: 'aaa', + startTaskId: '0', + tasks: { + '0': { + id: '0', + taskId: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + type: 'start', + task: { + id: 'e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', + version: 1, + cacheVersn: 0, + sequenceNumber: 13431901, + primaryTerm: 8, + modified: '2023-05-23T07:16:19.930125981Z', + sizeInBytes: 0, + }, + nextTasks: { + '#none#': ['1'], + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 50, + }, + }, + evidenceData: {}, + }, + '1': { + id: '1', + taskId: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + type: 'regular', + task: { + id: 'c28b63d3-c860-4e16-82b4-6db6b58bdee3', + version: 1, + cacheVersn: 0, + sequenceNumber: 33831651, + primaryTerm: 11, + modified: '2023-12-12T13:51:15.604271789Z', + sizeInBytes: 0, + name: 'Untitled Task 1', + description: 'commands.local.cmd.set.incident', + scriptId: 'Builtin|||setIncident', + type: 'regular', + isCommand: true, + brand: 'Builtin', + }, + scriptArguments: { + severity: { + simple: '1', + }, + }, + continueOnErrorType: '', + view: { + position: { + x: 450, + y: 200, + }, + }, + evidenceData: {}, + }, + }, + taskIds: ['e228a044-2ad5-4ab0-873a-d5bb94a5c1b4', 'c28b63d3-c860-4e16-82b4-6db6b58bdee3'], + scriptIds: [], + commands: ['setIncident'], + brands: ['Builtin'], + missingScriptsIds: ['Builtin|||setIncident'], + view: { + linkLabelsPosition: {}, + paper: { + dimensions: { + height: 245, + width: 380, + x: 450, + y: 50, + }, + }, + }, + inputs: null, + outputs: null, + quiet: true, +}; + +export const XSOARPlaybooksResponse = { + playbooks: [XSOARPlaybook0], + tags: [ + 'Phishing', + 'Sandbox', + 'Severity', + 'Malware', + 'Remediation', + 'Job', + 'Sinkhole', + 'TIM', + 'PAN-OS', + ], + total: 1, +}; + +export const XSOARRunSuccessResponse = { + id: '178791', + version: 0, + cacheVersn: 0, + modified: '1970-01-01T00:00:00Z', + sizeInBytes: 0, + CustomFields: { + bmcassignee: [{}], + bmccustomer: [{}], + bmcrequester: [{}], + containmentsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 30, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + crowdstrikefalconbehaviourpatterndispositiondetails: [{}, {}, {}], + datadogcloudsiem: [{}, {}, {}], + dataminrpulserelatedterms: [{}, {}, {}], + decyfirdatadetails: [{}, {}, {}], + detectionsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 20, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + domaintoolsirisdetect: [{}, {}, {}], + endpoint: [{}], + externalid: '178791', + extrahoprevealxdetectiondevices: [{}, {}, {}], + extrahoprevealxmitretechniques: [{}, {}, {}], + filerelationships: [{}, {}, {}], + fortisiemattacktactics: [{}, {}], + fortisiemevents: [{}], + incidentduration: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 0, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + incidentrdpachehuntingstringssimilarity: [{}, {}, {}], + incidentrdpcachehuntingstringsifter: [{}, {}, {}], + inventasource: [{}], + microsoftsentinelowner: [], + qintelqwatchexposures: [{}, {}, {}], + remediationsla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 7200, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + rsametasevents: [], + rsarawlogslist: [], + securitypolicymatch: [{}], + similarincidentsdbot: [{}], + spycloudcompassdevicedata: [{}, {}, {}], + suspiciousexecutions: [{}, {}, {}], + timetoassignment: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 0, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + triagesla: { + accumulatedPause: 0, + breachTriggered: false, + dueDate: '0001-01-01T00:00:00Z', + endDate: '0001-01-01T00:00:00Z', + lastPauseDate: '0001-01-01T00:00:00Z', + runStatus: 'idle', + sla: 30, + slaStatus: -1, + startDate: '0001-01-01T00:00:00Z', + totalDuration: 0, + }, + urlsslverification: [], + xdralertsearchresults: [{}, {}, {}], + xdrinvestigationresults: [ + {}, + {}, + {}, + { + columnheader1: '', + }, + {}, + { + columnheader1: '', + }, + {}, + {}, + ], + xpanseserviceclassifications: [{}, {}, {}], + xpanseservicevalidation: [ + { + columnheader1: '', + }, + {}, + {}, + ], + }, + account: '', + autime: 1713700028107000000, + type: 'Unclassified', + rawType: 'Unclassified', + name: 'My test incident', + rawName: 'My test incident', + status: 0, + custom_status: '', + resolution_status: '', + reason: '', + created: '2024-04-21T11:47:08.107Z', + occurred: '2024-04-21T11:47:08.107982676Z', + closed: '0001-01-01T00:00:00Z', + sla: 0, + severity: 2, + investigationId: '', + labels: [ + { + value: '', + type: 'Instance', + }, + { + value: 'Manual', + type: 'Brand', + }, + ], + attachment: null, + details: 'My test incident', + openDuration: 0, + lastOpen: '0001-01-01T00:00:00Z', + closingUserId: '', + owner: '', + activated: '0001-01-01T00:00:00Z', + closeReason: '', + rawCloseReason: '', + closeNotes: '', + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + dueDate: '2024-05-01T11:47:08.107988742Z', + reminder: '0001-01-01T00:00:00Z', + runStatus: '', + notifyTime: '0001-01-01T00:00:00Z', + phase: '', + rawPhase: '', + isPlayground: false, + rawJSON: '', + parent: '', + parentXDRIncident: '', + retained: false, + category: '', + rawCategory: '', + linkedIncidents: null, + linkedCount: 0, + droppedCount: 0, + sourceInstance: '', + sourceBrand: 'Manual', + canvases: null, + lastJobRunTime: '0001-01-01T00:00:00Z', + feedBased: false, + dbotMirrorId: '', + dbotMirrorInstance: '', + dbotMirrorDirection: '', + dbotDirtyFields: null, + dbotCurrentDirtyFields: null, + dbotMirrorTags: null, + dbotMirrorLastSync: '0001-01-01T00:00:00Z', + isDebug: false, +}; + +export const XSOARFailedResponse = { + id: 'incCreateErr', + status: 400, + title: 'Failed creating incident', + detail: 'Failed creating incident', + error: '', + encrypted: false, + multires: null, +}; + +export class XSOARSimulator extends Simulator { + private readonly returnError: boolean; + + constructor({ returnError = false, proxy }: { returnError?: boolean; proxy?: ProxyArgs }) { + super(proxy); + + this.returnError = returnError; + } + + public async handler( + request: http.IncomingMessage, + response: http.ServerResponse, + data: Record + ) { + if (this.returnError) { + return XSOARSimulator.sendErrorResponse(response); + } + return XSOARSimulator.sendResponse(request, response); + } + + private static sendResponse(request: http.IncomingMessage, response: http.ServerResponse) { + response.setHeader('Content-Type', 'application/json'); + let body; + if (request.url?.match('/incident')) { + response.statusCode = 201; + body = XSOARRunSuccessResponse; + } else if (request.url?.match('/playbook/search')) { + response.statusCode = 200; + body = XSOARPlaybooksResponse; + } + response.end(JSON.stringify(body, null, 4)); + } + + private static sendErrorResponse(response: http.ServerResponse) { + response.statusCode = 400; + response.setHeader('Content-Type', 'application/json'); + response.end(JSON.stringify(XSOARFailedResponse, null, 4)); + } +} + +export function initPlugin(router: IRouter, path: string) { + router.post( + { + path: `${path}/playbook/search`, + options: { + authRequired: false, + }, + validate: {}, + security: { authz: { enabled: false, reason: 'This route is opted out from authorization' } }, + }, + async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return res.ok({ body: XSOARPlaybooksResponse }); + } + ); + + router.post( + { + path: `${path}/incident`, + options: { + authRequired: false, + }, + validate: {}, + security: { authz: { enabled: false, reason: 'This route is opted out from authorization' } }, + }, + async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return res.ok({ body: XSOARRunSuccessResponse }); + } + ); +} diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xsoar.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xsoar.ts new file mode 100644 index 000000000000..b81256d4ae38 --- /dev/null +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xsoar.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 expect from '@kbn/expect'; + +import { + XSOARSimulator, + XSOARPlaybooksResponse, +} from '@kbn/actions-simulators-plugin/server/xsoar_simulation'; +import { TaskErrorSource } from '@kbn/task-manager-plugin/common'; +import type { FtrProviderContext } from '../../../../../common/ftr_provider_context'; + +const connectorTypeId = '.xsoar'; +const name = 'XSOAR action'; +const secrets = { + apiKey: 'apiKey', +}; + +// eslint-disable-next-line import/no-default-export +export default function xsoarTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const configService = getService('config'); + + const createConnector = async (url: string) => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: { url }, + secrets, + }) + .expect(200); + + return body.id; + }; + + describe('XSOAR', () => { + describe('action creation', () => { + const simulator = new XSOARSimulator({ + returnError: false, + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + const config = { url: '' }; + + before(async () => { + config.url = await simulator.start(); + }); + + after(() => { + simulator.close(); + }); + + it('should return 200 when creating the connector', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config, + secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_system_action: false, + is_deprecated: false, + name, + connector_type_id: connectorTypeId, + is_missing_secrets: false, + config, + }); + }); + + it('should return 400 Bad Request when creating the connector without the url', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: {}, + secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [url]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should return 400 Bad Request when creating the connector with a url that is not allowed', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: { + url: 'http://xsoar.mynonexistent.com', + }, + secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error validating url: target url "http://xsoar.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should return 400 Bad Request when creating the connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [apiKey]: expected value of type [string] but got [undefined]', + }); + }); + }); + }); + + describe('executor', () => { + describe('validation', () => { + const simulator = new XSOARSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let xsoarActionId: string; + + before(async () => { + const url = await simulator.start(); + xsoarActionId = await createConnector(url); + }); + + after(() => { + simulator.close(); + }); + + it('should fail when the params is empty', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }); + expect(200); + + expect(Object.keys(body).sort()).to.eql([ + 'connector_id', + 'errorSource', + 'message', + 'retry', + 'status', + ]); + expect(body.connector_id).to.eql(xsoarActionId); + expect(body.status).to.eql('error'); + }); + + it('should fail when the subAction is invalid', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'invalidAction' }, + }) + .expect(200); + + expect(body).to.eql({ + connector_id: xsoarActionId, + status: 'error', + retry: true, + message: 'an error occurred while running the action', + errorSource: TaskErrorSource.FRAMEWORK, + service_message: `Sub action "invalidAction" is not registered. Connector id: ${xsoarActionId}. Connector name: XSOAR. Connector type: ${connectorTypeId}`, + }); + }); + + it("should fail to run when the name parameter isn't included", async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'run', + subActionParams: { + severity: 1, + createInvestigation: false, + body: '', + isRuleSeverity: false, + }, + }, + }) + .expect(200); + + expect(body).to.eql({ + connector_id: xsoarActionId, + status: 'error', + retry: true, + message: 'an error occurred while running the action', + errorSource: TaskErrorSource.USER, + service_message: + 'Request validation failed (Error: [name]: expected value of type [string] but got [undefined])', + }); + }); + }); + + describe('execution', () => { + describe('successful response simulator', () => { + const simulator = new XSOARSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let url: string; + let xsoarActionId: string; + + before(async () => { + url = await simulator.start(); + xsoarActionId = await createConnector(url); + }); + + after(() => { + simulator.close(); + }); + + it('should get playbooks', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'getPlaybooks', subActionParams: {} }, + }) + .expect(200); + + expect(simulator.requestUrl).to.eql(`${url}/playbook/search`); + expect(body).to.eql({ + status: 'ok', + connector_id: xsoarActionId, + data: XSOARPlaybooksResponse, + }); + }); + + it('should create incident', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'run', + subActionParams: { + name: 'My test incident', + severity: 2, + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + createInvestigation: false, + body: null, + isRuleSeverity: false, + }, + }, + }) + .expect(200); + + expect(simulator.requestData).to.eql({ + name: 'My test incident', + severity: 2, + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + createInvestigation: false, + }); + expect(simulator.requestUrl).to.eql(`${url}/incident`); + expect(body).to.eql({ + status: 'ok', + connector_id: xsoarActionId, + data: {}, + }); + }); + }); + + describe('error response simulator', () => { + const simulator = new XSOARSimulator({ + returnError: true, + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + + let xsoarActionId: string; + + before(async () => { + const url = await simulator.start(); + xsoarActionId = await createConnector(url); + }); + + after(() => { + simulator.close(); + }); + + it('should return a failure when attempting to get playbooks', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getPlaybooks', + subActionParams: {}, + }, + }) + .expect(200); + + expect(body).to.eql({ + status: 'error', + message: 'an error occurred while running the action', + retry: true, + connector_id: xsoarActionId, + errorSource: TaskErrorSource.FRAMEWORK, + service_message: 'Status code: 400. Message: API Error: Bad Request', + }); + }); + + it('should return a failure when attempting to run', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${xsoarActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'run', + subActionParams: { + name: 'My test incident', + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + severity: 1, + isRuleSeverity: false, + createInvestigation: false, + }, + }, + }) + .expect(200); + + expect(simulator.requestData).to.eql({ + name: 'My test incident', + playbookId: '8db0105c-f674-4d83-8095-f95a9f61e77a', + severity: 1, + createInvestigation: false, + }); + expect(body).to.eql({ + status: 'error', + message: 'an error occurred while running the action', + retry: true, + connector_id: xsoarActionId, + errorSource: TaskErrorSource.FRAMEWORK, + service_message: 'Status code: 400. Message: API Error: Bad Request', + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index a6f435fbee99..fa41956eee48 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -44,6 +44,7 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./connector_types/thehive')); loadTestFile(require.resolve('./connector_types/bedrock')); loadTestFile(require.resolve('./connector_types/gemini')); + loadTestFile(require.resolve('./connector_types/xsoar')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./execute')); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts index 2300c3996071..e136151df7df 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts @@ -58,6 +58,7 @@ export default function createRegisteredConnectorTypeTests({ getService }: FtrPr '.cases', '.crowdstrike', '.microsoft_defender_endpoint', + '.xsoar', ].sort() ); }); diff --git a/x-pack/platform/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/platform/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index 7eeded4099ad..4f7a6f1b7473 100644 --- a/x-pack/platform/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/platform/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -88,6 +88,7 @@ export default function ({ getService }: FtrProviderContext) { 'actions:.torq', 'actions:.webhook', 'actions:.xmatters', + 'actions:.xsoar', 'actions:connector_usage_reporting', 'actions_telemetry', 'ad_hoc_run-backfill',