Skip to content
This repository has been archived by the owner on Mar 31, 2024. It is now read-only.

Commit

Permalink
[Security Solution][Endpoint] Adds matches wildcard operator for `f…
Browse files Browse the repository at this point in the history
…ile.path.text` field for Event Filters (elastic#125202)

* labels for wildcard path entries

fixes elastic/security-team/issues/2525

* consistent naming and add missing tests

refs elastic/pull/120679

* add autocompletion for wildcard

fixes elastic/security-team/issues/2525

* ensure event filter artifacts have correct wildcard type for process.name entry

fixes elastic/security-team/issues/2525
fixes elastic/security-team/issues/2723

* set warning for input values

fixes elastic/security-team/issues/2525

* lift path validations to packages

fixes elastic/security-team/issues/2525

* Add more tests

fixes elastic/security-team/issues/2525

* Add wildcards to event filter generator

* fix merge i18n check

* Remove not match/excluded operator for now

review changes

* add mixed entries for wildcard

review changes

* comparison typo

refs 06c868b

* fix vulnerable regex

review changes

* ignore empty space on input

review changes

* update component

review changes

* use const enum

review changes

* update type imports to use ConditionEntryField, OperatingSystem, TrustedAppEntryTypes
  • Loading branch information
ashokaditya authored Mar 2, 2022
1 parent 6b63660 commit 9d53810
Show file tree
Hide file tree
Showing 71 changed files with 1,385 additions and 268 deletions.
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"uiActions": "src/plugins/ui_actions",
"uiActionsExamples": "examples/ui_action_examples",
"usageCollection": "src/plugins/usage_collection",
"utils": "packages/kbn-securitysolution-utils/src",
"visDefaultEditor": "src/plugins/vis_default_editor",
"visTypeHeatmap": "src/plugins/vis_types/heatmap",
"visTypeMarkdown": "src/plugins/vis_type_markdown",
Expand Down
22 changes: 21 additions & 1 deletion packages/kbn-securitysolution-autocomplete/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autocomplete Fields

Need an input that shows available index fields? Or an input that autocompletes based on a selected indexPattern field? Bingo! That's what these components are for. They are generalized enough so that they can be reused throughout and repurposed based on your needs.
Need an input that shows available index fields? Or an input that auto-completes based on a selected indexPattern field? Bingo! That's what these components are for. They are generalized enough so that they can be reused throughout and repurposed based on your needs.

All three of the available components rely on Eui's combo box.

Expand Down Expand Up @@ -119,4 +119,24 @@ The `onChange` handler is passed selected `string[]`.
indexPattern={indexPattern}
onChange={handleFieldMatchAnyValueChange}
/>
```

## AutocompleteFieldWildcardComponent

This component can be used to allow users to select a single value. It uses the autocomplete hook to display any autocomplete options based on the passed in `indexPattern`, but also allows a user to add their own value.

The `onChange` handler is passed selected `string[]`.

```js
<AutocompleteFieldWildcardComponent
placeholder={i18n.FIELD_VALUE_PLACEHOLDER}
selectedField={selectedField}
selectedValue={values}
isDisabled={false}
isLoading={isLoading}
isClearable={false}
indexPattern={indexPattern}
onChange={handleFieldMatchAnyValueChange}
warning='input warning'
/>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
* 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.
*/

import React from 'react';
import { ReactWrapper, mount } from 'enzyme';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { act } from '@testing-library/react';
import { AutocompleteFieldWildcardComponent } from '.';
import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete';
import { fields, getField } from '../fields/index.mock';
import { autocompleteStartMock } from '../autocomplete/index.mock';

jest.mock('../hooks/use_field_value_autocomplete');

describe('AutocompleteFieldWildcardComponent', () => {
let wrapper: ReactWrapper;

const getValueSuggestionsMock = jest
.fn()
.mockResolvedValue([false, true, ['value 3', 'value 4'], jest.fn()]);

beforeEach(() => {
(useFieldValueAutocomplete as jest.Mock).mockReturnValue([
false,
true,
['value 1', 'value 2'],
getValueSuggestionsMock,
]);
});

afterEach(() => {
jest.clearAllMocks();
wrapper.unmount();
});

test('it renders row label if one passed in', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled
isLoading={false}
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
rowLabel={'Row Label'}
selectedField={getField('file.path.text')}
selectedValue="/opt/bin/app.dmg"
/>
);

expect(
wrapper.find('[data-test-subj="valuesAutocompleteWildcardLabel"] label').at(0).text()
).toEqual('Row Label');
});

test('it renders disabled if "isDisabled" is true', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled
isLoading={false}
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue="/opt/*/app.dmg"
/>
);

expect(
wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').prop('disabled')
).toBeTruthy();
});

test('it renders loading if "isLoading" is true', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled={false}
isLoading
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue="/opt/*/app.dmg"
/>
);
wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] button').at(0).simulate('click');
expect(
wrapper
.find('EuiComboBoxOptionsList[data-test-subj="valuesAutocompleteWildcard-optionsList"]')
.prop('isLoading')
).toBeTruthy();
});

test('it allows user to clear values if "isClearable" is true', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={true}
isDisabled={false}
isLoading={false}
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue="/opt/*/app.dmg"
/>
);

expect(
wrapper
.find('[data-test-subj="comboBoxInput"]')
.hasClass('euiComboBox__inputWrap-isClearable')
).toBeTruthy();
});

test('it correctly displays selected value', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled={false}
isLoading={false}
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue="/opt/*/app.dmg"
/>
);

expect(
wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] EuiComboBoxPill').at(0).text()
).toEqual('/opt/*/app.dmg');
});

test('it invokes "onChange" when new value created', async () => {
const mockOnChange = jest.fn();
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled={false}
isLoading={false}
onChange={mockOnChange}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue=""
/>
);

(
wrapper.find(EuiComboBox).props() as unknown as {
onCreateOption: (a: string) => void;
}
).onCreateOption('/opt/*/app.dmg');

expect(mockOnChange).toHaveBeenCalledWith('/opt/*/app.dmg');
});

test('it invokes "onChange" when new value selected', async () => {
const mockOnChange = jest.fn();
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled={false}
isLoading={false}
onChange={mockOnChange}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue=""
/>
);

(
wrapper.find(EuiComboBox).props() as unknown as {
onChange: (a: EuiComboBoxOptionOption[]) => void;
}
).onChange([{ label: 'value 1' }]);

expect(mockOnChange).toHaveBeenCalledWith('value 1');
});

test('it refreshes autocomplete with search query when new value searched', () => {
wrapper = mount(
<AutocompleteFieldWildcardComponent
autocompleteService={autocompleteStartMock}
indexPattern={{
fields,
id: '1234',
title: 'logs-endpoint.events.*',
}}
isClearable={false}
isDisabled={false}
isLoading={false}
onChange={jest.fn()}
onError={jest.fn()}
onWarning={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('file.path.text')}
selectedValue=""
/>
);
act(() => {
(
wrapper.find(EuiComboBox).props() as unknown as {
onSearchChange: (a: string) => void;
}
).onSearchChange('A:\\Some Folder\\inc*.exe');
});

expect(useFieldValueAutocomplete).toHaveBeenCalledWith({
autocompleteService: autocompleteStartMock,
fieldValue: '',
indexPattern: {
fields,
id: '1234',
title: 'logs-endpoint.events.*',
},
operatorType: 'wildcard',
query: 'A:\\Some Folder\\inc*.exe',
selectedField: getField('file.path.text'),
});
});
});
Loading

0 comments on commit 9d53810

Please sign in to comment.