[8.15] [EDR Workflows] Add warning for duplicated `event.category` for Process Descendant event filter (#187844) (#188071)

# Backport

This will backport the following commits from `main` to `8.15`:
- [[EDR Workflows] Add warning for duplicated `event.category`
for Process Descendant event filter
(#187844)](https://github.com/elastic/kibana/pull/187844)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Gergő
Ábrahám","email":"gergo.abraham@elastic.co"},"sourceCommit":{"committedDate":"2024-07-11T09:40:56Z","message":"[EDR
Workflows] Add warning for duplicated `event.category` for Process
Descendant event filter (#187844)\n\n##
Summary\r\n\r\n\r\n![warning](998b8c57-f852-4983-b545-80c810f21a54)\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a4cd90b6f328ba87a13416fd1266217bafb7d7cd","branchLabelMapping":{"^v8.16.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Defend
Workflows","v8.15.0","v8.16.0"],"title":"[EDR Workflows] Add warning for
duplicated `event.category` for Process Descendant event
filter","number":187844,"url":"https://github.com/elastic/kibana/pull/187844","mergeCommit":{"message":"[EDR
Workflows] Add warning for duplicated `event.category` for Process
Descendant event filter (#187844)\n\n##
Summary\r\n\r\n\r\n![warning](998b8c57-f852-4983-b545-80c810f21a54)\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a4cd90b6f328ba87a13416fd1266217bafb7d7cd"}},"sourceBranch":"main","suggestedTargetBranches":["8.15"],"targetPullRequestStates":[{"branch":"8.15","label":"v8.15.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/187844","number":187844,"mergeCommit":{"message":"[EDR
Workflows] Add warning for duplicated `event.category` for Process
Descendant event filter (#187844)\n\n##
Summary\r\n\r\n\r\n![warning](998b8c57-f852-4983-b545-80c810f21a54)\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Elastic Machine
<elasticmachine@users.noreply.github.com>","sha":"a4cd90b6f328ba87a13416fd1266217bafb7d7cd"}}]}]
BACKPORT-->

---------

Co-authored-by: Gergő Ábrahám <gergo.abraham@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-07-15 13:29:03 +02:00 committed by GitHub
parent 8d6510f30e
commit 98cd57d9c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 299 additions and 80 deletions

View file

@ -546,90 +546,294 @@ describe('Event filter form', () => {
});
describe('Warnings', () => {
beforeEach(() => {
render();
describe('duplicate fields', () => {
it('should not show warning text when unique fields are added', async () => {
formProps.item.entries = [
{
field: 'event.action',
operator: 'included',
type: 'match',
value: 'some value',
},
{
field: 'file.name',
operator: 'excluded',
type: 'match',
value: 'some other value',
},
];
render();
expect(await renderResult.findByDisplayValue('some value')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should not show warning text when field values are not added', async () => {
formProps.item.entries = [
{
field: 'event.action',
operator: 'included',
type: 'match',
value: '',
},
{
field: 'event.action',
operator: 'excluded',
type: 'match',
value: '',
},
];
render();
expect((await renderResult.findAllByTestId('fieldAutocompleteComboBox')).length).toBe(2);
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should show warning text when duplicate fields are added with values', async () => {
formProps.item.entries = [
{
field: 'event.action',
operator: 'included',
type: 'match',
value: 'some value',
},
{
field: 'event.action',
operator: 'excluded',
type: 'match',
value: 'some other value',
},
];
render();
expect(
await renderResult.findByTestId('duplicate-fields-warning-message')
).toBeInTheDocument();
});
describe('in relation with Process Descendant filtering', () => {
it('should not show warning text when event.category is added but feature flag is disabled', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: false,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 1',
},
];
formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG];
render();
expect(await renderResult.findByDisplayValue('some value 1')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should not show warning text when event.category is added but process descendant filter is disabled', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 2',
},
];
formProps.item.tags = [];
render();
expect(await renderResult.findByDisplayValue('some value 2')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should not show warning text when event.category is NOT added and process descendant filter is enabled', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.action',
operator: 'included',
type: 'match',
value: 'some value 3',
},
];
formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG];
render();
expect(await renderResult.findByDisplayValue('some value 3')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should show warning text when event.category is added and process descendant filter is enabled', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 4',
},
];
formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG];
render();
expect(await renderResult.findByDisplayValue('some value 4')).toBeInTheDocument();
expect(
await renderResult.findByTestId('duplicate-fields-warning-message')
).toBeInTheDocument();
});
it('should add warning text when switching to process descendant filtering', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 5',
},
];
formProps.item.tags = [];
render();
expect(await renderResult.findByDisplayValue('some value 5')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
// switch to Process Descendant filtering
userEvent.click(renderResult.getByTestId(`${formPrefix}-filterProcessDescendantsButton`));
rerenderWithLatestProps();
expect(
await renderResult.findByTestId('duplicate-fields-warning-message')
).toBeInTheDocument();
});
it('should remove warning text when switching from process descendant filtering', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 6',
},
];
formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG];
render();
expect(
await renderResult.findByTestId('duplicate-fields-warning-message')
).toBeInTheDocument();
// switch to classic Event filtering
userEvent.click(renderResult.getByTestId(`${formPrefix}-filterEventsButton`));
rerenderWithLatestProps();
expect(await renderResult.findByDisplayValue('some value 6')).toBeInTheDocument();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
it('should remove warning text when removing `event.category`', async () => {
mockedContext.setExperimentalFlag({
filterProcessDescendantsForEventFiltersEnabled: true,
});
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value 6',
},
];
formProps.item.tags = [FILTER_PROCESS_DESCENDANTS_TAG];
render();
expect(
await renderResult.findByTestId('duplicate-fields-warning-message')
).toBeInTheDocument();
// switch to classic Event filtering
userEvent.click(renderResult.getByTestId(`builderItemEntryDeleteButton`));
rerenderWithLatestProps();
expect(
renderResult.queryByTestId('duplicate-fields-warning-message')
).not.toBeInTheDocument();
});
});
});
it('should not show warning text when unique fields are added', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value',
},
{
field: 'file.name',
operator: 'excluded',
type: 'match',
value: 'some other value',
},
];
rerender();
expect(renderResult.queryByTestId('duplicate-fields-warning-message')).toBeNull();
});
describe('wildcard with wrong operator', () => {
it('should not show warning callout when wildcard is used with the "MATCHES" operator', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'wildcard',
value: 'valuewithwildcard*',
},
];
render();
expect(await renderResult.findByDisplayValue('valuewithwildcard*')).toBeInTheDocument();
it('should not show warning text when field values are not added', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: '',
},
{
field: 'event.category',
operator: 'excluded',
type: 'match',
value: '',
},
];
rerender();
expect(renderResult.queryByTestId('duplicate-fields-warning-message')).toBeNull();
});
expect(
renderResult.queryByTestId('wildcardWithWrongOperatorCallout')
).not.toBeInTheDocument();
});
it('should show warning text when duplicate fields are added with values', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'some value',
},
{
field: 'event.category',
operator: 'excluded',
type: 'match',
value: 'some other value',
},
];
rerender();
expect(renderResult.findByTestId('duplicate-fields-warning-message')).not.toBeNull();
});
it('should show warning callout when wildcard is used with the "IS" operator', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'valuewithwildcard*',
},
];
render();
it('should not show warning callout when wildcard is used with the "MATCHES" operator', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'wildcard',
value: 'valuewithwildcard*',
},
];
rerender();
expect(renderResult.queryByTestId('wildcardWithWrongOperatorCallout')).toBeNull();
});
it('should show warning callout when wildcard is used with the "IS" operator', async () => {
formProps.item.entries = [
{
field: 'event.category',
operator: 'included',
type: 'match',
value: 'valuewithwildcard*',
},
];
rerender();
await expect(renderResult.findByTestId('wildcardWithWrongOperatorCallout')).not.toBeNull();
expect(
await renderResult.findByTestId('wildcardWithWrongOperatorCallout')
).toBeInTheDocument();
});
});
});

