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] Adds rule execution log table #126215

Merged
merged 52 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2bcf458
Adds Rule Execution Log table
spong Feb 23, 2022
c7f91ae
Removes total alerts created and detected metrics
spong Feb 23, 2022
e100c42
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Feb 23, 2022
8699e4d
i18n fix
spong Feb 23, 2022
038a2d3
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Feb 24, 2022
817adc8
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Feb 28, 2022
fbe882b
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 1, 2022
5cc47c1
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 3, 2022
923bdd1
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 7, 2022
7207d0f
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 8, 2022
7e3f96b
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 11, 2022
373ba43
Sort type fix
spong Mar 11, 2022
f0074d7
Adds FR translations
spong Mar 11, 2022
0ad3bb2
Reworks to use event-log aggregations
spong Mar 14, 2022
2fba083
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 14, 2022
de5e2e4
Resolving duplicate i18n keys
spong Mar 14, 2022
49e7431
Fixing existing tests
spong Mar 14, 2022
8331602
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 14, 2022
5c5c81c
Move to EuiBasictable and support pagination & sorting through API
spong Mar 15, 2022
9ec38ef
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 15, 2022
a6df26e
Fixing tests
spong Mar 15, 2022
aea19d9
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 15, 2022
c9a6088
More test updates
spong Mar 15, 2022
7a74092
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 15, 2022
53edd9a
Adding API integration and backend tests
spong Mar 16, 2022
72fe407
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 16, 2022
a1cc082
Adding remaining test stubs
spong Mar 16, 2022
6511b3b
Fixing more tests
spong Mar 16, 2022
defb927
Removing broken sort and fixing showing count on table
spong Mar 16, 2022
a3229d7
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 16, 2022
bf2940f
Removing unused import
spong Mar 16, 2022
024bf30
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 16, 2022
702ad4f
Removing another unused import typechecker missed
spong Mar 16, 2022
fbbc311
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 16, 2022
f9ca0ea
Hides SearchBar, ui & bugfixes, and expanding API integration tests
spong Mar 18, 2022
6b02501
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 18, 2022
0f0f01a
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 18, 2022
8b57322
Increases integrity of test outputs
spong Mar 18, 2022
c4bfb27
Bugfixes around table counts and fixing snapshot
spong Mar 18, 2022
80f1e21
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 18, 2022
bf98dfa
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 20, 2022
3f8891b
Copy fixes
spong Mar 21, 2022
e2964dc
Updated snapshot
spong Mar 21, 2022
8430e1f
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 21, 2022
1e8dbfb
Fixing duration display and adding format for hours
spong Mar 22, 2022
d3f7b70
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 22, 2022
c9f9752
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 23, 2022
ca6e3f4
Type fixes and fallback duration fix
spong Mar 23, 2022
81a282f
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 24, 2022
2b294ad
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 25, 2022
611a0c0
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 28, 2022
9f847a3
Merge branch 'main' of github.com:elastic/kibana into adds-rule-execu…
spong Mar 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/kbn-securitysolution-rules/src/configuration_constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

/**
* Max number of execution events to aggregate in memory for the Rule Execution Log
*/
export const MAX_EXECUTION_EVENTS_DISPLAYED = 1000 as const;
1 change: 1 addition & 0 deletions packages/kbn-securitysolution-rules/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

