Skip to content
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

[Security Solution][Detections] Reading last 5 failures from Event Log v1 - raw implementation #115574

Merged
7 changes: 7 additions & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ export const DETECTION_ENGINE_RULES_PREVIEW = `${DETECTION_ENGINE_RULES_URL}/pre
export const DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL =
`${DETECTION_ENGINE_RULES_PREVIEW}/index` as const;

/**
* Internal detection engine routes
*/
export const INTERNAL_DETECTION_ENGINE_URL = '/internal/detection_engine' as const;
export const INTERNAL_DETECTION_ENGINE_RULE_STATUS_URL =
`${INTERNAL_DETECTION_ENGINE_URL}/rules/_find_status` as const;

export const TIMELINE_RESOLVE_URL = '/api/timeline/resolve' as const;
export const TIMELINE_URL = '/api/timeline' as const;
export const TIMELINES_URL = '/api/timelines' as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ export const findRulesStatusesSchema = t.exact(
export type FindRulesStatusesSchema = t.TypeOf<typeof findRulesStatusesSchema>;

export type FindRulesStatusesSchemaDecoded = FindRulesStatusesSchema;

export const findRuleStatusSchema = t.exact(
t.type({
ruleId: t.string,
})
);

export type FindRuleStatusSchema = t.TypeOf<typeof findRuleStatusSchema>;

export type FindRuleStatusSchemaDecoded = FindRuleStatusSchema;
Original file line number Diff line number Diff line change
Expand Up @@ -669,8 +669,8 @@ describe('Detections Rules API', () => {

test('check parameter url, query', async () => {
await getRuleStatusById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find_statuses', {
body: '{"ids":["mySuperRuleId"]}',
expect(fetchMock).toHaveBeenCalledWith('/internal/detection_engine/rules/_find_status', {
body: '{"ruleId":"mySuperRuleId"}',
method: 'POST',
signal: abortCtrl.signal,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DETECTION_ENGINE_TAGS_URL,
DETECTION_ENGINE_RULES_BULK_ACTION,
DETECTION_ENGINE_RULES_PREVIEW,
INTERNAL_DETECTION_ENGINE_RULE_STATUS_URL,
} from '../../../../../common/constants';
import {
UpdateRulesProps,
Expand Down Expand Up @@ -372,9 +373,9 @@ export const getRuleStatusById = async ({
id: string;
signal: AbortSignal;
}): Promise<RuleStatusResponse> =>
KibanaServices.get().http.fetch<RuleStatusResponse>(DETECTION_ENGINE_RULES_STATUS_URL, {
KibanaServices.get().http.fetch<RuleStatusResponse>(INTERNAL_DETECTION_ENGINE_RULE_STATUS_URL, {
method: 'POST',
body: JSON.stringify({ ids: [id] }),
body: JSON.stringify({ ruleId: id }),
signal,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -23,6 +23,7 @@ import {
DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL,
DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL,
DETECTION_ENGINE_RULES_BULK_ACTION,
INTERNAL_DETECTION_ENGINE_RULE_STATUS_URL,
} from '../../../../../common/constants';
import {
RuleAlertType,
Expand All @@ -42,7 +43,7 @@ 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';
// eslint-disable-next-line no-restricted-imports
import type { LegacyRuleNotificationAlertType } from '../../notifications/legacy_types';

Expand Down Expand Up @@ -232,6 +233,13 @@ export const ruleStatusRequest = () =>
body: { ids: ['04128c15-0d1b-4716-a4c5-46997ac7f3bd'] },
});

export const internalRuleStatusRequest = () =>
requestMock.create({
method: 'post',
path: INTERNAL_DETECTION_ENGINE_RULE_STATUS_URL,
body: { ruleId: '04128c15-0d1b-4716-a4c5-46997ac7f3bd' },
});

export const getImportRulesRequest = (hapiStream?: HapiReadableStream) =>
requestMock.create({
method: 'post',
Expand Down Expand Up @@ -475,94 +483,64 @@ export const getEmptySavedObjectsResponse =
saved_objects: [],
});

export const getRuleExecutionStatuses = (): Array<
SavedObjectsFindResult<IRuleStatusSOAttributes>
> => [
{
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<unknown> => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
getEmptyFindResult,
getAlertMock,
getCreateRequest,
getRuleExecutionStatuses,
getRuleExecutionStatusSucceeded,
getFindResultWithSingleHit,
createMlRuleRequest,
getBasicEmptySearchResponse,
Expand Down Expand Up @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
getDeleteRequest,
getFindResultWithSingleHit,
getDeleteRequestById,
getRuleExecutionStatuses,
getRuleExecutionStatusSucceeded,
getEmptySavedObjectsResponse,
} from '../__mocks__/request_responses';
import { requestContextMock, serverMock, requestMock } from '../__mocks__';
Expand All @@ -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);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading