diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index a890b12d3b7aa..b78f0b5d64082 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; import { ruleTypeMappings } from '@kbn/securitysolution-rules'; -import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'kibana/server'; +import { SavedObjectsFindResponse } from 'src/core/server'; import { ActionResult } from '../../../../../../actions/server'; import { @@ -42,7 +42,8 @@ import { SanitizedAlert, ResolvedSanitizedRule } from '../../../../../../alertin import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; import { getPerformBulkActionSchemaMock } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema.mock'; import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { FindBulkExecutionLogResponse } from '../../rule_execution_log/types'; +import { GetCurrentStatusBulkResult } from '../../rule_execution_log/types'; +import { ruleTypeMappings } from '../../signals/utils'; // eslint-disable-next-line no-restricted-imports import type { LegacyRuleNotificationAlertType } from '../../notifications/legacy_types'; @@ -475,94 +476,64 @@ export const getEmptySavedObjectsResponse = saved_objects: [], }); -export const getRuleExecutionStatuses = (): Array< - SavedObjectsFindResult -> => [ - { - type: 'my-type', - id: 'e0b86950-4e9f-11ea-bdbd-07b56aa159b3', - attributes: { - statusDate: '2020-02-18T15:26:49.783Z', - status: RuleExecutionStatus.succeeded, - lastFailureAt: undefined, - lastSuccessAt: '2020-02-18T15:26:49.783Z', - lastFailureMessage: undefined, - lastSuccessMessage: 'succeeded', - lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), - gap: '500.32', - searchAfterTimeDurations: ['200.00'], - bulkCreateTimeDurations: ['800.43'], - }, - score: 1, - references: [ - { - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bc', - type: 'alert', - name: 'alert_0', - }, - ], - updated_at: '2020-02-18T15:26:51.333Z', - version: 'WzQ2LDFd', - }, - { - type: 'my-type', - id: '91246bd0-5261-11ea-9650-33b954270f67', - attributes: { - statusDate: '2020-02-18T15:15:58.806Z', - status: RuleExecutionStatus.failed, - lastFailureAt: '2020-02-18T15:15:58.806Z', - lastSuccessAt: '2020-02-13T20:31:59.855Z', - lastFailureMessage: - 'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.', - lastSuccessMessage: 'succeeded', - lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), - gap: '500.32', - searchAfterTimeDurations: ['200.00'], - bulkCreateTimeDurations: ['800.43'], - }, - score: 1, - references: [ - { - id: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', - type: 'alert', - name: 'alert_0', - }, - ], - updated_at: '2020-02-18T15:15:58.860Z', - version: 'WzMyLDFd', - }, +export const getRuleExecutionStatusSucceeded = (): IRuleStatusSOAttributes => ({ + statusDate: '2020-02-18T15:26:49.783Z', + status: RuleExecutionStatus.succeeded, + lastFailureAt: undefined, + lastSuccessAt: '2020-02-18T15:26:49.783Z', + lastFailureMessage: undefined, + lastSuccessMessage: 'succeeded', + lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), + gap: '500.32', + searchAfterTimeDurations: ['200.00'], + bulkCreateTimeDurations: ['800.43'], +}); + +export const getRuleExecutionStatusFailed = (): IRuleStatusSOAttributes => ({ + statusDate: '2020-02-18T15:15:58.806Z', + status: RuleExecutionStatus.failed, + lastFailureAt: '2020-02-18T15:15:58.806Z', + lastSuccessAt: '2020-02-13T20:31:59.855Z', + lastFailureMessage: + 'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.', + lastSuccessMessage: 'succeeded', + lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), + gap: '500.32', + searchAfterTimeDurations: ['200.00'], + bulkCreateTimeDurations: ['800.43'], +}); + +export const getRuleExecutionStatuses = (): IRuleStatusSOAttributes[] => [ + getRuleExecutionStatusSucceeded(), + getRuleExecutionStatusFailed(), ]; -export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({ - '04128c15-0d1b-4716-a4c5-46997ac7f3bd': [ - { - statusDate: '2020-02-18T15:26:49.783Z', - status: RuleExecutionStatus.succeeded, - lastFailureAt: undefined, - lastSuccessAt: '2020-02-18T15:26:49.783Z', - lastFailureMessage: undefined, - lastSuccessMessage: 'succeeded', - lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), - gap: '500.32', - searchAfterTimeDurations: ['200.00'], - bulkCreateTimeDurations: ['800.43'], - }, - ], - '1ea5a820-4da1-4e82-92a1-2b43a7bece08': [ - { - statusDate: '2020-02-18T15:15:58.806Z', - status: RuleExecutionStatus.failed, - lastFailureAt: '2020-02-18T15:15:58.806Z', - lastSuccessAt: '2020-02-13T20:31:59.855Z', - lastFailureMessage: - 'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.', - lastSuccessMessage: 'succeeded', - lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), - gap: '500.32', - searchAfterTimeDurations: ['200.00'], - bulkCreateTimeDurations: ['800.43'], - }, - ], +export const getFindBulkResultStatus = (): GetCurrentStatusBulkResult => ({ + '04128c15-0d1b-4716-a4c5-46997ac7f3bd': { + statusDate: '2020-02-18T15:26:49.783Z', + status: RuleExecutionStatus.succeeded, + lastFailureAt: undefined, + lastSuccessAt: '2020-02-18T15:26:49.783Z', + lastFailureMessage: undefined, + lastSuccessMessage: 'succeeded', + lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), + gap: '500.32', + searchAfterTimeDurations: ['200.00'], + bulkCreateTimeDurations: ['800.43'], + }, + '1ea5a820-4da1-4e82-92a1-2b43a7bece08': { + statusDate: '2020-02-18T15:15:58.806Z', + status: RuleExecutionStatus.failed, + lastFailureAt: '2020-02-18T15:15:58.806Z', + lastSuccessAt: '2020-02-13T20:31:59.855Z', + lastFailureMessage: + 'Signal rule name: "Query with a rule id Number 1", id: "1ea5a820-4da1-4e82-92a1-2b43a7bece08", rule_id: "query-rule-id-1" has a time gap of 5 days (412682928ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.', + lastSuccessMessage: 'succeeded', + lastLookBackDate: new Date('2020-02-18T15:14:58.806Z').toISOString(), + gap: '500.32', + searchAfterTimeDurations: ['200.00'], + bulkCreateTimeDurations: ['800.43'], + }, }); export const getBasicEmptySearchResponse = (): estypes.SearchResponse => ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 010c4b27507bb..a9f5938abb921 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -10,7 +10,7 @@ import { getEmptyFindResult, getAlertMock, getCreateRequest, - getRuleExecutionStatuses, + getRuleExecutionStatusSucceeded, getFindResultWithSingleHit, createMlRuleRequest, getBasicEmptySearchResponse, @@ -43,7 +43,9 @@ describe.each([ clients.rulesClient.create.mockResolvedValue( getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); // creation succeeds - clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); // needed to transform: ; + clients.ruleExecutionLogClient.getCurrentStatus.mockResolvedValue( + getRuleExecutionStatusSucceeded() + ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index 9e03e5f8f2143..71d453809d0fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -106,14 +106,13 @@ export const createRulesRoute = ( await rulesClient.muteAll({ id: createdRule.id }); } - const ruleStatuses = await context.securitySolution.getExecutionLogClient().find({ - logsCount: 1, + const ruleStatus = await context.securitySolution.getExecutionLogClient().getCurrentStatus({ ruleId: createdRule.id, spaceId: context.securitySolution.getSpaceId(), }); const [validated, errors] = newTransformValidate( createdRule, - ruleStatuses[0], + ruleStatus, isRuleRegistryEnabled ); if (errors != null) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 6aecfff1178bc..20bf9cc2f45cc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -80,21 +80,19 @@ export const deleteRulesBulkRoute = ( return getIdBulkError({ id, ruleId }); } - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 6, + const ruleStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); await deleteRules({ + ruleId: rule.id, rulesClient, ruleStatusClient, - ruleStatuses, - id: rule.id, }); return transformValidateBulkError( idOrRuleIdOrUnknown, rule, - ruleStatuses, + [ruleStatus], isRuleRegistryEnabled ); } catch (err) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index 466012a045eb3..9c126a177eeb5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -12,7 +12,7 @@ import { getDeleteRequest, getFindResultWithSingleHit, getDeleteRequestById, - getRuleExecutionStatuses, + getRuleExecutionStatusSucceeded, getEmptySavedObjectsResponse, } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; @@ -32,7 +32,9 @@ describe.each([ clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); - clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); + clients.ruleExecutionLogClient.getCurrentStatus.mockResolvedValue( + getRuleExecutionStatusSucceeded() + ); deleteRulesRoute(server.router, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 77b8dd6fc5b54..abcf0d07a33b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -62,18 +62,16 @@ export const deleteRulesRoute = ( }); } - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 6, + const currentStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); await deleteRules({ + ruleId: rule.id, rulesClient, ruleStatusClient, - ruleStatuses, - id: rule.id, }); - const transformed = transform(rule, ruleStatuses[0], isRuleRegistryEnabled); + const transformed = transform(rule, currentStatus, isRuleRegistryEnabled); if (transformed == null) { return siemResponse.error({ statusCode: 500, body: 'failed to transform alert' }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 0b0650d48872f..9f151d1db9292 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -36,7 +36,9 @@ describe.each([ getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); - clients.ruleExecutionLogClient.findBulk.mockResolvedValue(getFindBulkResultStatus()); + clients.ruleExecutionLogClient.getCurrentStatusBulk.mockResolvedValue( + getFindBulkResultStatus() + ); findRulesRoute(server.router, logger, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts index a55a525806b17..199ef75e22f25 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -68,15 +68,14 @@ export const findRulesRoute = ( }); const alertIds = rules.data.map((rule) => rule.id); - const [ruleStatuses, ruleActions] = await Promise.all([ - execLogClient.findBulk({ + const [currentStatusesByRuleId, ruleActions] = await Promise.all([ + execLogClient.getCurrentStatusBulk({ ruleIds: alertIds, - logsCount: 1, spaceId: context.securitySolution.getSpaceId(), }), legacyGetBulkRuleActionsSavedObject({ alertIds, savedObjectsClient, logger }), ]); - const transformed = transformFindAlerts(rules, ruleStatuses, ruleActions); + const transformed = transformFindAlerts(rules, currentStatusesByRuleId, ruleActions); if (transformed == null) { return siemResponse.error({ statusCode: 500, body: 'Internal error transforming' }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts index 5d6b9810a2cda..2286c010a0a5a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts @@ -27,7 +27,9 @@ describe.each([ beforeEach(async () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.ruleExecutionLogClient.findBulk.mockResolvedValue(getFindBulkResultStatus()); // successful status search + clients.ruleExecutionLogClient.getCurrentStatusBulk.mockResolvedValue( + getFindBulkResultStatus() + ); // successful status search clients.rulesClient.get.mockResolvedValue( getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); @@ -48,7 +50,7 @@ describe.each([ }); test('catch error when status search throws error', async () => { - clients.ruleExecutionLogClient.findBulk.mockImplementation(async () => { + clients.ruleExecutionLogClient.getCurrentStatusBulk.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(ruleStatusRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index 71ebe23f124d2..a1668df42eada 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -48,31 +48,30 @@ export const findRulesStatusesRoute = (router: SecuritySolutionPluginRouter) => const ids = body.ids; try { const ruleStatusClient = context.securitySolution.getExecutionLogClient(); - const [statusesById, failingRules] = await Promise.all([ - ruleStatusClient.findBulk({ + const [currentStatusesByRuleId, failingRules] = await Promise.all([ + ruleStatusClient.getCurrentStatusBulk({ ruleIds: ids, - logsCount: 6, spaceId: context.securitySolution.getSpaceId(), }), getFailingRules(ids, rulesClient), ]); const statuses = ids.reduce((acc, id) => { - const lastFiveErrorsForId = statusesById[id]; + const currentStatus = currentStatusesByRuleId[id]; + const failingRule = failingRules[id]; - if (lastFiveErrorsForId == null || lastFiveErrorsForId.length === 0) { + if (currentStatus == null) { return acc; } - const failingRule = failingRules[id]; + const finalCurrentStatus = + failingRule != null + ? mergeAlertWithSidecarStatus(failingRule, currentStatus) + : currentStatus; - if (failingRule != null) { - const currentStatus = mergeAlertWithSidecarStatus(failingRule, lastFiveErrorsForId[0]); - const updatedLastFiveErrorsSO = [currentStatus, ...lastFiveErrorsForId.slice(1)]; - return mergeStatuses(id, updatedLastFiveErrorsSO, acc); - } - return mergeStatuses(id, [...lastFiveErrorsForId], acc); + return mergeStatuses(id, [finalCurrentStatus], acc); }, {}); + return response.ok({ body: statuses }); } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 2b514ba911091..eb334e90e5fbd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -194,12 +194,11 @@ export const patchRulesBulkRoute = ( exceptionsList, }); if (rule != null && rule.enabled != null && rule.name != null) { - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 1, + const ruleStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); - return transformValidateBulkError(rule.id, rule, ruleStatuses, isRuleRegistryEnabled); + return transformValidateBulkError(rule.id, rule, [ruleStatus], isRuleRegistryEnabled); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index 00d7180dfc9be..fe8e4470a61cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -10,7 +10,7 @@ import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../mach import { buildMlAuthz } from '../../../machine_learning/authz'; import { getEmptyFindResult, - getRuleExecutionStatuses, + getRuleExecutionStatusSucceeded, getAlertMock, getPatchRequest, getFindResultWithSingleHit, @@ -46,8 +46,15 @@ describe.each([ getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); // successful update clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // successful transform - clients.savedObjectsClient.create.mockResolvedValue(getRuleExecutionStatuses()[0]); // successful transform - clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); + clients.savedObjectsClient.create.mockResolvedValue({ + type: 'my-type', + id: 'e0b86950-4e9f-11ea-bdbd-07b56aa159b3', + attributes: getRuleExecutionStatusSucceeded(), + references: [], + }); // successful transform + clients.ruleExecutionLogClient.getCurrentStatus.mockResolvedValue( + getRuleExecutionStatusSucceeded() + ); patchRulesRoute(server.router, ml, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index 0096cd2e38180..bb9f7e1475247 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -195,17 +195,12 @@ export const patchRulesRoute = ( exceptionsList, }); if (rule != null && rule.enabled != null && rule.name != null) { - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 1, + const ruleStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); - const [validated, errors] = transformValidate( - rule, - ruleStatuses[0], - isRuleRegistryEnabled - ); + const [validated, errors] = transformValidate(rule, ruleStatus, isRuleRegistryEnabled); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index 44f2577e032b5..251ff1e6e5f38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -109,16 +109,10 @@ export const performBulkActionRoute = ( case BulkAction.delete: await Promise.all( rules.data.map(async (rule) => { - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 6, - ruleId: rule.id, - spaceId: context.securitySolution.getSpaceId(), - }); await deleteRules({ + ruleId: rule.id, rulesClient, ruleStatusClient, - ruleStatuses, - id: rule.id, }); }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index bc9fa43b56ae7..4264ca9961bd4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -16,6 +16,7 @@ import { getFindResultWithSingleHit, nonRuleFindResult, getEmptySavedObjectsResponse, + getRuleExecutionStatusSucceeded, resolveAlertMock, } from '../__mocks__/request_responses'; import { requestMock, requestContextMock, serverMock } from '../__mocks__'; @@ -37,7 +38,9 @@ describe.each([ clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // successful transform - clients.ruleExecutionLogClient.find.mockResolvedValue([]); + clients.ruleExecutionLogClient.getCurrentStatus.mockResolvedValue( + getRuleExecutionStatusSucceeded() + ); clients.rulesClient.resolve.mockResolvedValue({ ...resolveAlertMock(isRuleRegistryEnabled, { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts index c3d6f09c306f0..06d0b9d8c327a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -70,12 +70,10 @@ export const readRulesRoute = ( ruleAlertId: rule.id, logger, }); - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 1, + const currentStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); - const [currentStatus] = ruleStatuses; if (currentStatus != null && rule.executionStatus.status === 'error') { currentStatus.attributes.lastFailureMessage = `Reason: ${rule.executionStatus.error?.reason} Message: ${rule.executionStatus.error?.message}`; currentStatus.attributes.lastFailureAt = diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 067f7b80dfca1..000c87e5b3f9b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -95,12 +95,11 @@ export const updateRulesBulkRoute = ( isRuleRegistryEnabled, }); if (rule != null) { - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 1, + const ruleStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); - return transformValidateBulkError(rule.id, rule, ruleStatuses, isRuleRegistryEnabled); + return transformValidateBulkError(rule.id, rule, [ruleStatus], isRuleRegistryEnabled); } else { return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 37df792b421b0..131015880053c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -12,6 +12,7 @@ import { getAlertMock, getUpdateRequest, getFindResultWithSingleHit, + getRuleExecutionStatusSucceeded, nonRuleFindResult, typicalMlRulePayload, } from '../__mocks__/request_responses'; @@ -43,8 +44,11 @@ describe.each([ clients.rulesClient.update.mockResolvedValue( getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); // successful update - clients.ruleExecutionLogClient.find.mockResolvedValue([]); // successful transform: ; + clients.ruleExecutionLogClient.getCurrentStatus.mockResolvedValue( + getRuleExecutionStatusSucceeded() + ); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); + updateRulesRoute(server.router, ml, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 543591c415a6b..1aad28d110bd9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -86,16 +86,11 @@ export const updateRulesRoute = ( }); if (rule != null) { - const ruleStatuses = await ruleStatusClient.find({ - logsCount: 1, + const ruleStatus = await ruleStatusClient.getCurrentStatus({ ruleId: rule.id, spaceId: context.securitySolution.getSpaceId(), }); - const [validated, errors] = transformValidate( - rule, - ruleStatuses[0], - isRuleRegistryEnabled - ); + const [validated, errors] = transformValidate(rule, ruleStatus, isRuleRegistryEnabled); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 7472d41b9ab77..e706a3c914974 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -6,7 +6,6 @@ */ import { countBy } from 'lodash/fp'; -import { SavedObject } from 'kibana/server'; import uuid from 'uuid'; import { RulesSchema } from '../../../../../common/detection_engine/schemas/response/rules_schema'; @@ -18,8 +17,7 @@ import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; import { RuleAlertType, isAlertType, - IRuleSavedAttributesSavedObjectAttributes, - isRuleStatusSavedObjectType, + isRuleStatusSavedObjectAttributes, IRuleStatusSOAttributes, } from '../../rules/types'; import { createBulkErrorObject, BulkError, OutputError } from '../utils'; @@ -98,10 +96,10 @@ export const transformTags = (tags: string[]): string[] => { // those on the export export const transformAlertToRule = ( alert: SanitizedAlert, - ruleStatus?: SavedObject, + ruleStatus?: IRuleStatusSOAttributes, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): Partial => { - return internalRuleToAPIResponse(alert, ruleStatus?.attributes, legacyRuleActions); + return internalRuleToAPIResponse(alert, ruleStatus, legacyRuleActions); }; export const transformAlertsToRules = ( @@ -113,7 +111,7 @@ export const transformAlertsToRules = ( export const transformFindAlerts = ( findResults: FindResult, - ruleStatuses: { [key: string]: IRuleStatusSOAttributes[] | undefined }, + currentStatusesByRuleId: { [key: string]: IRuleStatusSOAttributes | undefined }, legacyRuleActions: Record ): { page: number; @@ -126,8 +124,7 @@ export const transformFindAlerts = ( perPage: findResults.perPage, total: findResults.total, data: findResults.data.map((alert) => { - const statuses = ruleStatuses[alert.id]; - const status = statuses ? statuses[0] : undefined; + const status = currentStatusesByRuleId[alert.id]; return internalRuleToAPIResponse(alert, status, legacyRuleActions[alert.id]); }), }; @@ -135,14 +132,14 @@ export const transformFindAlerts = ( export const transform = ( alert: PartialAlert, - ruleStatus?: SavedObject, + ruleStatus?: IRuleStatusSOAttributes, isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): Partial | null => { if (isAlertType(isRuleRegistryEnabled ?? false, alert)) { return transformAlertToRule( alert, - isRuleStatusSavedObjectType(ruleStatus) ? ruleStatus : undefined, + isRuleStatusSavedObjectAttributes(ruleStatus) ? ruleStatus : undefined, legacyRuleActions ); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index 307b6c96da3e5..f1c2eff1f898f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { SavedObject, SavedObjectsFindResult } from 'kibana/server'; - import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { FullResponseSchema, @@ -19,9 +17,8 @@ import { import { PartialAlert } from '../../../../../../alerting/server'; import { isAlertType, - IRuleSavedAttributesSavedObjectAttributes, IRuleStatusSOAttributes, - isRuleStatusSavedObjectType, + isRuleStatusSavedObjectAttributes, } from '../../rules/types'; import { createBulkErrorObject, BulkError } from '../utils'; import { transform, transformAlertToRule } from './utils'; @@ -31,7 +28,7 @@ import { LegacyRulesActionsSavedObject } from '../../rule_actions/legacy_get_rul export const transformValidate = ( alert: PartialAlert, - ruleStatus?: SavedObject, + ruleStatus?: IRuleStatusSOAttributes, isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): [RulesSchema | null, string | null] => { @@ -45,7 +42,7 @@ export const transformValidate = ( export const newTransformValidate = ( alert: PartialAlert, - ruleStatus?: SavedObject, + ruleStatus?: IRuleStatusSOAttributes, isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): [FullResponseSchema | null, string | null] => { @@ -60,11 +57,11 @@ export const newTransformValidate = ( export const transformValidateBulkError = ( ruleId: string, alert: PartialAlert, - ruleStatus?: Array>, + ruleStatus?: IRuleStatusSOAttributes[], isRuleRegistryEnabled?: boolean ): RulesSchema | BulkError => { if (isAlertType(isRuleRegistryEnabled ?? false, alert)) { - if (ruleStatus && ruleStatus?.length > 0 && isRuleStatusSavedObjectType(ruleStatus[0])) { + if (ruleStatus && ruleStatus?.length > 0 && isRuleStatusSavedObjectAttributes(ruleStatus[0])) { const transformed = transformAlertToRule(alert, ruleStatus[0]); const [validated, errors] = validateNonExact(transformed, rulesSchema); if (errors != null || validated == null) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts index 2d82cd7f8732a..42d7f960beb22 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts @@ -6,10 +6,9 @@ */ import { rulesClientMock } from '../../../../../alerting/server/mocks'; -import { deleteRules } from './delete_rules'; -import { SavedObjectsFindResult } from '../../../../../../../src/core/server'; -import { DeleteRuleOptions, IRuleStatusSOAttributes } from './types'; import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client'; +import { deleteRules } from './delete_rules'; +import { DeleteRuleOptions } from './types'; describe('deleteRules', () => { let rulesClient: ReturnType; @@ -21,35 +20,15 @@ describe('deleteRules', () => { }); it('should delete the rule along with its actions, and statuses', async () => { - const ruleStatus: SavedObjectsFindResult = { - id: 'statusId', - type: '', - references: [], - attributes: { - statusDate: '', - lastFailureAt: null, - lastFailureMessage: null, - lastSuccessAt: null, - lastSuccessMessage: null, - status: null, - lastLookBackDate: null, - gap: null, - bulkCreateTimeDurations: null, - searchAfterTimeDurations: null, - }, - score: 0, - }; - - const rule: DeleteRuleOptions = { + const options: DeleteRuleOptions = { + ruleId: 'ruleId', rulesClient, ruleStatusClient, - id: 'ruleId', - ruleStatuses: [ruleStatus], }; - await deleteRules(rule); + await deleteRules(options); - expect(rulesClient.delete).toHaveBeenCalledWith({ id: rule.id }); - expect(ruleStatusClient.delete).toHaveBeenCalledWith(ruleStatus.id); + expect(rulesClient.delete).toHaveBeenCalledWith({ id: options.ruleId }); + expect(ruleStatusClient.deleteCurrentStatus).toHaveBeenCalledWith(options.ruleId); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts index 5003dbf0279e4..880132434f7cc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts @@ -5,17 +5,9 @@ * 2.0. */ -import { asyncForEach } from '@kbn/std'; import { DeleteRuleOptions } from './types'; -export const deleteRules = async ({ - rulesClient, - ruleStatusClient, - ruleStatuses, - id, -}: DeleteRuleOptions) => { - await rulesClient.delete({ id }); - await asyncForEach(ruleStatuses, async (obj) => { - await ruleStatusClient.delete(obj.id); - }); +export const deleteRules = async ({ ruleId, rulesClient, ruleStatusClient }: DeleteRuleOptions) => { + await rulesClient.delete({ id: ruleId }); + await ruleStatusClient.deleteCurrentStatus(ruleId); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts index b75a1b0d80e9a..e24da8a2ba0d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts @@ -33,25 +33,11 @@ export const enableRule = async ({ }: EnableRuleArgs) => { await rulesClient.enable({ id: rule.id }); - const ruleCurrentStatus = await ruleStatusClient.find({ - logsCount: 1, + await ruleStatusClient.logStatusChange({ ruleId: rule.id, + ruleName: rule.name, + ruleType: rule.alertTypeId, spaceId, + newStatus: RuleExecutionStatus['going to run'], }); - - // set current status for this rule to be 'going to run' - if (ruleCurrentStatus && ruleCurrentStatus.length > 0) { - const currentStatusToDisable = ruleCurrentStatus[0]; - await ruleStatusClient.update({ - id: currentStatusToDisable.id, - ruleId: rule.id, - ruleName: rule.name, - ruleType: rule.alertTypeId, - attributes: { - ...currentStatusToDisable.attributes, - status: RuleExecutionStatus['going to run'], - }, - spaceId, - }); - } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index 037f85091bfcc..ed0f0447ad3b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -8,12 +8,7 @@ import { get } from 'lodash/fp'; import { Readable } from 'stream'; -import { - SavedObject, - SavedObjectAttributes, - SavedObjectsClientContract, - SavedObjectsFindResult, -} from 'kibana/server'; +import { SavedObject, SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/server'; import type { MachineLearningJobIdOrUndefined, From, @@ -207,10 +202,8 @@ export const isAlertType = ( : partialAlert.alertTypeId === SIGNALS_ID; }; -export const isRuleStatusSavedObjectType = ( - obj: unknown -): obj is SavedObject => { - return get('attributes', obj) != null; +export const isRuleStatusSavedObjectAttributes = (obj: unknown): obj is IRuleStatusSOAttributes => { + return get('status', obj) != null; }; export interface CreateRulesOptions { @@ -342,10 +335,9 @@ export interface ReadRuleOptions { } export interface DeleteRuleOptions { + ruleId: Id; rulesClient: RulesClient; ruleStatusClient: IRuleExecutionLogClient; - ruleStatuses: Array>; - id: Id; } export interface FindRuleOptions {