export * from './configuration_constants';
export * from './rule_type_constants';
export * from './rule_type_mappings';
export * from './utils';
3 changes: 3 additions & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,6 @@ export const RULES_TABLE_PAGE_SIZE_OPTIONS = [5, 10, 20, 50, RULES_TABLE_MAX_PAG
*/
export const RULES_MANAGEMENT_FEATURE_TOUR_STORAGE_KEY =
'securitySolution.rulesManagementPage.newFeaturesTour.v8.1';

export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY =
'securitySolution.ruleDetails.ruleExecutionLog.showMetrics.v8.2';
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,31 @@ export const ruleExecutionEvent = t.type({
});

export type RuleExecutionEvent = t.TypeOf<typeof ruleExecutionEvent>;

// -------------------------------------------------------------------------------------------------
// Aggregate Rule execution events

export const aggregateRuleExecutionEvent = t.type({
execution_uuid: t.string,
timestamp: IsoDateString,
duration_ms: t.number,
status: t.string,
message: t.string,
num_active_alerts: t.number,
num_new_alerts: t.number,
num_recovered_alerts: t.number,
num_triggered_actions: t.number,
num_succeeded_actions: t.number,
num_errored_actions: t.number,
total_search_duration_ms: t.number,
es_search_duration_ms: t.number,
schedule_delay_ms: t.number,
timed_out: t.boolean,
indexing_duration_ms: t.number,
search_duration_ms: t.number,
gap_duration_ms: t.number,
security_status: t.string,
security_message: t.string,
});

export type AggregateRuleExecutionEvent = t.TypeOf<typeof aggregateRuleExecutionEvent>;
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,27 @@

import * as t from 'io-ts';

import { sortFieldOrUndefined, sortOrderOrUndefined } from '../common';

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

export const GetRuleExecutionEventsQueryParams = t.exact(
t.type({
start: t.string,
end: t.string,
query_text: t.union([t.string, t.undefined]),
status_filters: t.union([t.string, t.undefined]),
per_page: t.union([t.string, t.undefined]),
page: t.union([t.string, t.undefined]),
sort_field: sortFieldOrUndefined,
sort_order: sortOrderOrUndefined,
})
);

export type GetRuleExecutionEventsRequestParams = t.TypeOf<
typeof GetRuleExecutionEventsRequestParams
>;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import * as t from 'io-ts';
import { ruleExecutionEvent } from '../common';
import { aggregateRuleExecutionEvent, ruleExecutionEvent } from '../common';

export const GetRuleExecutionEventsResponse = t.exact(
t.type({
Expand All @@ -15,3 +15,14 @@ export const GetRuleExecutionEventsResponse = t.exact(
);

export type GetRuleExecutionEventsResponse = t.TypeOf<typeof GetRuleExecutionEventsResponse>;

export const GetAggregateRuleExecutionEventsResponse = t.exact(
t.type({
events: t.array(aggregateRuleExecutionEvent),
total: t.number,
})
);

export type GetAggregateRuleExecutionEventsResponse = t.TypeOf<
typeof GetAggregateRuleExecutionEventsResponse
>;
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
* 2.0.
*/

import { RuleExecutionStatus } from '../../../../../../common/detection_engine/schemas/common';
import {
GetRuleExecutionEventsResponse,
GetAggregateRuleExecutionEventsResponse,
RulesSchema,
} from '../../../../../../common/detection_engine/schemas/response';

Expand Down Expand Up @@ -62,19 +61,44 @@ export const fetchRules = async (_: FetchRulesProps): Promise<FetchRulesResponse

export const fetchRuleExecutionEvents = async ({
ruleId,
start,
end,
filters,
signal,
}: {
ruleId: string;
start: string;
end: string;
filters?: string;
signal?: AbortSignal;
}): Promise<GetRuleExecutionEventsResponse> => {
}): Promise<GetAggregateRuleExecutionEventsResponse> => {
return Promise.resolve({
events: [
{
date: '2021-12-29T10:42:59.996Z',
status: RuleExecutionStatus.succeeded,
message: 'Rule executed successfully',
duration_ms: 3866,
es_search_duration_ms: 1236,
execution_uuid: '88d15095-7937-462c-8f21-9763e1387cad',
gap_duration_ms: 0,
indexing_duration_ms: 95,
message:
"rule executed: siem.queryRule:fb1fc150-a292-11ec-a2cf-c1b28b0392b0: 'Lots of Execution Events'",
num_active_alerts: 0,
num_errored_actions: 0,
num_new_alerts: 0,
num_recovered_alerts: 0,
num_succeeded_actions: 1,
num_triggered_actions: 1,
schedule_delay_ms: -127535,
search_duration_ms: 1255,
security_message: 'succeeded',
security_status: 'succeeded',
status: 'success',
timed_out: false,
timestamp: '2022-03-13T06:04:05.838Z',
total_search_duration_ms: 0,
},
],
total: 1,
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,19 +595,43 @@ describe('Detections Rules API', () => {
});

test('calls API with correct parameters', async () => {
await fetchRuleExecutionEvents({ ruleId: '42', signal: abortCtrl.signal });
await fetchRuleExecutionEvents({
ruleId: '42',
start: '2001-01-01T17:00:00.000Z',
end: '2001-01-02T17:00:00.000Z',
queryText: '',
statusFilters: '',
signal: abortCtrl.signal,
});

expect(fetchMock).toHaveBeenCalledWith(
'/internal/detection_engine/rules/42/execution/events',
{
method: 'GET',
query: {
end: '2001-01-02T17:00:00.000Z',
page: undefined,
per_page: undefined,
query_text: '',
sort_field: undefined,
sort_order: undefined,
start: '2001-01-01T17:00:00.000Z',
status_filters: '',
},
signal: abortCtrl.signal,
}
);
});

test('returns API response as is', async () => {
const response = await fetchRuleExecutionEvents({ ruleId: '42', signal: abortCtrl.signal });
const response = await fetchRuleExecutionEvents({
ruleId: '42',
start: 'now-30',
end: 'now',
queryText: '',
statusFilters: '',
signal: abortCtrl.signal,
});
expect(response).toEqual(responseMock);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { camelCase } from 'lodash';
import dateMath from '@elastic/datemath';
import { HttpStart } from 'src/core/public';

import {
Expand All @@ -24,7 +25,7 @@ import {
} from '../../../../../common/detection_engine/schemas/request';
import {
RulesSchema,
GetRuleExecutionEventsResponse,
GetAggregateRuleExecutionEventsResponse,
} from '../../../../../common/detection_engine/schemas/response';

import {
Expand Down Expand Up @@ -315,21 +316,57 @@ export const exportRules = async ({
/**
* Fetch rule execution events (e.g. status changes) from Event Log.
*
* @param ruleId string Saved Object ID of the rule (`rule.id`, not static `rule.rule_id`)
* @param ruleId Saved Object ID of the rule (`rule.id`, not static `rule.rule_id`)
* @param start Start daterange either in UTC ISO8601 or as datemath string (e.g. `2021-12-29T02:44:41.653Z` or `now-30`)
* @param end End daterange either in UTC ISO8601 or as datemath string (e.g. `2021-12-29T02:44:41.653Z` or `now/w`)
* @param queryText search string in querystring format (e.g. `event.duration > 1000 OR kibana.alert.rule.execution.metrics.execution_gap_duration_s > 100`)
* @param statusFilters comma separated string of `statusFilters` (e.g. `succeeded,failed,partial failure`)
* @param page current page to fetch
* @param perPage number of results to fetch per page
* @param sortField field to sort by
* @param sortOrder what order to sort by (e.g. `asc` or `desc`)
* @param signal AbortSignal Optional signal for cancelling the request
*
* @throws An error if response is not OK
*/
export const fetchRuleExecutionEvents = async ({
ruleId,
start,
end,
queryText,
statusFilters,
page,
perPage,
sortField,
sortOrder,
signal,
}: {
ruleId: string;
start: string;
end: string;
queryText?: string;
statusFilters?: string;
page?: number;
perPage?: number;
sortField?: string;
sortOrder?: string;
signal?: AbortSignal;
}): Promise<GetRuleExecutionEventsResponse> => {
}): Promise<GetAggregateRuleExecutionEventsResponse> => {
const url = detectionEngineRuleExecutionEventsUrl(ruleId);
return KibanaServices.get().http.fetch<GetRuleExecutionEventsResponse>(url, {
const startDate = dateMath.parse(start);
const endDate = dateMath.parse(end, { roundUp: true });
return KibanaServices.get().http.fetch<GetAggregateRuleExecutionEventsResponse>(url, {
method: 'GET',
query: {
start: startDate?.utc().toISOString(),
end: endDate?.utc().toISOString(),
query_text: queryText?.trim(),
status_filters: statusFilters?.trim(),
page,
per_page: perPage,
sort_field: sortField,
sort_order: sortOrder,
},
signal,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export const RULE_AND_TIMELINE_FETCH_FAILURE = i18n.translate(
}
);

export const RULE_EXECUTION_FETCH_FAILURE = i18n.translate(
'xpack.securitySolution.containers.detectionEngine.ruleExecutionLogFailureDescription',
{
defaultMessage: 'Failed to fetch Rule Execution Events',
}
);

export const RULE_ADD_FAILURE = i18n.translate(
'xpack.securitySolution.containers.detectionEngine.addRuleFailDescription',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { renderHook, cleanup } from '@testing-library/react-hooks';

import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common';
import { useRuleExecutionEvents } from './use_rule_execution_events';

import * as api from './api';
Expand Down Expand Up @@ -45,9 +44,19 @@ describe('useRuleExecutionEvents', () => {
};

const render = () =>
renderHook(() => useRuleExecutionEvents(SOME_RULE_ID), {
wrapper: createReactQueryWrapper(),
});
renderHook(
() =>
useRuleExecutionEvents({
ruleId: SOME_RULE_ID,
start: 'now-30',
end: 'now',
queryText: '',
statusFilters: '',
}),
{
wrapper: createReactQueryWrapper(),
}
);

it('calls the API via fetchRuleExecutionEvents', async () => {
const fetchRuleExecutionEvents = jest.spyOn(api, 'fetchRuleExecutionEvents');
Expand Down Expand Up @@ -77,13 +86,34 @@ describe('useRuleExecutionEvents', () => {
expect(result.current.isLoading).toEqual(false);
expect(result.current.isSuccess).toEqual(true);
expect(result.current.isError).toEqual(false);
expect(result.current.data).toEqual([
{
date: '2021-12-29T10:42:59.996Z',
status: RuleExecutionStatus.succeeded,
message: 'Rule executed successfully',
},
]);
expect(result.current.data).toEqual({
events: [
{
duration_ms: 3866,
es_search_duration_ms: 1236,
execution_uuid: '88d15095-7937-462c-8f21-9763e1387cad',
gap_duration_ms: 0,
indexing_duration_ms: 95,
message:
"rule executed: siem.queryRule:fb1fc150-a292-11ec-a2cf-c1b28b0392b0: 'Lots of Execution Events'",
num_active_alerts: 0,
num_errored_actions: 0,
num_new_alerts: 0,
num_recovered_alerts: 0,
num_succeeded_actions: 1,
num_triggered_actions: 1,
schedule_delay_ms: -127535,
search_duration_ms: 1255,
security_message: 'succeeded',
security_status: 'succeeded',
status: 'success',
timed_out: false,
timestamp: '2022-03-13T06:04:05.838Z',
total_search_duration_ms: 0,
},
],
total: 1,
});
});

it('handles exceptions from the API', async () => {
Expand Down
Loading