[Security Solution][Detections] Updates RuleDetails to query alerts by RuleId instead of Rule SO ID (#120053) (#120710)

* Switches RuleDetails to query alerts by ruleId instead of SO id

* Increases integrity of test outputs

* Cleans up duplicate RuleRegistry functions

* Removes support for rule.id for alerts filter and updates exceptions to use new filter
This commit is contained in:
Garrett Spong 2021-12-07 23:17:36 -07:00 committed by GitHub
parent 32ff2d11e9
commit 3de8da3964
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 102 additions and 294 deletions

View file

@ -24,6 +24,7 @@ const VERSION = `${KIBANA_NAMESPACE}.version` as const;
// Fields pertaining to the alert
const ALERT_ACTION_GROUP = `${ALERT_NAMESPACE}.action_group` as const;
const ALERT_BUILDING_BLOCK_TYPE = `${ALERT_NAMESPACE}.building_block_type` as const;
const ALERT_DURATION = `${ALERT_NAMESPACE}.duration.us` as const;
const ALERT_END = `${ALERT_NAMESPACE}.end` as const;
const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const;
@ -91,6 +92,7 @@ const fields = {
TAGS,
TIMESTAMP,
ALERT_ACTION_GROUP,
ALERT_BUILDING_BLOCK_TYPE,
ALERT_DURATION,
ALERT_END,
ALERT_EVALUATION_THRESHOLD,
@ -141,6 +143,7 @@ const fields = {
export {
ALERT_ACTION_GROUP,
ALERT_BUILDING_BLOCK_TYPE,
ALERT_DURATION,
ALERT_END,
ALERT_EVALUATION_THRESHOLD,

View file

@ -327,11 +327,16 @@ export const AddExceptionModal = memo(function AddExceptionModal({
const alertIdToClose = shouldCloseAlert && alertData ? alertData._id : undefined;
const bulkCloseIndex =
shouldBulkCloseAlert && signalIndexName != null ? [signalIndexName] : undefined;
addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), alertIdToClose, bulkCloseIndex);
addOrUpdateExceptionItems(
maybeRule?.rule_id ?? '',
enrichExceptionItems(),
alertIdToClose,
bulkCloseIndex
);
}
}, [
addOrUpdateExceptionItems,
ruleId,
maybeRule,
enrichExceptionItems,
shouldCloseAlert,
shouldBulkCloseAlert,

View file

@ -267,11 +267,16 @@ export const EditExceptionModal = memo(function EditExceptionModal({
if (addOrUpdateExceptionItems !== null) {
const bulkCloseIndex =
shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined;
addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), undefined, bulkCloseIndex);
addOrUpdateExceptionItems(
maybeRule?.rule_id ?? '',
enrichExceptionItems(),
undefined,
bulkCloseIndex
);
}
}, [
addOrUpdateExceptionItems,
ruleId,
maybeRule,
enrichExceptionItems,
shouldBulkCloseAlert,
signalIndexName,

View file

@ -44,16 +44,14 @@ describe('useAddOrUpdateException', () => {
let updateExceptionListItem: jest.SpyInstance<Promise<ExceptionListItemSchema>>;
let getQueryFilter: jest.SpyInstance<ReturnType<typeof getQueryFilterHelper.getQueryFilter>>;
let buildAlertStatusesFilter: jest.SpyInstance<
ReturnType<typeof buildFilterHelpers.buildAlertStatusesFilterRuleRegistry>
>;
let buildAlertsRuleIdFilter: jest.SpyInstance<
ReturnType<typeof buildFilterHelpers.buildAlertsRuleIdFilter>
ReturnType<typeof buildFilterHelpers.buildAlertStatusesFilter>
>;
let buildAlertsFilter: jest.SpyInstance<ReturnType<typeof buildFilterHelpers.buildAlertsFilter>>;
let addOrUpdateItemsArgs: Parameters<AddOrUpdateExceptionItemsFunc>;
let render: () => RenderHookResult<UseAddOrUpdateExceptionProps, ReturnUseAddOrUpdateException>;
const onError = jest.fn();
const onSuccess = jest.fn();
const ruleId = 'rule-id';
const ruleStaticId = 'rule-id';
const alertIdToClose = 'idToClose';
const bulkCloseIndex = ['.custom'];
const itemsToAdd: CreateExceptionListItemSchema[] = [
@ -128,14 +126,11 @@ describe('useAddOrUpdateException', () => {
getQueryFilter = jest.spyOn(getQueryFilterHelper, 'getQueryFilter');
buildAlertStatusesFilter = jest.spyOn(
buildFilterHelpers,
'buildAlertStatusesFilterRuleRegistry'
);
buildAlertStatusesFilter = jest.spyOn(buildFilterHelpers, 'buildAlertStatusesFilter');
buildAlertsRuleIdFilter = jest.spyOn(buildFilterHelpers, 'buildAlertsRuleIdFilter');
buildAlertsFilter = jest.spyOn(buildFilterHelpers, 'buildAlertsFilter');
addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate];
addOrUpdateItemsArgs = [ruleStaticId, itemsToAddOrUpdate];
render = () =>
renderHook<UseAddOrUpdateExceptionProps, ReturnUseAddOrUpdateException>(
() =>
@ -262,7 +257,7 @@ describe('useAddOrUpdateException', () => {
describe('when alertIdToClose is passed in', () => {
beforeEach(() => {
addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate, alertIdToClose];
addOrUpdateItemsArgs = [ruleStaticId, itemsToAddOrUpdate, alertIdToClose];
});
it('should update the alert status', async () => {
await act(async () => {
@ -317,7 +312,7 @@ describe('useAddOrUpdateException', () => {
describe('when bulkCloseIndex is passed in', () => {
beforeEach(() => {
addOrUpdateItemsArgs = [ruleId, itemsToAddOrUpdate, undefined, bulkCloseIndex];
addOrUpdateItemsArgs = [ruleStaticId, itemsToAddOrUpdate, undefined, bulkCloseIndex];
});
it('should update the status of only alerts that are open', async () => {
await act(async () => {
@ -351,8 +346,8 @@ describe('useAddOrUpdateException', () => {
addOrUpdateItems(...addOrUpdateItemsArgs);
}
await waitForNextUpdate();
expect(buildAlertsRuleIdFilter).toHaveBeenCalledTimes(1);
expect(buildAlertsRuleIdFilter.mock.calls[0][0]).toEqual(ruleId);
expect(buildAlertsFilter).toHaveBeenCalledTimes(1);
expect(buildAlertsFilter.mock.calls[0][0]).toEqual(ruleStaticId);
});
});
it('should generate the query filter using exceptions', async () => {

View file

@ -17,27 +17,25 @@ import { HttpStart } from '../../../../../../../src/core/public';
import { updateAlertStatus } from '../../../detections/containers/detection_engine/alerts/api';
import { getUpdateAlertsQuery } from '../../../detections/components/alerts_table/actions';
import {
buildAlertsRuleIdFilter,
buildAlertsFilter,
buildAlertStatusesFilter,
buildAlertStatusesFilterRuleRegistry,
} from '../../../detections/components/alerts_table/default_config';
import { getQueryFilter } from '../../../../common/detection_engine/get_query_filter';
import { Index } from '../../../../common/detection_engine/schemas/common/schemas';
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
import { formatExceptionItemForUpdate, prepareExceptionItemsForBulkClose } from './helpers';
import { useKibana } from '../../lib/kibana';
/**
* Adds exception items to the list. Also optionally closes alerts.
*
* @param ruleId id of the rule where the exception updates will be applied
* @param ruleStaticId static id of the rule (rule.ruleId, not rule.id) where the exception updates will be applied
* @param exceptionItemsToAddOrUpdate array of ExceptionListItemSchema to add or update
* @param alertIdToClose - optional string representing alert to close
* @param bulkCloseIndex - optional index used to create bulk close query
*
*/
export type AddOrUpdateExceptionItemsFunc = (
ruleId: string,
ruleStaticId: string,
exceptionItemsToAddOrUpdate: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>,
alertIdToClose?: string,
bulkCloseIndex?: Index
@ -72,10 +70,10 @@ export const useAddOrUpdateException = ({
const addOrUpdateExceptionRef = useRef<AddOrUpdateExceptionItemsFunc | null>(null);
const { addExceptionListItem, updateExceptionListItem } = useApi(services.http);
const addOrUpdateException = useCallback<AddOrUpdateExceptionItemsFunc>(
async (ruleId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => {
async (ruleStaticId, exceptionItemsToAddOrUpdate, alertIdToClose, bulkCloseIndex) => {
if (addOrUpdateExceptionRef.current != null) {
addOrUpdateExceptionRef.current(
ruleId,
ruleStaticId,
exceptionItemsToAddOrUpdate,
alertIdToClose,
bulkCloseIndex
@ -84,15 +82,13 @@ export const useAddOrUpdateException = ({
},
[]
);
// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
useEffect(() => {
let isSubscribed = true;
const abortCtrl = new AbortController();
const onUpdateExceptionItemsAndAlertStatus: AddOrUpdateExceptionItemsFunc = async (
ruleId,
ruleStaticId,
exceptionItemsToAddOrUpdate,
alertIdToClose,
bulkCloseIndex
@ -131,15 +127,16 @@ export const useAddOrUpdateException = ({
}
if (bulkCloseIndex != null) {
// TODO: Once we are past experimental phase this code should be removed
const alertStatusFilter = ruleRegistryEnabled
? buildAlertStatusesFilterRuleRegistry(['open', 'acknowledged', 'in-progress'])
: buildAlertStatusesFilter(['open', 'acknowledged', 'in-progress']);
const alertStatusFilter = buildAlertStatusesFilter([
'open',
'acknowledged',
'in-progress',
]);
const filter = getQueryFilter(
'',
'kuery',
[...buildAlertsRuleIdFilter(ruleId), ...alertStatusFilter],
[...buildAlertsFilter(ruleStaticId), ...alertStatusFilter],
bulkCloseIndex,
prepareExceptionItemsForBulkClose(exceptionItemsToAddOrUpdate),
false
@ -185,14 +182,7 @@ export const useAddOrUpdateException = ({
isSubscribed = false;
abortCtrl.abort();
};
}, [
addExceptionListItem,
http,
onSuccess,
onError,
ruleRegistryEnabled,
updateExceptionListItem,
]);
}, [addExceptionListItem, http, onSuccess, onError, updateExceptionListItem]);
return [{ isLoading }, addOrUpdateException];
};

View file

@ -7,7 +7,7 @@
import { ExistsFilter, Filter } from '@kbn/es-query';
import {
buildAlertsRuleIdFilter,
buildAlertsFilter,
buildAlertStatusesFilter,
buildAlertStatusFilter,
buildThreatMatchFilter,
@ -18,21 +18,21 @@ jest.mock('./actions');
describe('alerts default_config', () => {
describe('buildAlertsRuleIdFilter', () => {
test('given a rule id this will return an array with a single filter', () => {
const filters: Filter[] = buildAlertsRuleIdFilter('rule-id-1');
const filters: Filter[] = buildAlertsFilter('rule-id-1');
const expectedFilter: Filter = {
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: 'kibana.alert.rule.uuid',
key: 'kibana.alert.rule.rule_id',
params: {
query: 'rule-id-1',
},
},
query: {
match_phrase: {
'kibana.alert.rule.uuid': 'rule-id-1',
'kibana.alert.rule.rule_id': 'rule-id-1',
},
},
};

View file

@ -6,21 +6,13 @@
*/
import {
ALERT_DURATION,
ALERT_RULE_PRODUCER,
ALERT_START,
ALERT_BUILDING_BLOCK_TYPE,
ALERT_WORKFLOW_STATUS,
ALERT_UUID,
ALERT_RULE_UUID,
ALERT_RULE_NAME,
ALERT_RULE_CATEGORY,
ALERT_RULE_SEVERITY,
ALERT_RULE_RISK_SCORE,
ALERT_RULE_RULE_ID,
} from '@kbn/rule-data-utils/technical_field_names';
import type { Filter } from '@kbn/es-query';
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
import { ColumnHeaderOptions, RowRendererId } from '../../../../common/types/timeline';
import { RowRendererId } from '../../../../common/types/timeline';
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
@ -34,12 +26,12 @@ export const buildAlertStatusFilter = (status: Status): Filter[] => {
should: [
{
term: {
'kibana.alert.workflow_status': status,
[ALERT_WORKFLOW_STATUS]: status,
},
},
{
term: {
'kibana.alert.workflow_status': 'in-progress',
[ALERT_WORKFLOW_STATUS]: 'in-progress',
},
},
],
@ -47,7 +39,7 @@ export const buildAlertStatusFilter = (status: Status): Filter[] => {
}
: {
term: {
'kibana.alert.workflow_status': status,
[ALERT_WORKFLOW_STATUS]: status,
},
};
@ -58,7 +50,7 @@ export const buildAlertStatusFilter = (status: Status): Filter[] => {
negate: false,
disabled: false,
type: 'phrase',
key: 'kibana.alert.workflow_status',
key: ALERT_WORKFLOW_STATUS,
params: {
query: status,
},
@ -76,7 +68,7 @@ export const buildAlertStatusesFilter = (statuses: Status[]): Filter[] => {
bool: {
should: statuses.map((status) => ({
term: {
'kibana.alert.workflow_status': status,
[ALERT_WORKFLOW_STATUS]: status,
},
})),
},
@ -94,8 +86,15 @@ export const buildAlertStatusesFilter = (statuses: Status[]): Filter[] => {
];
};
export const buildAlertsRuleIdFilter = (ruleId: string | null): Filter[] =>
ruleId
/**
* Builds Kuery filter for fetching alerts for a specific rule. Takes the rule's
* static id, i.e. `rule.ruleId` (not rule.id), so that alerts for _all
* historical instances_ of the rule are returned.
*
* @param ruleStaticId Rule's static id: `rule.ruleId`
*/
export const buildAlertsFilter = (ruleStaticId: string | null): Filter[] =>
ruleStaticId
? [
{
meta: {
@ -103,14 +102,14 @@ export const buildAlertsRuleIdFilter = (ruleId: string | null): Filter[] =>
negate: false,
disabled: false,
type: 'phrase',
key: 'kibana.alert.rule.uuid',
key: ALERT_RULE_RULE_ID,
params: {
query: ruleId,
query: ruleStaticId,
},
},
query: {
match_phrase: {
'kibana.alert.rule.uuid': ruleId,
[ALERT_RULE_RULE_ID]: ruleStaticId,
},
},
},
@ -127,10 +126,10 @@ export const buildShowBuildingBlockFilter = (showBuildingBlockAlerts: boolean):
negate: true,
disabled: false,
type: 'exists',
key: 'kibana.alert.building_block_type',
key: ALERT_BUILDING_BLOCK_TYPE,
value: 'exists',
},
query: { exists: { field: 'kibana.alert.building_block_type' } },
query: { exists: { field: ALERT_BUILDING_BLOCK_TYPE } },
},
];
@ -183,121 +182,3 @@ export const requiredFieldsForActions = [
'host.os.family',
'event.code',
];
// TODO: Once we are past experimental phase this code should be removed
export const buildAlertStatusFilterRuleRegistry = (status: Status): Filter[] => {
const combinedQuery =
status === 'acknowledged'
? {
bool: {
should: [
{
term: {
[ALERT_WORKFLOW_STATUS]: status,
},
},
{
term: {
[ALERT_WORKFLOW_STATUS]: 'in-progress',
},
},
],
},
}
: {
term: {
[ALERT_WORKFLOW_STATUS]: status,
},
};
return [
{
meta: {
alias: null,
negate: false,
disabled: false,
type: 'phrase',
key: ALERT_WORKFLOW_STATUS,
params: {
query: status,
},
},
query: combinedQuery,
},
];
};
// TODO: Once we are past experimental phase this code should be removed
export const buildAlertStatusesFilterRuleRegistry = (statuses: Status[]): Filter[] => {
const combinedQuery = {
bool: {
should: statuses.map((status) => ({
term: {
[ALERT_WORKFLOW_STATUS]: status,
},
})),
},
};
return [
{
meta: {
alias: null,
negate: false,
disabled: false,
},
query: combinedQuery,
},
];
};
export const buildShowBuildingBlockFilterRuleRegistry = (
showBuildingBlockAlerts: boolean
): Filter[] =>
showBuildingBlockAlerts
? []
: [
{
meta: {
alias: null,
negate: true,
disabled: false,
type: 'exists',
key: 'kibana.alert.building_block_type',
value: 'exists',
},
query: { exists: { field: 'kibana.alert.building_block_type' } },
},
];
export const requiredFieldMappingsForActionsRuleRegistry = {
'@timestamp': '@timestamp',
'event.kind': 'event.kind',
'rule.severity': ALERT_RULE_SEVERITY,
'rule.risk_score': ALERT_RULE_RISK_SCORE,
'alert.uuid': ALERT_UUID,
'alert.start': ALERT_START,
'event.action': 'event.action',
'alert.workflow_status': ALERT_WORKFLOW_STATUS,
'alert.duration.us': ALERT_DURATION,
'rule.uuid': ALERT_RULE_UUID,
'rule.name': ALERT_RULE_NAME,
'rule.category': ALERT_RULE_CATEGORY,
producer: ALERT_RULE_PRODUCER,
tags: 'tags',
};
export const alertsHeadersRuleRegistry: ColumnHeaderOptions[] = Object.entries(
requiredFieldMappingsForActionsRuleRegistry
).map<ColumnHeaderOptions>(([alias, field]) => ({
columnHeaderType: defaultColumnHeaderType,
displayAsText: alias,
id: field,
}));
export const alertsDefaultModelRuleRegistry: SubsetTimelineModel = {
...timelineDefaults,
columns: alertsHeadersRuleRegistry,
showCheckboxes: true,
excludedRowRendererIds: Object.values(RowRendererId),
};

View file

@ -40,9 +40,7 @@ import { updateAlertStatusAction } from './actions';
import { AditionalFiltersAction, AlertsUtilityBar } from './alerts_utility_bar';
import {
alertsDefaultModel,
alertsDefaultModelRuleRegistry,
buildAlertStatusFilter,
buildAlertStatusFilterRuleRegistry,
requiredFieldsForActions,
} from './default_config';
import { buildTimeRangeFilter } from './helpers';
@ -106,8 +104,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
const kibana = useKibana();
const [, dispatchToaster] = useStateToaster();
const { addWarning } = useAppToasts();
// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
const ACTION_BUTTON_COUNT = 4;
const getGlobalQuery = useCallback(
@ -247,14 +243,9 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
refetchQuery: inputsModel.Refetch,
{ status, selectedStatus }: UpdateAlertsStatusProps
) => {
// TODO: Once we are past experimental phase this code should be removed
const currentStatusFilter = ruleRegistryEnabled
? buildAlertStatusFilterRuleRegistry(status)
: buildAlertStatusFilter(status);
await updateAlertStatusAction({
query: showClearSelectionAction
? getGlobalQuery(currentStatusFilter)?.filterQuery
? getGlobalQuery(buildAlertStatusFilter(status))?.filterQuery
: undefined,
alertIds: Object.keys(selectedEventIds),
selectedStatus,
@ -273,7 +264,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
showClearSelectionAction,
onAlertStatusUpdateSuccess,
onAlertStatusUpdateFailure,
ruleRegistryEnabled,
]
);
@ -327,24 +317,16 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
);
const defaultFiltersMemo = useMemo(() => {
// TODO: Once we are past experimental phase this code should be removed
const alertStatusFilter = ruleRegistryEnabled
? buildAlertStatusFilterRuleRegistry(filterGroup)
: buildAlertStatusFilter(filterGroup);
const alertStatusFilter = buildAlertStatusFilter(filterGroup);
if (isEmpty(defaultFilters)) {
return alertStatusFilter;
} else if (defaultFilters != null && !isEmpty(defaultFilters)) {
return [...defaultFilters, ...alertStatusFilter];
}
}, [defaultFilters, filterGroup, ruleRegistryEnabled]);
}, [defaultFilters, filterGroup]);
const { filterManager } = useKibana().services.data.query;
// TODO: Once we are past experimental phase this code should be removed
const defaultTimelineModel = ruleRegistryEnabled
? alertsDefaultModelRuleRegistry
: alertsDefaultModel;
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
useEffect(() => {
@ -359,7 +341,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
: c
),
documentType: i18n.ALERTS_DOCUMENT_TYPE,
excludedRowRendererIds: defaultTimelineModel.excludedRowRendererIds as RowRendererId[],
excludedRowRendererIds: alertsDefaultModel.excludedRowRendererIds as RowRendererId[],
filterManager,
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
id: timelineId,
@ -370,7 +352,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
showCheckboxes: true,
})
);
}, [dispatch, defaultTimelineModel, filterManager, tGridEnabled, timelineId]);
}, [dispatch, filterManager, tGridEnabled, timelineId]);
const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
@ -383,7 +365,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
additionalFilters={additionalFiltersComponent}
currentFilter={filterGroup}
defaultCellActions={defaultCellActions}
defaultModel={defaultTimelineModel}
defaultModel={alertsDefaultModel}
end={to}
entityType="events"
hasAlertsCrud={hasIndexWrite && hasIndexMaintenance}

View file

@ -24,7 +24,6 @@ import { connect, ConnectedProps, useDispatch } from 'react-redux';
import { Dispatch } from 'redux';
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { isTab } from '../../../../../timelines/public';
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
import { SecurityPageName } from '../../../app/types';
@ -61,9 +60,7 @@ import { timelineActions, timelineSelectors } from '../../../timelines/store/tim
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
import {
buildAlertStatusFilter,
buildAlertStatusFilterRuleRegistry,
buildShowBuildingBlockFilter,
buildShowBuildingBlockFilterRuleRegistry,
buildThreatMatchFilter,
} from '../../components/alerts_table/default_config';
import { useSourcererDataView } from '../../../common/containers/sourcerer';
@ -115,8 +112,6 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []);
const query = useDeepEqualSelector(getGlobalQuerySelector);
const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector);
// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
const { to, from } = useGlobalTime();
const { globalFullScreen } = useGlobalFullScreen();
@ -187,39 +182,20 @@ const DetectionEnginePageComponent: React.FC<DetectionEngineComponentProps> = ({
const alertsHistogramDefaultFilters = useMemo(
() => [
...filters,
...(ruleRegistryEnabled
? [
// TODO: Once we are past experimental phase this code should be removed
...buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts),
...buildAlertStatusFilterRuleRegistry(filterGroup),
]
: [
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildAlertStatusFilter(filterGroup),
]),
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildAlertStatusFilter(filterGroup),
...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts),
],
[
filters,
ruleRegistryEnabled,
showBuildingBlockAlerts,
showOnlyThreatIndicatorAlerts,
filterGroup,
]
[filters, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, filterGroup]
);
// AlertsTable manages global filters itself, so not including `filters`
const alertsTableDefaultFilters = useMemo(
() => [
...(ruleRegistryEnabled
? [
// TODO: Once we are past experimental phase this code should be removed
...buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts),
]
: [...buildShowBuildingBlockFilter(showBuildingBlockAlerts)]),
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts),
],
[ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
[showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
);
const onShowBuildingBlockAlertsChangedCallback = useCallback(

View file

@ -63,11 +63,9 @@ import { useUserData } from '../../../../components/user_info';
import { StepDefineRule } from '../../../../components/rules/step_define_rule';
import { StepScheduleRule } from '../../../../components/rules/step_schedule_rule';
import {
buildAlertsRuleIdFilter,
buildAlertsFilter,
buildAlertStatusFilter,
buildAlertStatusFilterRuleRegistry,
buildShowBuildingBlockFilter,
buildShowBuildingBlockFilterRuleRegistry,
buildThreatMatchFilter,
} from '../../../../components/alerts_table/default_config';
import { RuleSwitch } from '../../../../components/rules/rule_switch';
@ -250,9 +248,6 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
const { globalFullScreen } = useGlobalFullScreen();
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);
// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
// TODO: Steph/ueba remove when past experimental
const uebaEnabled = useIsExperimentalFeatureEnabled('uebaEnabled');
@ -416,39 +411,21 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
const alertDefaultFilters = useMemo(
() => [
...buildAlertsRuleIdFilter(ruleId),
...(ruleRegistryEnabled
? [
...buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts), // TODO: Once we are past experimental phase this code should be removed
...buildAlertStatusFilterRuleRegistry(filterGroup),
]
: [
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildAlertStatusFilter(filterGroup),
]),
...buildAlertsFilter(rule?.rule_id ?? ''),
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildAlertStatusFilter(filterGroup),
...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts),
],
[
ruleId,
ruleRegistryEnabled,
showBuildingBlockAlerts,
showOnlyThreatIndicatorAlerts,
filterGroup,
]
[rule, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, filterGroup]
);
const alertsTableDefaultFilters = useMemo(
() => [
...buildAlertsRuleIdFilter(ruleId),
...(ruleRegistryEnabled
? [
// TODO: Once we are past experimental phase this code should be removed
...buildShowBuildingBlockFilterRuleRegistry(showBuildingBlockAlerts),
]
: [...buildShowBuildingBlockFilter(showBuildingBlockAlerts)]),
...buildAlertsFilter(rule?.rule_id ?? ''),
...buildShowBuildingBlockFilter(showBuildingBlockAlerts),
...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts),
],
[ruleId, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
[rule, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts]
);
const alertMergedFilters = useMemo(

View file

@ -8,11 +8,7 @@
import { useMemo } from 'react';
import type { Filter } from '@kbn/es-query';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import {
buildShowBuildingBlockFilter,
buildShowBuildingBlockFilterRuleRegistry,
} from '../../../detections/components/alerts_table/default_config';
import { buildShowBuildingBlockFilter } from '../../../detections/components/alerts_table/default_config';
// On the Overview page, in the Detection Alert Trend, we never show
// "building block" alerts to remove noise from the Overview UI.
@ -20,17 +16,9 @@ import {
const SHOW_BUILDING_BLOCK_ALERTS = false;
export const useFiltersForSignalsByCategory = (baseFilters: Filter[]) => {
// TODO: Once we are past experimental phase this code should be removed
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
const resultingFilters = useMemo(
() => [
...baseFilters,
...(ruleRegistryEnabled
? buildShowBuildingBlockFilterRuleRegistry(SHOW_BUILDING_BLOCK_ALERTS) // TODO: Once we are past experimental phase this code should be removed
: buildShowBuildingBlockFilter(SHOW_BUILDING_BLOCK_ALERTS)),
],
[baseFilters, ruleRegistryEnabled]
() => [...baseFilters, ...buildShowBuildingBlockFilter(SHOW_BUILDING_BLOCK_ALERTS)],
[baseFilters]
);
return resultingFilters;

View file

@ -11,6 +11,7 @@ import {
getAlertMock,
getRuleExecutionStatusSucceeded,
getRuleExecutionStatusFailed,
resolveAlertMock,
} from '../__mocks__/request_responses';
import { serverMock, requestContextMock, requestMock } from '../__mocks__';
import { findRuleStatusInternalRoute } from './find_rule_status_internal_route';
@ -38,6 +39,9 @@ describe.each([
clients.rulesClient.get.mockResolvedValue(
getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
);
clients.rulesClient.resolve.mockResolvedValue(
resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
);
findRuleStatusInternalRoute(server.router);
});
@ -70,7 +74,7 @@ describe.each([
test('returns success if rule status client writes an error status', async () => {
// 0. task manager tried to run the rule but couldn't, so the alerting framework
// wrote an error to the executionStatus.
const failingExecutionRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
const failingExecutionRule = resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
failingExecutionRule.executionStatus = {
status: 'error',
lastExecutionDate: failingExecutionRule.executionStatus.lastExecutionDate,
@ -81,7 +85,7 @@ describe.each([
};
// 1. getFailingRules api found a rule where the executionStatus was 'error'
clients.rulesClient.get.mockResolvedValue({
clients.rulesClient.resolve.mockResolvedValue({
...failingExecutionRule,
});

View file

@ -20,7 +20,7 @@ import {
} from './utils';
import { responseMock } from './__mocks__';
import { exampleRuleStatus } from '../signals/__mocks__/es_results';
import { getAlertMock } from './__mocks__/request_responses';
import { resolveAlertMock } from './__mocks__/request_responses';
import { AlertExecutionStatusErrorReasons } from '../../../../../alerting/common';
import { getQueryRuleParams } from '../schemas/rule_schemas.mock';
import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas';
@ -223,12 +223,14 @@ describe.each([
rulesClient = rulesClientMock.create();
});
it('getFailingRules finds no failing rules', async () => {
rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()));
rulesClient.resolve.mockResolvedValue(
resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams())
);
const res = await getFailingRules(['my-fake-id'], rulesClient);
expect(res).toEqual({});
});
it('getFailingRules finds a failing rule', async () => {
const foundRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
const foundRule = resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
foundRule.executionStatus = {
status: 'error',
lastExecutionDate: foundRule.executionStatus.lastExecutionDate,
@ -237,12 +239,12 @@ describe.each([
message: 'oops',
},
};
rulesClient.get.mockResolvedValue(foundRule);
rulesClient.resolve.mockResolvedValue(foundRule);
const res = await getFailingRules([foundRule.id], rulesClient);
expect(res).toEqual({ [foundRule.id]: foundRule });
});
it('getFailingRules throws an error', async () => {
rulesClient.get.mockImplementation(() => {
rulesClient.resolve.mockImplementation(() => {
throw new Error('my test error');
});
let error;

View file

@ -238,7 +238,7 @@ export const getFailingRules = async (
try {
const errorRules = await Promise.all(
ids.map(async (id) =>
rulesClient.get({
rulesClient.resolve({
id,
})
)