View file

@ -41,6 +41,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use
import { useGetUpdatedTags } from '../../../../hooks/artifacts';
import {
FILTER_PROCESS_DESCENDANTS_TAG,
PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY,
PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY_TEXT,
} from '../../../../../../common/endpoint/service/artifacts/constants';
import {
@ -545,11 +546,19 @@ export const EventFiltersForm: React.FC<ArtifactFormComponentProps & { allowSele
const hasDuplicates =
(!hasFormChanged && arg.exceptionItems[0] === undefined) ||
isEqual(arg.exceptionItems[0]?.entries, exception?.entries);
if (hasDuplicates) {
const addedFields = arg.exceptionItems[0]?.entries.map((e) => e.field) || [''];
if (isFilterProcessDescendantsFeatureEnabled && isFilterProcessDescendantsSelected) {
addedFields.push(PROCESS_DESCENDANT_EVENT_FILTER_EXTRA_ENTRY.field);
}
setHasDuplicateFields(computeHasDuplicateFields(getAddedFieldsCounts(addedFields)));
if (!hasFormChanged) setHasFormChanged(true);
return;
} else {
setHasDuplicateFields(false);
}
// handle wildcard with wrong operator case
@ -576,7 +585,13 @@ export const EventFiltersForm: React.FC<ArtifactFormComponentProps & { allowSele
processChanged(updatedItem);
if (!hasFormChanged) setHasFormChanged(true);
},
[exception, hasFormChanged, processChanged]
[
exception,
hasFormChanged,
isFilterProcessDescendantsFeatureEnabled,
isFilterProcessDescendantsSelected,
processChanged,
]
);
const exceptionBuilderComponentMemo = useMemo(
() =>