Skip to content

Commit

Permalink
[Detections Response] Rule action filters missing in UI (#175043) (#1…
Browse files Browse the repository at this point in the history
…75050)

## Summary

Addresses #175043

These changes fix the issue where the alerts filter options are not
visible on the actions page while creating/editing SIEM rules.

<img width="960" alt="Screenshot 2024-01-17 at 18 43 17"
src="https://github.com/elastic/kibana/assets/2700761/81395e6d-f39f-4ccd-bdb6-46a5fdb024ea">

**Cause**:
These changes
https://github.com/elastic/kibana/pull/171049/files#diff-c6d0c14f7d10731840e8e8522e21a1d2ee18a29f120a769db98edf8bd93d9b02R431
where we removed the check of `producerId === AlertConsumers.SIEM`

---------

Co-authored-by: Xavier Mouligneau <[email protected]>
(cherry picked from commit 4b18cdc)
  • Loading branch information
e40pud committed Jan 18, 2024
1 parent f98d9d3 commit 7a7d840
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { render, waitFor, screen } from '@testing-library/react';
import { DEFAULT_FREQUENCY } from '../../../common/constants';
import { transformActionVariables } from '../../lib/action_variables';
import { RuleNotifyWhen, RuleNotifyWhenType } from '@kbn/alerting-plugin/common';
import { AlertConsumers } from '@kbn/rule-data-utils';

const CUSTOM_NOTIFY_WHEN_OPTIONS: NotifyWhenSelectOptions[] = [
{
Expand Down Expand Up @@ -217,6 +218,56 @@ describe('action_type_form', () => {
});
});

it('renders the alerts filters with the producerId set to SIEM', async () => {
const actionType = actionTypeRegistryMock.createMockActionTypeModel({
id: '.pagerduty',
iconClass: 'test',
selectMessage: 'test',
validateParams: (): Promise<GenericValidationResult<unknown>> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
},
actionConnectorFields: null,
actionParamsFields: mockedActionParamsFieldsWithExecutionMode,
defaultActionParams: {
dedupKey: 'test',
eventAction: 'resolve',
},
});
actionTypeRegistry.get.mockReturnValue(actionType);

render(
<I18nProvider>
{getActionTypeForm({
index: 1,
actionItem: {
id: '123',
actionTypeId: '.pagerduty',
group: 'recovered',
params: {
eventAction: 'recovered',
dedupKey: undefined,
summary: '2323',
source: 'source',
severity: '1',
timestamp: new Date().toISOString(),
component: 'test',
group: 'group',
class: 'test class',
},
},
producerId: AlertConsumers.SIEM,
featureId: AlertConsumers.SIEM,
})}
</I18nProvider>
);

await waitFor(() => {
expect(screen.getByTestId('alertsFilterQueryToggle')).toBeInTheDocument();
expect(screen.getByTestId('alertsFilterTimeframeToggle')).toBeInTheDocument();
});
});

