diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts index 64663f8330b24..676ce1d27d2fc 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts @@ -571,6 +571,132 @@ describe('7.11.2', () => { } as SavedObjectUnsanitizedDoc; expect(isAnyActionSupportIncidents(doc)).toBe(false); }); + + test('it does not transforms alerts when the right structure connectors is already applied', () => { + const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; + const alert = getMockData({ + actions: [ + { + actionTypeId: '.server-log', + group: 'threshold met', + params: { + level: 'info', + message: 'log message', + }, + id: '99257478-e591-4560-b264-441bdd4fe1d9', + }, + { + actionTypeId: '.servicenow', + group: 'threshold met', + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + short_description: 'SN short desc', + description: 'SN desc', + severity: '2', + impact: '2', + urgency: '2', + }, + comments: [{ commentId: '1', comment: 'sn comment' }], + }, + }, + id: '1266562a-4e1f-4305-99ca-1b44c469b26e', + }, + ], + }); + + expect(migration7112(alert, migrationContext)).toEqual(alert); + }); + + test('if incident attribute is an empty object, copy back the related attributes from subActionParams back to incident', () => { + const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; + const alert = getMockData({ + actions: [ + { + actionTypeId: '.server-log', + group: 'threshold met', + params: { + level: 'info', + message: 'log message', + }, + id: '99257478-e591-4560-b264-441bdd4fe1d9', + }, + { + actionTypeId: '.servicenow', + group: 'threshold met', + params: { + subAction: 'pushToService', + subActionParams: { + short_description: 'SN short desc', + description: 'SN desc', + severity: '2', + impact: '2', + urgency: '2', + incident: {}, + comments: [{ commentId: '1', comment: 'sn comment' }], + }, + }, + id: '1266562a-4e1f-4305-99ca-1b44c469b26e', + }, + ], + }); + + expect(migration7112(alert, migrationContext)).toEqual({ + ...alert, + attributes: { + ...alert.attributes, + actions: [ + alert.attributes.actions![0], + { + actionTypeId: '.servicenow', + group: 'threshold met', + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + short_description: 'SN short desc', + description: 'SN desc', + severity: '2', + impact: '2', + urgency: '2', + }, + comments: [{ commentId: '1', comment: 'sn comment' }], + }, + }, + id: '1266562a-4e1f-4305-99ca-1b44c469b26e', + }, + ], + }, + }); + }); + + test('custom action does not get migrated/loss', () => { + const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; + const alert = getMockData({ + actions: [ + { + actionTypeId: '.mike', + group: 'threshold met', + params: { + subAction: 'pushToService', + subActionParams: { + short_description: 'SN short desc', + description: 'SN desc', + severity: '2', + impact: '2', + urgency: '2', + incident: {}, + comments: [{ commentId: '1', comment: 'sn comment' }], + }, + }, + id: '1266562a-4e1f-4305-99ca-1b44c469b26e', + }, + ], + }); + + expect(migration7112(alert, migrationContext)).toEqual(alert); + }); }); function getUpdatedAt(): string { diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts index f2f956a0a2b26..729290498561f 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts @@ -10,6 +10,7 @@ import { SavedObjectUnsanitizedDoc, SavedObjectMigrationFn, SavedObjectMigrationContext, + SavedObjectAttributes, } from '../../../../../src/core/server'; import { RawAlert, RawAlertAction } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; @@ -180,113 +181,147 @@ function initializeExecutionStatus( }; } +function isEmptyObject(obj: {}) { + for (const attr in obj) { + if (Object.prototype.hasOwnProperty.call(obj, attr)) { + return false; + } + } + return true; +} + function restructureConnectorsThatSupportIncident( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { const { actions } = doc.attributes; const newActions = actions.reduce((acc, action) => { - if (action.params.subAction !== 'pushToService') { - return [...acc, action]; - } - - if (action.actionTypeId === '.servicenow') { - const { title, comments, comment, description, severity, urgency, impact } = action.params - .subActionParams as { - title: string; - description?: string; - severity?: string; - urgency?: string; - impact?: string; - comment?: string; - comments?: Array<{ commentId: string; comment: string }>; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - short_description: title, - description, - severity, - urgency, - impact, + if ( + ['.servicenow', '.jira', '.resilient'].includes(action.actionTypeId) && + action.params.subAction === 'pushToService' + ) { + // Future developer, we needed to do that because when we created this migration + // we forget to think about user already using 7.11.0 and having an incident attribute build the right way + // IMPORTANT -> if you change this code please do the same inside of this file + // x-pack/plugins/alerting/server/saved_objects/migrations.ts + const subActionParamsIncident = + (action.params?.subActionParams as SavedObjectAttributes)?.incident ?? null; + if (subActionParamsIncident != null && !isEmptyObject(subActionParamsIncident)) { + return [...acc, action]; + } + if (action.actionTypeId === '.servicenow') { + const { + title, + comments, + comment, + description, + severity, + urgency, + impact, + short_description: shortDescription, + } = action.params.subActionParams as { + title: string; + description?: string; + severity?: string; + urgency?: string; + impact?: string; + comment?: string; + comments?: Array<{ commentId: string; comment: string }>; + short_description?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + short_description: shortDescription ?? title, + description, + severity, + urgency, + impact, + }, + comments: [ + ...(comments ?? []), + ...(comment != null ? [{ commentId: '1', comment }] : []), + ], }, - comments: [ - ...(comments ?? []), - ...(comment != null ? [{ commentId: '1', comment }] : []), - ], }, }, - }, - ] as RawAlertAction[]; - } - - if (action.actionTypeId === '.jira') { - const { title, comments, description, issueType, priority, labels, parent } = action.params - .subActionParams as { - title: string; - description: string; - issueType: string; - priority?: string; - labels?: string[]; - parent?: string; - comments?: unknown[]; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - summary: title, - description, - issueType, - priority, - labels, - parent, + ] as RawAlertAction[]; + } else if (action.actionTypeId === '.jira') { + const { + title, + comments, + description, + issueType, + priority, + labels, + parent, + summary, + } = action.params.subActionParams as { + title: string; + description: string; + issueType: string; + priority?: string; + labels?: string[]; + parent?: string; + comments?: unknown[]; + summary?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + summary: summary ?? title, + description, + issueType, + priority, + labels, + parent, + }, + comments, }, - comments, }, }, - }, - ] as RawAlertAction[]; - } - - if (action.actionTypeId === '.resilient') { - const { title, comments, description, incidentTypes, severityCode } = action.params - .subActionParams as { - title: string; - description: string; - incidentTypes?: number[]; - severityCode?: number; - comments?: unknown[]; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - name: title, - description, - incidentTypes, - severityCode, + ] as RawAlertAction[]; + } else if (action.actionTypeId === '.resilient') { + const { title, comments, description, incidentTypes, severityCode, name } = action.params + .subActionParams as { + title: string; + description: string; + incidentTypes?: number[]; + severityCode?: number; + comments?: unknown[]; + name?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + name: name ?? title, + description, + incidentTypes, + severityCode, + }, + comments, }, - comments, }, }, - }, - ] as RawAlertAction[]; + ] as RawAlertAction[]; + } } - return acc; + return [...acc, action]; }, [] as RawAlertAction[]); return { diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts index a3291af3a9eb6..996df2a8fe60a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts @@ -43,7 +43,7 @@ describe('Cases connectors', () => { }; beforeEach(() => { cleanKibana(); - cy.intercept('POST', '/api/actions/action').as('createConnector'); + cy.intercept('POST', '/api/actions/connector').as('createConnector'); cy.intercept('POST', '/api/cases/configure', (req) => { const connector = req.body.connector; req.reply((res) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts index 154e90d509c61..2e0599dfcae21 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/exceptions_modal.spec.ts @@ -133,7 +133,7 @@ describe('Exceptions modal', () => { closeExceptionBuilderModal(); }); - it('Does not overwrite values of nested entry items', () => { + it.skip('Does not overwrite values of nested entry items', () => { openExceptionModalFromRuleSettings(); cy.get(LOADING_SPINNER).should('not.exist'); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts index dbaba4d4f5c7b..cb93007f19c9c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts @@ -74,7 +74,7 @@ describe('timeline data providers', () => { }); }); - it('sets the background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers', () => { + it.skip('sets the background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers', () => { dragFirstHostToTimeline(); if (Cypress.browser.name === 'firefox') { diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts index 25b790b57bc06..6b6463803ee37 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/flyout_button.spec.ts @@ -76,7 +76,7 @@ describe('timeline flyout button', () => { closeTimelineUsingCloseButton(); }); - it('sets the data providers background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers area', () => { + it.skip('sets the data providers background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers area', () => { dragFirstHostToTimeline(); if (Cypress.browser.name === 'firefox') { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/migrations.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/migrations.ts index 2180dbb68524d..4b66c20e5784a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/migrations.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/migrations.ts @@ -8,117 +8,152 @@ import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, + SavedObjectAttributes, } from '../../../../../../../src/core/server'; import { IRuleActionsAttributesSavedObjectAttributes, RuleAlertAction } from './types'; +function isEmptyObject(obj: {}) { + for (const attr in obj) { + if (Object.prototype.hasOwnProperty.call(obj, attr)) { + return false; + } + } + return true; +} + export const ruleActionsSavedObjectMigration = { '7.11.2': ( doc: SavedObjectUnsanitizedDoc ): SavedObjectSanitizedDoc => { const { actions } = doc.attributes; const newActions = actions.reduce((acc, action) => { - if (action.params.subAction !== 'pushToService') { - return [...acc, action]; - } - - if (action.action_type_id === '.servicenow') { - const { title, comments, comment, description, severity, urgency, impact } = action.params - .subActionParams as { - title: string; - description?: string; - severity?: string; - urgency?: string; - impact?: string; - comment?: string; - comments?: Array<{ commentId: string; comment: string }>; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - short_description: title, - description, - severity, - urgency, - impact, + if ( + ['.servicenow', '.jira', '.resilient'].includes(action.action_type_id) && + action.params.subAction === 'pushToService' + ) { + // Future developer, we needed to do that because when we created this migration + // we forget to think about user already using 7.11.0 and having an incident attribute build the right way + // IMPORTANT -> if you change this code please do the same inside of this file + // x-pack/plugins/alerting/server/saved_objects/migrations.ts + const subActionParamsIncident = + (action.params?.subActionParams as SavedObjectAttributes)?.incident ?? null; + if (subActionParamsIncident != null && !isEmptyObject(subActionParamsIncident)) { + return [...acc, action]; + } + if (action.action_type_id === '.servicenow') { + const { + title, + comments, + comment, + description, + severity, + urgency, + impact, + short_description: shortDescription, + } = action.params.subActionParams as { + title: string; + description?: string; + severity?: string; + urgency?: string; + impact?: string; + comment?: string; + comments?: Array<{ commentId: string; comment: string }>; + short_description?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + short_description: shortDescription ?? title, + description, + severity, + urgency, + impact, + }, + comments: [ + ...(comments ?? []), + ...(comment != null ? [{ commentId: '1', comment }] : []), + ], }, - comments: [ - ...(comments ?? []), - ...(comment != null ? [{ commentId: '1', comment }] : []), - ], }, }, - }, - ] as RuleAlertAction[]; - } - - if (action.action_type_id === '.jira') { - const { title, comments, description, issueType, priority, labels, parent } = action.params - .subActionParams as { - title: string; - description: string; - issueType: string; - priority?: string; - labels?: string[]; - parent?: string; - comments?: unknown[]; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - summary: title, - description, - issueType, - priority, - labels, - parent, + ] as RuleAlertAction[]; + } else if (action.action_type_id === '.jira') { + const { + title, + comments, + description, + issueType, + priority, + labels, + parent, + summary, + } = action.params.subActionParams as { + title: string; + description: string; + issueType: string; + priority?: string; + labels?: string[]; + parent?: string; + comments?: unknown[]; + summary?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + summary: summary ?? title, + description, + issueType, + priority, + labels, + parent, + }, + comments, }, - comments, }, }, - }, - ] as RuleAlertAction[]; - } - - if (action.action_type_id === '.resilient') { - const { title, comments, description, incidentTypes, severityCode } = action.params - .subActionParams as { - title: string; - description: string; - incidentTypes?: number[]; - severityCode?: number; - comments?: unknown[]; - }; - return [ - ...acc, - { - ...action, - params: { - subAction: 'pushToService', - subActionParams: { - incident: { - name: title, - description, - incidentTypes, - severityCode, + ] as RuleAlertAction[]; + } else if (action.action_type_id === '.resilient') { + const { title, comments, description, incidentTypes, severityCode, name } = action.params + .subActionParams as { + title: string; + description: string; + incidentTypes?: number[]; + severityCode?: number; + comments?: unknown[]; + name?: string; + }; + return [ + ...acc, + { + ...action, + params: { + subAction: 'pushToService', + subActionParams: { + incident: { + name: name ?? title, + description, + incidentTypes, + severityCode, + }, + comments, }, - comments, }, }, - }, - ] as RuleAlertAction[]; + ] as RuleAlertAction[]; + } } - return acc; + return [...acc, action]; }, [] as RuleAlertAction[]); return {