mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution][Admin][Policy][Event Filters] Update event filters creation to include more match options (#170495)
## Summary - [x] Adds a `matches` and `does not match` operator option to all eligible event filter creation entry fields that support matches - [x] Updates the existing warning to only appear if that is the `file.path.text` entry field is selected - [x] Generalize the warning for wildcard usage if operator is matches and a wildcard is used in the value - [x] Updates wildcard warning tooltip to include "Creating event filters with both `matches` and `does not match` operators may significantly decrease performance." - [x] Unit tests # Screenshots  Warning about wildcards affecting Endpoint performance <img width="1219" alt="image" src="5bceec10
-6387-44d5-bc7f-76de1816ce46"> # Event Filter & Artifact **LINUX**  <details open> <summary> linux artifact entry </summary> <p> ``` { "entries": [ { "type": "simple", "entries": [ { "field": "event.category", "operator": "included", "type": "wildcard_cased", "value": "network" }, { "field": "process.name", "operator": "included", "type": "exact_cased", "value": "network" } ] } ] } ``` </p> </details open> **WINDOWS**  <details open> <summary> windows artifact entry </summary> <p> ``` { "entries": [ { "type": "simple", "entries": [ { "field": "event.kind", "operator": "included", "type": "wildcard_cased", "value": "event" }, { "field": "process.name", "operator": "included", "type": "exact_caseless", "value": "event" }, { "field": "event.category", "operator": "included", "type": "wildcard_cased", "value": "authentication" }, { "field": "process.name", "operator": "included", "type": "exact_caseless", "value": "authentication" } ] } ] } ``` </p> </details open> **MAC**  <details open> <summary> mac artifact entry</summary> <p> ``` { "entries": [ { "type": "simple", "entries": [ { "field": "event.id", "operator": "included", "type": "wildcard_cased", "value": "071e1cfc-8333-4c6c-965a-00678c7b1d61" }, { "field": "process.name", "operator": "included", "type": "exact_caseless", "value": "071e1cfc-8333-4c6c-965a-00678c7b1d61" }, { "field": "file.path", "operator": "included", "type": "wildcard_cased", "value": "C:\\My Documents\\business\\January\\processName" }, { "field": "process.name", "operator": "included", "type": "exact_caseless", "value": "C:\\My Documents\\business\\January\\processName" } ] } ] } ``` </p> </details open>
This commit is contained in:
parent
7f61770f44
commit
69b2cd2b38
13 changed files with 106 additions and 41 deletions
|
@ -14,7 +14,7 @@ import { AutocompleteFieldWildcardComponent } from '.';
|
|||
import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete';
|
||||
import { fields, getField } from '../fields/index.mock';
|
||||
import { autocompleteStartMock } from '../autocomplete/index.mock';
|
||||
import { FILENAME_WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils';
|
||||
import { WILDCARD_WARNING, FILEPATH_WARNING } from '@kbn/securitysolution-utils';
|
||||
|
||||
jest.mock('../hooks/use_field_value_autocomplete');
|
||||
jest.mock('../translations', () => ({
|
||||
|
@ -368,7 +368,7 @@ describe('AutocompleteFieldWildcardComponent', () => {
|
|||
placeholder="Placeholder text"
|
||||
selectedField={getField('file.path.text')}
|
||||
selectedValue="invalid path"
|
||||
warning={FILENAME_WILDCARD_WARNING}
|
||||
warning={WILDCARD_WARNING}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -384,7 +384,7 @@ describe('AutocompleteFieldWildcardComponent', () => {
|
|||
const helpText = wrapper
|
||||
.find('[data-test-subj="valuesAutocompleteWildcardLabel"] div.euiFormHelpText')
|
||||
.at(0);
|
||||
expect(helpText.text()).toEqual(FILENAME_WILDCARD_WARNING);
|
||||
expect(helpText.text()).toEqual(WILDCARD_WARNING);
|
||||
expect(helpText.find('.euiToolTipAnchor')).toBeTruthy();
|
||||
});
|
||||
test('should show the warning helper text if the new value contains spaces when change', async () => {
|
||||
|
@ -412,7 +412,7 @@ describe('AutocompleteFieldWildcardComponent', () => {
|
|||
placeholder="Placeholder text"
|
||||
selectedField={getField('file.path.text')}
|
||||
selectedValue="invalid path"
|
||||
warning={FILENAME_WILDCARD_WARNING}
|
||||
warning={WILDCARD_WARNING}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -223,6 +223,7 @@ describe('operator', () => {
|
|||
{ label: 'is one of' },
|
||||
{ label: 'is not one of' },
|
||||
{ label: 'matches' },
|
||||
{ label: 'does not match' },
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -109,6 +109,7 @@ export const EVENT_FILTERS_OPERATORS: OperatorOption[] = [
|
|||
isOneOfOperator,
|
||||
isNotOneOfOperator,
|
||||
matchesOperator,
|
||||
doesNotMatchOperator,
|
||||
];
|
||||
|
||||
/*
|
||||
|
|
|
@ -11,11 +11,43 @@ import {
|
|||
hasSimpleExecutableName,
|
||||
OperatingSystem,
|
||||
ConditionEntryField,
|
||||
validatePotentialWildcardInput,
|
||||
validateFilePathInput,
|
||||
FILENAME_WILDCARD_WARNING,
|
||||
validateWildcardInput,
|
||||
WILDCARD_WARNING,
|
||||
FILEPATH_WARNING,
|
||||
} from '.';
|
||||
|
||||
describe('validatePotentialWildcardInput', () => {
|
||||
it('warns on wildcard when field is file.path.text', () => {
|
||||
expect(
|
||||
validatePotentialWildcardInput({
|
||||
field: 'file.path.text',
|
||||
os: OperatingSystem.WINDOWS,
|
||||
value: 'c:\\path*.exe',
|
||||
})
|
||||
).toEqual(WILDCARD_WARNING);
|
||||
});
|
||||
it('warns on wildcard when field is not file.path.text', () => {
|
||||
expect(
|
||||
validatePotentialWildcardInput({
|
||||
field: 'event.category',
|
||||
os: OperatingSystem.WINDOWS,
|
||||
value: 'some*value',
|
||||
})
|
||||
).toEqual(WILDCARD_WARNING);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateWildcardInput', () => {
|
||||
it('warns on wildcard for fields that are not file paths', () => {
|
||||
expect(validateWildcardInput('*')).toEqual(WILDCARD_WARNING);
|
||||
});
|
||||
it('does not warn if no wildcard', () => {
|
||||
expect(validateWildcardInput('non-wildcard')).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateFilePathInput', () => {
|
||||
describe('windows', () => {
|
||||
const os = OperatingSystem.WINDOWS;
|
||||
|
@ -36,15 +68,13 @@ describe('validateFilePathInput', () => {
|
|||
});
|
||||
|
||||
it('warns on wildcard in file name at the end of the path', () => {
|
||||
expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual(
|
||||
FILENAME_WILDCARD_WARNING
|
||||
);
|
||||
expect(validateFilePathInput({ os, value: 'c:\\path*.exe' })).toEqual(WILDCARD_WARNING);
|
||||
expect(
|
||||
validateFilePathInput({
|
||||
os,
|
||||
value: 'C:\\Windows\\*\\FILENAME.EXE-*.gz',
|
||||
})
|
||||
).toEqual(FILENAME_WILDCARD_WARNING);
|
||||
).toEqual(WILDCARD_WARNING);
|
||||
});
|
||||
|
||||
it('warns on unix paths or non-windows paths', () => {
|
||||
|
@ -65,20 +95,23 @@ describe('validateFilePathInput', () => {
|
|||
: OperatingSystem.LINUX;
|
||||
|
||||
it('does not warn on valid filenames', () => {
|
||||
expect(validateFilePathInput({ os, value: '/opt/*/FILENAME.EXE-1231205124.gz' })).not.toEqual(
|
||||
FILENAME_WILDCARD_WARNING
|
||||
);
|
||||
expect(
|
||||
validateFilePathInput({
|
||||
os,
|
||||
value: '/opt/*/FILENAME.EXE-1231205124.gz',
|
||||
})
|
||||
).not.toEqual(WILDCARD_WARNING);
|
||||
expect(
|
||||
validateFilePathInput({
|
||||
os,
|
||||
value: "/opt/*/test$ as2@13---12!@#A,DS.#$^&$!#~ 'as'd.华语.txt",
|
||||
})
|
||||
).not.toEqual(FILENAME_WILDCARD_WARNING);
|
||||
).not.toEqual(WILDCARD_WARNING);
|
||||
});
|
||||
it('warns on wildcard in file name at the end of the path', () => {
|
||||
expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(FILENAME_WILDCARD_WARNING);
|
||||
expect(validateFilePathInput({ os, value: '/opt/bin*' })).toEqual(WILDCARD_WARNING);
|
||||
expect(validateFilePathInput({ os, value: '/opt/FILENAME.EXE-*.gz' })).toEqual(
|
||||
FILENAME_WILDCARD_WARNING
|
||||
WILDCARD_WARNING
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const FILENAME_WILDCARD_WARNING = i18n.translate('utils.filename.wildcardWarning', {
|
||||
defaultMessage: `Using wildcards in file paths can impact Endpoint performance`,
|
||||
export const WILDCARD_WARNING = i18n.translate('utils.wildcardWarning', {
|
||||
defaultMessage: `Using wildcards can impact Endpoint performance`,
|
||||
});
|
||||
|
||||
export const FILEPATH_WARNING = i18n.translate('utils.filename.pathWarning', {
|
||||
|
@ -52,39 +52,60 @@ export enum OperatingSystem {
|
|||
export type EntryTypes = 'match' | 'wildcard' | 'match_any';
|
||||
export type TrustedAppEntryTypes = Extract<EntryTypes, 'match' | 'wildcard'>;
|
||||
|
||||
export const validateFilePathInput = ({
|
||||
export const validatePotentialWildcardInput = ({
|
||||
field = '',
|
||||
os,
|
||||
value = '',
|
||||
}: {
|
||||
field?: string;
|
||||
os: OperatingSystem;
|
||||
value?: string;
|
||||
}): string | undefined => {
|
||||
const textInput = value.trim();
|
||||
if (field === 'file.path.text') {
|
||||
return validateFilePathInput({ os, value: textInput });
|
||||
}
|
||||
return validateWildcardInput(textInput);
|
||||
};
|
||||
|
||||
export const validateFilePathInput = ({
|
||||
os,
|
||||
value,
|
||||
}: {
|
||||
os: OperatingSystem;
|
||||
value: string;
|
||||
}): string | undefined => {
|
||||
const isValidFilePath = isPathValid({
|
||||
os,
|
||||
field: 'file.path.text',
|
||||
type: 'wildcard',
|
||||
value: textInput,
|
||||
value,
|
||||
});
|
||||
const hasSimpleFileName = hasSimpleExecutableName({
|
||||
os,
|
||||
type: 'wildcard',
|
||||
value: textInput,
|
||||
value,
|
||||
});
|
||||
|
||||
if (!textInput.length) {
|
||||
if (!value.length) {
|
||||
return FILEPATH_WARNING;
|
||||
}
|
||||
|
||||
if (isValidFilePath) {
|
||||
if (hasSimpleFileName !== undefined && !hasSimpleFileName) {
|
||||
return FILENAME_WILDCARD_WARNING;
|
||||
return WILDCARD_WARNING;
|
||||
}
|
||||
} else {
|
||||
return FILEPATH_WARNING;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateWildcardInput = (value?: string): string | undefined => {
|
||||
if (/[*?]/.test(value ?? '')) {
|
||||
return WILDCARD_WARNING;
|
||||
}
|
||||
};
|
||||
|
||||
export const hasSimpleExecutableName = ({
|
||||
os,
|
||||
type,
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
isOperator,
|
||||
matchesOperator,
|
||||
} from '@kbn/securitysolution-list-utils';
|
||||
import { validateFilePathInput } from '@kbn/securitysolution-utils';
|
||||
import { validatePotentialWildcardInput } from '@kbn/securitysolution-utils';
|
||||
import { useFindListsBySize } from '@kbn/securitysolution-list-hooks';
|
||||
import type { FieldSpec } from '@kbn/data-plugin/common';
|
||||
import { fields, getField } from '@kbn/data-plugin/common/mocks';
|
||||
|
@ -1050,7 +1050,7 @@ describe('BuilderEntryItem', () => {
|
|||
test('it invokes "setWarningsExist" when invalid value in field value input', async () => {
|
||||
const mockSetWarningsExists = jest.fn();
|
||||
|
||||
(validateFilePathInput as jest.Mock).mockReturnValue('some warning message');
|
||||
(validatePotentialWildcardInput as jest.Mock).mockReturnValue('some warning message');
|
||||
wrapper = mount(
|
||||
<BuilderEntryItem
|
||||
autocompleteService={autocompleteStartMock}
|
||||
|
@ -1099,7 +1099,7 @@ describe('BuilderEntryItem', () => {
|
|||
test('it does not invoke "setWarningsExist" when valid value in field value input', async () => {
|
||||
const mockSetWarningsExists = jest.fn();
|
||||
|
||||
(validateFilePathInput as jest.Mock).mockReturnValue(undefined);
|
||||
(validatePotentialWildcardInput as jest.Mock).mockReturnValue(undefined);
|
||||
wrapper = mount(
|
||||
<BuilderEntryItem
|
||||
autocompleteService={autocompleteStartMock}
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
EXCEPTION_OPERATORS_ONLY_LISTS,
|
||||
FormattedBuilderEntry,
|
||||
OperatorOption,
|
||||
fieldSupportsMatches,
|
||||
getEntryOnFieldChange,
|
||||
getEntryOnListChange,
|
||||
getEntryOnMatchAnyChange,
|
||||
|
@ -50,9 +51,9 @@ import {
|
|||
OperatorComponent,
|
||||
} from '@kbn/securitysolution-autocomplete';
|
||||
import {
|
||||
FILENAME_WILDCARD_WARNING,
|
||||
OperatingSystem,
|
||||
validateFilePathInput,
|
||||
WILDCARD_WARNING,
|
||||
validatePotentialWildcardInput,
|
||||
} from '@kbn/securitysolution-utils';
|
||||
import { DataViewBase, DataViewFieldBase } from '@kbn/es-query';
|
||||
import type { AutocompleteStart } from '@kbn/unified-search-plugin/public';
|
||||
|
@ -310,11 +311,11 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
|
|||
|
||||
const renderOperatorInput = (isFirst: boolean): JSX.Element => {
|
||||
// for event filters forms
|
||||
// show extra operators for wildcards when field is `file.path.text`
|
||||
const isFilePathTextField = entry.field !== undefined && entry.field.name === 'file.path.text';
|
||||
// show extra operators for wildcards when field supports matches
|
||||
const doesFieldSupportMatches = entry.field !== undefined && fieldSupportsMatches(entry.field);
|
||||
const isEventFilterList = listType === 'endpoint_events';
|
||||
const augmentedOperatorsList =
|
||||
operatorsList && isFilePathTextField && isEventFilterList
|
||||
operatorsList && doesFieldSupportMatches && isEventFilterList
|
||||
? operatorsList
|
||||
: operatorsList?.filter((operator) => operator.type !== OperatorTypeEnum.WILDCARD);
|
||||
|
||||
|
@ -358,8 +359,8 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
// show this when wildcard filename with matches operator
|
||||
const getWildcardWarning = (precedingWarning: string): React.ReactNode => {
|
||||
// show this when wildcard with matches operator
|
||||
const getWildcardWarningInfo = (precedingWarning: string): React.ReactNode => {
|
||||
return (
|
||||
<p>
|
||||
{precedingWarning}{' '}
|
||||
|
@ -368,7 +369,7 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
|
|||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.lists.exceptions.builder.exceptionMatchesOperator.warningMessage.wildcardInFilepath"
|
||||
defaultMessage="To make a more efficient event filter, use multiple conditions and make them as specific as possible when using wildcards in the path values. For instance, adding a process.name or file.name field."
|
||||
defaultMessage="To make a more efficient event filter, use multiple conditions and make them as specific as possible when using wildcards in the values. For instance, adding a process.name or file.name field. Creating event filters with both `matches` and `does not match` operators may significantly decrease performance."
|
||||
/>
|
||||
}
|
||||
size="m"
|
||||
|
@ -430,11 +431,13 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
|
|||
if (osTypes) {
|
||||
[os] = osTypes as OperatingSystem[];
|
||||
}
|
||||
const warning = validateFilePathInput({ os, value: wildcardValue });
|
||||
const warning = validatePotentialWildcardInput({
|
||||
field: entry.field?.name,
|
||||
os,
|
||||
value: wildcardValue,
|
||||
});
|
||||
actualWarning =
|
||||
warning === FILENAME_WILDCARD_WARNING
|
||||
? warning && getWildcardWarning(warning)
|
||||
: warning;
|
||||
warning === WILDCARD_WARNING ? warning && getWildcardWarningInfo(warning) : warning;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -212,7 +212,7 @@ describe('Exception builder helpers', () => {
|
|||
expect(output).toEqual(expected);
|
||||
});
|
||||
|
||||
test('it returns all fields unfiletered if "item.nested" is not "child" or "parent"', () => {
|
||||
test('it returns all fields unfiltered if "item.nested" is not "child" or "parent"', () => {
|
||||
const payloadIndexPattern = getMockIndexPattern();
|
||||
const payloadItem: FormattedBuilderEntry = getMockBuilderEntry();
|
||||
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem);
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
OS_WINDOWS,
|
||||
CONDITION_AND,
|
||||
CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES,
|
||||
CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH,
|
||||
CONDITION_OPERATOR_TYPE_NESTED,
|
||||
CONDITION_OPERATOR_TYPE_MATCH,
|
||||
CONDITION_OPERATOR_TYPE_MATCH_ANY,
|
||||
|
@ -47,6 +48,7 @@ const OPERATOR_TYPE_LABELS_INCLUDED = Object.freeze({
|
|||
const OPERATOR_TYPE_LABELS_EXCLUDED = Object.freeze({
|
||||
[ListOperatorTypeEnum.MATCH_ANY]: CONDITION_OPERATOR_TYPE_NOT_MATCH_ANY,
|
||||
[ListOperatorTypeEnum.MATCH]: CONDITION_OPERATOR_TYPE_NOT_MATCH,
|
||||
[ListOperatorTypeEnum.WILDCARD]: CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH,
|
||||
});
|
||||
|
||||
const EuiFlexGroupNested = styled(EuiFlexGroup)`
|
||||
|
|
|
@ -68,6 +68,13 @@ export const CONDITION_OPERATOR_TYPE_WILDCARD_MATCHES = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const CONDITION_OPERATOR_TYPE_DOES_NOT_MATCH = i18n.translate(
|
||||
'xpack.securitySolution.artifactCard.conditions.wildcardDoesNotMatchOperator',
|
||||
{
|
||||
defaultMessage: 'DOES NOT MATCH',
|
||||
}
|
||||
);
|
||||
|
||||
export const CONDITION_OPERATOR_TYPE_NESTED = i18n.translate(
|
||||
'xpack.securitySolution.artifactCard.conditions.nestedOperator',
|
||||
{
|
||||
|
|
|
@ -43451,7 +43451,6 @@
|
|||
"unifiedDocViewer.sourceViewer.errorMessageTitle": "Une erreur s'est produite.",
|
||||
"unifiedDocViewer.sourceViewer.refresh": "Actualiser",
|
||||
"utils.filename.pathWarning": "Le chemin est peut-être incorrectement formé ; vérifiez la valeur",
|
||||
"utils.filename.wildcardWarning": "l'utilisation de caractères génériques dans les chemins de fichier peut affecter les performances du point de terminaison",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "Active la bibliothèque de graphiques héritée pour les graphiques de jauge dans Visualize.",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "Bibliothèque de graphiques héritée pour les jauges",
|
||||
"visTypeGauge.controls.gaugeOptions.alignmentLabel": "Alignement",
|
||||
|
|
|
@ -43441,7 +43441,6 @@
|
|||
"unifiedDocViewer.sourceViewer.errorMessageTitle": "エラーが発生しました",
|
||||
"unifiedDocViewer.sourceViewer.refresh": "更新",
|
||||
"utils.filename.pathWarning": "パスの形式が正しくない可能性があります。値を検証してください",
|
||||
"utils.filename.wildcardWarning": "ファイルパスでワイルドカードを使用すると、エンドポイントのパフォーマンスに影響する可能性があります",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "Visualizeでゲージグラフのレガシーグラフライブラリを有効にします。",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "ゲージグラフのレガシーグラフライブラリ",
|
||||
"visTypeGauge.controls.gaugeOptions.alignmentLabel": "アラインメント",
|
||||
|
|
|
@ -43435,7 +43435,6 @@
|
|||
"unifiedDocViewer.sourceViewer.errorMessageTitle": "发生错误",
|
||||
"unifiedDocViewer.sourceViewer.refresh": "刷新",
|
||||
"utils.filename.pathWarning": "路径的格式可能不正确;请验证值",
|
||||
"utils.filename.wildcardWarning": "在文件路径中使用通配符可能会影响终端性能",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.description": "在 Visualize 中启用仪表盘图表的旧版图表库。",
|
||||
"visTypeGauge.advancedSettings.visualization.legacyGaugeChartsLibrary.name": "仪表盘旧版图表库",
|
||||
"visTypeGauge.controls.gaugeOptions.alignmentLabel": "对齐方式",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue