-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[Security Solution] Webhook - Case Management Connector #131762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
122 commits
Select commit
Hold shift + click to select a range
2fed291
bootstrap cases webhook
stephmilovic d981f51
Merge branch 'main' into cases_webhook
stephmilovic 2c6a233
Merge branch 'main' into cases_webhook
stephmilovic 78907b6
wip
stephmilovic b18270e
Merge branch 'main' into cases_webhook
stephmilovic 094ff9b
wip'
stephmilovic 74361d3
Merge branch 'main' into cases_webhook
stephmilovic ff959a4
wip
stephmilovic 85e3d97
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine b63647a
Merge branch 'main' into cases_webhook
stephmilovic 9b6bf56
make work in cases
stephmilovic 0cd8127
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine 6bd36f7
Merge branch 'main' into cases_webhook
stephmilovic 959e689
werk better
stephmilovic b84e705
add api file
stephmilovic 823c1fa
type cleanup
stephmilovic ef6e12e
fix types more
stephmilovic 4017aa8
add all fields to form
stephmilovic 8067c34
add in new fields
stephmilovic c958685
fix cases push
stephmilovic 2e8a899
add patch to fe
stephmilovic ba1ac12
merge in main
stephmilovic fe108a5
make link back url work
stephmilovic db286dd
type cleaning
stephmilovic 53a09fa
Merge branch 'main' into cases_webhook
stephmilovic 9de1167
add update and create comment
stephmilovic 211c24b
support jira
stephmilovic 5709c8c
labels
stephmilovic 490e4bb
Merge branch 'main' into cases_webhook
stephmilovic 06a8326
rm console logs and improve validators
stephmilovic 33adfe5
fix type
stephmilovic b016221
fix triggers actions ui tests
stephmilovic 9d2e57c
cleanup
stephmilovic 96368bf
fix translations
stephmilovic 4a2db85
Merge branch 'main' into cases_webhook
stephmilovic c41aca0
fix types
stephmilovic 2bc71a9
Merge branch 'main' into cases_webhook
stephmilovic d651513
Merge branch 'main' into cases_webhook
stephmilovic 93f68a9
update to new format
stephmilovic 4966881
fix webhook tests
stephmilovic f1a8cf3
fix type
stephmilovic 6abc85d
use mustache
stephmilovic c429071
fix!
stephmilovic 2e982c5
add mustache to urls
stephmilovic 6fb6da2
better form validation for mustache variables
stephmilovic 4a23a68
Merge branch 'main' into cases_webhook
stephmilovic 4f5c051
create comment optional
stephmilovic 1760fd5
add tags and comment to test form
stephmilovic ac980a4
make steps
stephmilovic 9790eaf
Merge branch 'main' into cases_webhook
stephmilovic 8486691
hide cases webhook from rule actions
stephmilovic afaf444
fix i18n
stephmilovic 35d1288
monina changes
stephmilovic c60ec73
test fixing
stephmilovic e7cfced
Merge branch 'main' into cases_webhook
stephmilovic 98b876c
fix text field input
stephmilovic 5cd8eb7
tests wip
stephmilovic 60d5be3
finish webhook connectors test
stephmilovic 18dd905
fix comment verbiage
stephmilovic eb15ea4
api tests
stephmilovic c35d2c2
action service tests
stephmilovic 8d14410
starting api tests
stephmilovic 1dd846e
api tests
stephmilovic 36d35f4
ues
stephmilovic 8a6d34a
add case connectors api tests
stephmilovic 5f4d5fe
Merge branch 'main' into cases_webhook
stephmilovic a4df495
uncomment test files
stephmilovic a3a5a22
fix translations
stephmilovic 2aa44a7
couple pr fixes
stephmilovic 17507e9
shorter step titles
stephmilovic 54ea733
Merge branch 'main' into cases_webhook
stephmilovic ffc0e80
move makeCaseStringy
stephmilovic a6aba99
more fix whoops
stephmilovic 3061484
tests
stephmilovic fd478d7
pr changes
stephmilovic a71ddf7
pr changes 3
stephmilovic 0902c49
fix type
stephmilovic 7d322a0
pr changes 4
stephmilovic 18db51d
remove only
stephmilovic 73cabbf
Merge branch 'main' into cases_webhook
stephmilovic 4ec23a4
fix func test
stephmilovic b0d344f
fix
stephmilovic bee21e7
incident > case
stephmilovic d870bec
Merge branch 'main' into cases_webhook
stephmilovic 81595e8
update allows external.service.id in json, not required in url
stephmilovic ae487b1
response id to string
stephmilovic 050ea48
fix user
stephmilovic c537288
getObjectValueByKeyAsString + url manipulation fix
stephmilovic 008bdcb
add technical preview badge
stephmilovic 90d69d1
fix test
stephmilovic cd86d5d
no async submit validation
stephmilovic a81c540
type fixes
stephmilovic 7064dc6
fix merge
stephmilovic 2dc98c3
fix i18n
stephmilovic f581b1d
translations and headers
stephmilovic 3222259
fix user/pw validation
stephmilovic 953e235
fix translations and tests
stephmilovic 8aecdc3
fix type
stephmilovic 3dc8e3e
Props to own interface
stephmilovic 4b8b1e3
better error messaging
stephmilovic df21227
fix tests
stephmilovic f70e80e
betaBadgeProps > isExperimental
stephmilovic d675a89
rm console.logs
stephmilovic 48be85f
resolve merge
stephmilovic 2d22adc
fix badge
stephmilovic 8693147
better errs
stephmilovic 3d4fcc4
better url err
stephmilovic 7a22fe5
err msg
stephmilovic 9de3498
fix tests and nullable
stephmilovic da17ac1
add tests for bad url and bad protocol
stephmilovic 31f1303
add test for showButtonTitle prop
stephmilovic 73efd75
rm url err
stephmilovic 2036e82
rm SecurityConnectorFeatureId
stephmilovic 7d71f3a
revert escape mustache vars in url
stephmilovic 9aee3f3
better esc
stephmilovic 485e86f
revert triple to double
stephmilovic 219018c
one more test
stephmilovic c416363
fix jest
stephmilovic 0fd363f
Merge branch 'main' into cases_webhook
stephmilovic 3dc2aaf
Merge branch 'main' into cases_webhook
kibanamachine 92672bf
Merge branch 'main' into cases_webhook
kibanamachine b9a23fa
Merge branch 'main' into cases_webhook
kibanamachine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
214 changes: 214 additions & 0 deletions
214
x-pack/plugins/actions/server/builtin_action_types/cases_webhook/api.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| /* | ||
| * 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 { Logger } from '@kbn/core/server'; | ||
| import { externalServiceMock, apiParams } from './mock'; | ||
| import { ExternalService } from './types'; | ||
| import { api } from './api'; | ||
| let mockedLogger: jest.Mocked<Logger>; | ||
|
|
||
| describe('api', () => { | ||
| let externalService: jest.Mocked<ExternalService>; | ||
|
|
||
| beforeEach(() => { | ||
| externalService = externalServiceMock.create(); | ||
| }); | ||
|
|
||
| describe('create incident - cases', () => { | ||
| test('it creates an incident', async () => { | ||
| const params = { ...apiParams, externalId: null }; | ||
| const res = await api.pushToService({ | ||
| externalService, | ||
| params, | ||
| logger: mockedLogger, | ||
| }); | ||
|
|
||
| expect(res).toEqual({ | ||
| id: 'incident-1', | ||
| title: 'CK-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| url: 'https://siem-kibana.atlassian.net/browse/CK-1', | ||
| comments: [ | ||
| { | ||
| commentId: 'case-comment-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| }, | ||
| { | ||
| commentId: 'case-comment-2', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| test('it creates an incident without comments', async () => { | ||
| const params = { ...apiParams, externalId: null, comments: [] }; | ||
| const res = await api.pushToService({ | ||
| externalService, | ||
| params, | ||
| logger: mockedLogger, | ||
| }); | ||
|
|
||
| expect(res).toEqual({ | ||
| id: 'incident-1', | ||
| title: 'CK-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| url: 'https://siem-kibana.atlassian.net/browse/CK-1', | ||
| }); | ||
| }); | ||
|
|
||
| test('it calls createIncident correctly', async () => { | ||
| const params = { ...apiParams, incident: { ...apiParams.incident, externalId: null } }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
|
|
||
| expect(externalService.createIncident).toHaveBeenCalledWith({ | ||
| incident: { | ||
| tags: ['kibana', 'elastic'], | ||
| description: 'Incident description', | ||
| title: 'Incident title', | ||
| }, | ||
| }); | ||
| expect(externalService.updateIncident).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('it calls createComment correctly', async () => { | ||
| const params = { ...apiParams, incident: { ...apiParams.incident, externalId: null } }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
| expect(externalService.createComment).toHaveBeenCalledTimes(2); | ||
| expect(externalService.createComment).toHaveBeenNthCalledWith(1, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-1', | ||
| comment: 'A comment', | ||
| }, | ||
| }); | ||
|
|
||
| expect(externalService.createComment).toHaveBeenNthCalledWith(2, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-2', | ||
| comment: 'Another comment', | ||
| }, | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('update incident', () => { | ||
| test('it updates an incident', async () => { | ||
| const res = await api.pushToService({ | ||
| externalService, | ||
| params: apiParams, | ||
| logger: mockedLogger, | ||
| }); | ||
|
|
||
| expect(res).toEqual({ | ||
| id: 'incident-1', | ||
| title: 'CK-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| url: 'https://siem-kibana.atlassian.net/browse/CK-1', | ||
| comments: [ | ||
| { | ||
| commentId: 'case-comment-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| }, | ||
| { | ||
| commentId: 'case-comment-2', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| }, | ||
| ], | ||
| }); | ||
| }); | ||
|
|
||
| test('it updates an incident without comments', async () => { | ||
| const params = { ...apiParams, comments: [] }; | ||
| const res = await api.pushToService({ | ||
| externalService, | ||
| params, | ||
| logger: mockedLogger, | ||
| }); | ||
|
|
||
| expect(res).toEqual({ | ||
| id: 'incident-1', | ||
| title: 'CK-1', | ||
| pushedDate: '2020-04-27T10:59:46.202Z', | ||
| url: 'https://siem-kibana.atlassian.net/browse/CK-1', | ||
| }); | ||
| }); | ||
|
|
||
| test('it calls updateIncident correctly', async () => { | ||
| const params = { ...apiParams }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
|
|
||
| expect(externalService.updateIncident).toHaveBeenCalledWith({ | ||
| incidentId: 'incident-3', | ||
| incident: { | ||
| tags: ['kibana', 'elastic'], | ||
| description: 'Incident description', | ||
| title: 'Incident title', | ||
| }, | ||
| }); | ||
| expect(externalService.createIncident).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('it calls updateIncident correctly without mapping', async () => { | ||
| const params = { ...apiParams }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
|
|
||
| expect(externalService.updateIncident).toHaveBeenCalledWith({ | ||
| incidentId: 'incident-3', | ||
| incident: { | ||
| description: 'Incident description', | ||
| title: 'Incident title', | ||
| tags: ['kibana', 'elastic'], | ||
| }, | ||
| }); | ||
| expect(externalService.createIncident).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| test('it calls createComment correctly', async () => { | ||
| const params = { ...apiParams }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
| expect(externalService.createComment).toHaveBeenCalledTimes(2); | ||
| expect(externalService.createComment).toHaveBeenNthCalledWith(1, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-1', | ||
| comment: 'A comment', | ||
| }, | ||
| }); | ||
|
|
||
| expect(externalService.createComment).toHaveBeenNthCalledWith(2, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-2', | ||
| comment: 'Another comment', | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| test('it calls createComment correctly without mapping', async () => { | ||
| const params = { ...apiParams }; | ||
| await api.pushToService({ externalService, params, logger: mockedLogger }); | ||
| expect(externalService.createComment).toHaveBeenCalledTimes(2); | ||
| expect(externalService.createComment).toHaveBeenNthCalledWith(1, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-1', | ||
| comment: 'A comment', | ||
| }, | ||
| }); | ||
|
|
||
| expect(externalService.createComment).toHaveBeenNthCalledWith(2, { | ||
| incidentId: 'incident-1', | ||
| comment: { | ||
| commentId: 'case-comment-2', | ||
| comment: 'Another comment', | ||
| }, | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
61 changes: 61 additions & 0 deletions
61
x-pack/plugins/actions/server/builtin_action_types/cases_webhook/api.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the Elastic License | ||
| * 2.0; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| import { | ||
| ExternalServiceApi, | ||
| Incident, | ||
| PushToServiceApiHandlerArgs, | ||
| PushToServiceResponse, | ||
| } from './types'; | ||
|
|
||
| const pushToServiceHandler = async ({ | ||
| externalService, | ||
| params, | ||
| }: PushToServiceApiHandlerArgs): Promise<PushToServiceResponse> => { | ||
| const { | ||
| incident: { externalId, ...rest }, | ||
| comments, | ||
| } = params; | ||
| const incident: Incident = rest; | ||
| let res: PushToServiceResponse; | ||
|
|
||
| if (externalId != null) { | ||
| res = await externalService.updateIncident({ | ||
| incidentId: externalId, | ||
| incident, | ||
| }); | ||
| } else { | ||
| res = await externalService.createIncident({ | ||
| incident, | ||
| }); | ||
| } | ||
|
|
||
| if (comments && Array.isArray(comments) && comments.length > 0) { | ||
| res.comments = []; | ||
| for (const currentComment of comments) { | ||
| if (!currentComment.comment) { | ||
| continue; | ||
| } | ||
| await externalService.createComment({ | ||
| incidentId: res.id, | ||
| comment: currentComment, | ||
| }); | ||
| res.comments = [ | ||
| ...(res.comments ?? []), | ||
| { | ||
| commentId: currentComment.commentId, | ||
| pushedDate: res.pushedDate, | ||
| }, | ||
| ]; | ||
| } | ||
| } | ||
|
|
||
| return res; | ||
| }; | ||
| export const api: ExternalServiceApi = { | ||
| pushToService: pushToServiceHandler, | ||
| }; |
117 changes: 117 additions & 0 deletions
117
x-pack/plugins/actions/server/builtin_action_types/cases_webhook/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| /* | ||
| * 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 { curry } from 'lodash'; | ||
| import { schema } from '@kbn/config-schema'; | ||
| import { Logger } from '@kbn/core/server'; | ||
| import { CasesConnectorFeatureId } from '../../../common'; | ||
| import { | ||
| CasesWebhookActionParamsType, | ||
| CasesWebhookExecutorResultData, | ||
| CasesWebhookPublicConfigurationType, | ||
| CasesWebhookSecretConfigurationType, | ||
| ExecutorParams, | ||
| ExecutorSubActionPushParams, | ||
| } from './types'; | ||
| import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types'; | ||
| import { ActionsConfigurationUtilities } from '../../actions_config'; | ||
| import { createExternalService } from './service'; | ||
| import { | ||
| ExecutorParamsSchema, | ||
| ExternalIncidentServiceConfiguration, | ||
| ExternalIncidentServiceSecretConfiguration, | ||
| } from './schema'; | ||
| import { api } from './api'; | ||
| import { validate } from './validators'; | ||
| import * as i18n from './translations'; | ||
|
|
||
| const supportedSubActions: string[] = ['pushToService']; | ||
| export type ActionParamsType = CasesWebhookActionParamsType; | ||
| export const ActionTypeId = '.cases-webhook'; | ||
| // action type definition | ||
| export function getActionType({ | ||
| logger, | ||
| configurationUtilities, | ||
| }: { | ||
| logger: Logger; | ||
| configurationUtilities: ActionsConfigurationUtilities; | ||
| }): ActionType< | ||
| CasesWebhookPublicConfigurationType, | ||
| CasesWebhookSecretConfigurationType, | ||
| ExecutorParams, | ||
| CasesWebhookExecutorResultData | ||
| > { | ||
| return { | ||
| id: ActionTypeId, | ||
| minimumLicenseRequired: 'gold', | ||
| name: i18n.NAME, | ||
| validate: { | ||
| config: schema.object(ExternalIncidentServiceConfiguration, { | ||
| validate: curry(validate.config)(configurationUtilities), | ||
| }), | ||
| secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { | ||
| validate: curry(validate.secrets), | ||
| }), | ||
| params: ExecutorParamsSchema, | ||
| connector: validate.connector, | ||
| }, | ||
| executor: curry(executor)({ logger, configurationUtilities }), | ||
| supportedFeatureIds: [CasesConnectorFeatureId], | ||
| }; | ||
| } | ||
|
|
||
| // action executor | ||
| export async function executor( | ||
| { | ||
| logger, | ||
| configurationUtilities, | ||
| }: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities }, | ||
| execOptions: ActionTypeExecutorOptions< | ||
| CasesWebhookPublicConfigurationType, | ||
| CasesWebhookSecretConfigurationType, | ||
| CasesWebhookActionParamsType | ||
| > | ||
| ): Promise<ActionTypeExecutorResult<CasesWebhookExecutorResultData>> { | ||
| const actionId = execOptions.actionId; | ||
| const { subAction, subActionParams } = execOptions.params; | ||
| let data: CasesWebhookExecutorResultData | undefined; | ||
|
|
||
| const externalService = createExternalService( | ||
| actionId, | ||
| { | ||
| config: execOptions.config, | ||
| secrets: execOptions.secrets, | ||
| }, | ||
| logger, | ||
| configurationUtilities | ||
| ); | ||
|
|
||
| if (!api[subAction]) { | ||
| const errorMessage = `[Action][ExternalService] Unsupported subAction type ${subAction}.`; | ||
| logger.error(errorMessage); | ||
| throw new Error(errorMessage); | ||
| } | ||
|
|
||
| if (!supportedSubActions.includes(subAction)) { | ||
| const errorMessage = `[Action][ExternalService] subAction ${subAction} not implemented.`; | ||
| logger.error(errorMessage); | ||
| throw new Error(errorMessage); | ||
| } | ||
|
|
||
| if (subAction === 'pushToService') { | ||
| const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; | ||
| data = await api.pushToService({ | ||
| externalService, | ||
| params: pushToServiceParams, | ||
| logger, | ||
| }); | ||
|
|
||
| logger.debug(`response push to service for case id: ${data.id}`); | ||
| } | ||
|
|
||
| return { status: 'ok', data, actionId }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.