it('does not call "setActionParamsProperty" because dedupKey is not empty', async () => {
const actionType = actionTypeRegistryMock.createMockActionTypeModel({
id: '.pagerduty',
Expand Down Expand Up @@ -598,6 +649,8 @@ function getActionTypeForm({
notifyWhenSelectOptions,
defaultNotifyWhenValue,
ruleTypeId,
producerId = AlertConsumers.INFRASTRUCTURE,
featureId = AlertConsumers.INFRASTRUCTURE,
}: {
index?: number;
actionConnector?: ActionConnector<Record<string, unknown>, Record<string, unknown>>;
Expand All @@ -617,6 +670,8 @@ function getActionTypeForm({
notifyWhenSelectOptions?: NotifyWhenSelectOptions[];
defaultNotifyWhenValue?: RuleNotifyWhenType;
ruleTypeId?: string;
producerId?: string;
featureId?: string;
}) {
const actionConnectorDefault = {
actionTypeId: '.pagerduty',
Expand Down Expand Up @@ -709,8 +764,8 @@ function getActionTypeForm({
summaryMessageVariables={summaryMessageVariables}
notifyWhenSelectOptions={notifyWhenSelectOptions}
defaultNotifyWhenValue={defaultNotifyWhenValue}
producerId="infrastructure"
featureId="infrastructure"
producerId={producerId}
featureId={featureId}
ruleTypeId={ruleTypeId}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React, { Suspense, useEffect, useState, useCallback, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { ValidFeatureId } from '@kbn/rule-data-utils';
import { ValidFeatureId, AlertConsumers } from '@kbn/rule-data-utils';
import {
EuiFlexGroup,
EuiFlexItem,
Expand Down Expand Up @@ -428,7 +428,7 @@ export const ActionTypeForm = ({
setActionGroupIdByIndex &&
!actionItem.frequency?.summary;

const showActionAlertsFilter = hasFieldsForAAD;
const showActionAlertsFilter = hasFieldsForAAD || producerId === AlertConsumers.SIEM;

const accordionContent = checkEnabledResult.isEnabled ? (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { RULE_NAME_HEADER } from '../../../../screens/rule_details';
import { getIndexConnector } from '../../../../objects/connector';
import { getSimpleCustomQueryRule } from '../../../../objects/rule';

Expand All @@ -23,6 +24,7 @@ import {
fillAboutRuleAndContinue,
fillDefineCustomRuleAndContinue,
fillRuleAction,
fillRuleActionFilters,
fillScheduleRuleAndContinue,
} from '../../../../tasks/create_new_rule';
import { login } from '../../../../tasks/login';
Expand Down Expand Up @@ -74,5 +76,35 @@ describe(
expect(response.body.hits.hits[0]._source).to.deep.equal(expectedJson);
});
});

it('Allows adding alerts filters for the action', function () {
visit(CREATE_RULE_URL);
fillDefineCustomRuleAndContinue(rule);
fillAboutRuleAndContinue(rule);
fillScheduleRuleAndContinue(rule);
fillRuleAction(actions);

// Fill out action filters
const alertsFilter = {
timeframe: {
days: [1, 3],
timezone: 'CET',
hours: {
start: '08:30',
end: '15:30',
},
},
query: {
kql: 'process.name : *',
},
};
fillRuleActionFilters(alertsFilter);

// Create the rule
createAndEnableRule();

// UI redirects to rule creation page of a created rule
cy.get(RULE_NAME_HEADER).should('contain', rule.name);
});
}
);
14 changes: 14 additions & 0 deletions x-pack/test/security_solution_cypress/cypress/objects/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ export interface Actions {
connectors: Connectors[];
}

export interface AlertsFilter {
query: {
kql: string;
};
timeframe: {
days: number[];
timezone: string;
hours: {
start: string;
end: string;
};
};
}

export interface SecurityEvent {
[field: string]: unknown;
'@timestamp': number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,21 @@ export const ACTIONS_SUMMARY_ALERT_BUTTON = '[data-test-subj="actionNotifyWhen-o

export const ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON =
'[data-test-subj="actionNotifyWhen-option-for_each"]';

export const ACTIONS_ALERTS_QUERY_FILTER_BUTTON = '[data-test-subj="alertsFilterQueryToggle"]';

export const ACTIONS_ALERTS_QUERY_FILTER_INPUT = (actionIndex = 0) =>
`[data-test-subj="alertActionAccordion-${actionIndex}"] textarea[data-test-subj="queryInput"]`;

export const ACTIONS_ALERTS_TIMEFRAME_FILTER_BUTTON =
'[data-test-subj="alertsFilterTimeframeToggle"]';

export const ACTIONS_ALERTS_TIMEFRAME_WEEKDAY_BUTTON = (day: number) =>
`[data-test-subj="alertsFilterTimeframeWeekdayButtons"] button[data-test-subj="${day}"]`;

export const ACTIONS_ALERTS_TIMEFRAME_START_INPUT = '.euiDatePicker.euiDatePickerRange__start';

export const ACTIONS_ALERTS_TIMEFRAME_END_INPUT = '.euiDatePicker.euiDatePickerRange__end';

export const ACTIONS_ALERTS_TIMEFRAME_TIMEZONE_INPUT =
'[data-test-subj="alertsFilterTimeframeTimezone"] [data-test-subj="comboBoxSearchInput"]';
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type {
ThreatMatchRuleCreateProps,
ThresholdRuleCreateProps,
} from '@kbn/security-solution-plugin/common/api/detection_engine/model';
import type { Actions } from '../objects/types';
import type { Actions, AlertsFilter } from '../objects/types';
// For some reason importing these functions from ../../public/detections/pages/detection_engine/rules/helpers
// causes a "Webpack Compilation Error" in this file specifically, even though it imports fine in the test files
// in ../e2e/*, so we have a copy of the implementations in the cypress helpers.
Expand Down Expand Up @@ -125,6 +125,13 @@ import {
INDEX_SELECTOR,
CREATE_ACTION_CONNECTOR_BTN,
EMAIL_ACTION_BTN,
ACTIONS_ALERTS_QUERY_FILTER_BUTTON,
ACTIONS_ALERTS_TIMEFRAME_FILTER_BUTTON,
ACTIONS_ALERTS_QUERY_FILTER_INPUT,
ACTIONS_ALERTS_TIMEFRAME_WEEKDAY_BUTTON,
ACTIONS_ALERTS_TIMEFRAME_START_INPUT,
ACTIONS_ALERTS_TIMEFRAME_END_INPUT,
ACTIONS_ALERTS_TIMEFRAME_TIMEZONE_INPUT,
} from '../screens/common/rule_actions';
import { fillIndexConnectorForm, fillEmailConnectorForm } from './common/rule_actions';
import { TOAST_ERROR } from '../screens/shared';
Expand Down Expand Up @@ -464,6 +471,25 @@ export const fillRuleAction = (actions: Actions) => {
});
};

export const fillRuleActionFilters = (alertsFilter: AlertsFilter) => {
cy.get(ACTIONS_ALERTS_QUERY_FILTER_BUTTON).click();
cy.get(ACTIONS_ALERTS_QUERY_FILTER_INPUT()).type(alertsFilter.query.kql);

cy.get(ACTIONS_ALERTS_TIMEFRAME_FILTER_BUTTON).click();
alertsFilter.timeframe.days.forEach((day) =>
cy.get(ACTIONS_ALERTS_TIMEFRAME_WEEKDAY_BUTTON(day)).click()
);
cy.get(ACTIONS_ALERTS_TIMEFRAME_START_INPUT)
.first()
.type(`{selectall}${alertsFilter.timeframe.hours.start}{enter}`);
cy.get(ACTIONS_ALERTS_TIMEFRAME_END_INPUT)
.first()
.type(`{selectall}${alertsFilter.timeframe.hours.end}{enter}`);
cy.get(ACTIONS_ALERTS_TIMEFRAME_TIMEZONE_INPUT)
.first()
.type(`{selectall}${alertsFilter.timeframe.timezone}{enter}`);
};

export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRuleCreateProps) => {
const thresholdField = 0;
const threshold = 1;
Expand Down

0 comments on commit 7a7d840

Please sign in to comment.