diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 03cbe53f60320..9858f2a72c2d0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; -import { ConditionEntry, ConditionEntryField, OperatingSystem, OperatorEntryField } from '../types'; +import { ConditionEntry, ConditionEntryField, OperatingSystem } from '../types'; import { getDuplicateFields, isValidHash } from '../service/trusted_apps/validations'; export const DeleteTrustedAppsRequestSchema = { @@ -29,17 +29,13 @@ export const GetTrustedAppsRequestSchema = { }), }; -const ConditionEntryTypeSchema = schema.literal('match'); -// when field === PATH -> operator in ('included', 'wildcard_caseless') else operator === 'included' -const ConditionEntryOperatorSchema = schema.conditional( +const ConditionEntryTypeSchema = schema.conditional( schema.siblingRef('field'), ConditionEntryField.PATH, - schema.oneOf([ - schema.literal(OperatorEntryField.included), - schema.literal(OperatorEntryField.wildcard_caseless), - ]), - schema.literal(OperatorEntryField.included) + schema.oneOf([schema.literal('match'), schema.literal('wildcard')]), + schema.literal('match') ); +const ConditionEntryOperatorSchema = schema.literal('included' as ConditionEntry['operator']); /* * A generic Entry schema to be used for a specific entry schema depending on the OS diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 371f7848b6bb4..3c854dbb7bd72 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -7,6 +7,8 @@ import { TypeOf } from '@kbn/config-schema'; import { ApplicationStart } from 'kibana/public'; + +import { Entry } from '../../../../lists/common/schemas/types/entries'; import { DeleteTrustedAppsRequestSchema, GetOneTrustedAppRequestSchema, @@ -69,14 +71,15 @@ export enum ConditionEntryField { SIGNER = 'process.Ext.code_signature', } -export enum OperatorEntryField { - included = 'included', - wildcard_caseless = 'wildcard_caseless', +export enum OperatorFieldIds { + is = 'is', + matches = 'matches', } + export interface ConditionEntry { field: T; - type: 'match'; - operator: keyof typeof OperatorEntryField; + type: Entry['type']; + operator: 'included'; value: string; } diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index aaae0d4dc25ef..f02cc472e9f60 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -20,6 +20,7 @@ export { EntryExists, EntryMatch, EntryMatchAny, + EntriesMatchWildcardCaseless, EntryNested, EntryList, EntriesArray, @@ -38,6 +39,7 @@ export { nestedEntryItem, entriesMatch, entriesMatchAny, + entriesMatchWildcardCaseless, entriesExists, entriesList, namespaceType, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts b/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts index c7a125daa54f8..7bce9f0b557b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/types.ts @@ -15,6 +15,7 @@ import { Entry, EntryMatch, EntryMatchAny, + EntriesMatchWildcardCaseless, EntryExists, ExceptionListItemSchema, CreateExceptionListItemSchema, @@ -92,6 +93,7 @@ export interface EmptyNestedEntry { type: OperatorTypeEnum.NESTED; entries: Array< | (EntryMatch & { id?: string }) + | (EntriesMatchWildcardCaseless & { id?: string }) | (EntryMatchAny & { id?: string }) | (EntryExists & { id?: string }) >; @@ -108,6 +110,7 @@ export type BuilderEntryNested = Omit & { id?: string; entries: Array< | (EntryMatch & { id?: string }) + | (EntriesMatchWildcardCaseless & { id?: string }) | (EntryMatchAny & { id?: string }) | (EntryExists & { id?: string }) >; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx index 21a472d80a955..1f3fed2fd4b61 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/condition_entry_input/index.tsx @@ -20,7 +20,7 @@ import { import { ConditionEntry, ConditionEntryField, - OperatorEntryField, + OperatorFieldIds, OperatingSystem, } from '../../../../../../../common/endpoint/types'; @@ -79,10 +79,10 @@ const InputItem = styled.div` vertical-align: baseline; `; -const operatorOptions = (Object.keys(OperatorEntryField) as OperatorEntryField[]).map((value) => ({ +const operatorOptions = (Object.keys(OperatorFieldIds) as OperatorFieldIds[]).map((value) => ({ dropdownDisplay: OPERATOR_TITLES[value], inputDisplay: OPERATOR_TITLES[value], - value, + value: value === 'matches' ? 'wildcard' : 'match', })); export const ConditionEntryInput = memo( @@ -144,7 +144,7 @@ export const ConditionEntryInput = memo( ); const handleOperatorUpdate = useCallback( - (newOperator) => onChange({ ...entry, operator: newOperator }, entry), + (newOperator) => onChange({ ...entry, type: newOperator }, entry), [entry, onChange] ); @@ -174,13 +174,13 @@ export const ConditionEntryInput = memo( ) : ( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx index aadee204dab14..8289792b81f89 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.tsx @@ -45,7 +45,7 @@ const getEntriesColumnDefinitions = (): Array truncateText: true, textOnly: true, width: '30%', - render(field: Entry['field'], entry: Entry) { + render(field: Entry['field'], _entry: Entry) { return CONDITION_FIELD_TITLE[field]; }, }, @@ -55,8 +55,8 @@ const getEntriesColumnDefinitions = (): Array sortable: false, truncateText: true, width: '20%', - render(field: Entry['operator'], entry: Entry) { - return OPERATOR_TITLES[field]; + render(_field: Entry['operator'], entry: Entry) { + return entry.type === 'wildcard' ? OPERATOR_TITLES.matches : OPERATOR_TITLES.is; }, }, { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index 7071022fb893f..f955e7b7dc568 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -11,7 +11,7 @@ import { MacosLinuxConditionEntry, WindowsConditionEntry, ConditionEntryField, - OperatorEntryField, + OperatorFieldIds, } from '../../../../../common/endpoint/types'; export { OS_TITLES } from '../../../common/translations'; @@ -52,19 +52,13 @@ export const CONDITION_FIELD_DESCRIPTION: { [K in ConditionEntryField]: string } ), }; -export const OPERATOR_TITLES: { [K in OperatorEntryField]: string } = { - [OperatorEntryField.included]: i18n.translate( - 'xpack.securitySolution.trustedapps.card.operator.is', - { - defaultMessage: 'is', - } - ), - [OperatorEntryField.wildcard_caseless]: i18n.translate( - 'xpack.securitySolution.trustedapps.card.operator.matches', - { - defaultMessage: 'matches', - } - ), +export const OPERATOR_TITLES: { [K in OperatorFieldIds]: string } = { + is: i18n.translate('xpack.securitySolution.trustedapps.card.operator.is', { + defaultMessage: 'is', + }), + matches: i18n.translate('xpack.securitySolution.trustedapps.card.operator.matches', { + defaultMessage: 'matches', + }), }; export const PROPERTY_TITLES: Readonly< diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts index cae1d9c27eb7f..a0ccef78ea0cf 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts @@ -9,12 +9,13 @@ import uuid from 'uuid'; import { OsType } from '../../../../../lists/common/schemas'; import { + Entry, EntriesArray, EntryMatch, + EntriesMatchWildcardCaseless, EntryNested, ExceptionListItemSchema, NestedEntriesArray, - Operator, } from '../../../../../lists/common'; import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../../lists/common/constants'; import { @@ -27,7 +28,6 @@ import { EffectScope, NewTrustedApp, OperatingSystem, - OperatorEntryField, TrustedApp, UpdateTrustedApp, } from '../../../../common/endpoint/types'; @@ -48,17 +48,18 @@ const OPERATING_SYSTEM_TO_OS_TYPE: Mapping = { }; const POLICY_REFERENCE_PREFIX = 'policy:'; +const OPERATOR_VALUE = 'included'; const filterUndefined = (list: Array): T[] => { return list.filter((item: T | undefined): item is T => item !== undefined); }; -export const createConditionEntry = ( +export const createConditionEntry = ( field: T, - operator: O, + type: Entry['type'], value: string ): ConditionEntry => { - return { field, value, type: 'match', operator }; + return { field, value, type, operator: OPERATOR_VALUE }; }; export const tagsToEffectScope = (tags: string[]): EffectScope => { @@ -76,46 +77,26 @@ export const tagsToEffectScope = (tags: string[]): EffectScope => { } }; -type TrustedAppsEntriesArray = Omit & { - operator: Operator | 'wildcard_caseless'; -}; - -export const entriesToConditionEntriesMap = ( - entries: TrustedAppsEntriesArray -): ConditionEntriesMap => { +export const entriesToConditionEntriesMap = (entries: EntriesArray): ConditionEntriesMap => { return entries.reduce((result, entry) => { if (entry.field.startsWith('process.hash') && entry.type === 'match') { return { ...result, [ConditionEntryField.HASH]: createConditionEntry( ConditionEntryField.HASH, - OperatorEntryField.included, - entry.value - ), - }; - } else if ( - entry.field === 'process.executable.caseless' && - entry.type === 'match' && - entry.operator === OperatorEntryField.wildcard_caseless - ) { - return { - ...result, - [ConditionEntryField.PATH]: createConditionEntry( - ConditionEntryField.PATH, - OperatorEntryField.wildcard_caseless, + entry.type, entry.value ), }; } else if ( entry.field === 'process.executable.caseless' && - entry.type === 'match' && - entry.operator === OperatorEntryField.included + (entry.type === 'match' || entry.type === 'wildcard') ) { return { ...result, [ConditionEntryField.PATH]: createConditionEntry( ConditionEntryField.PATH, - OperatorEntryField.included, + entry.type, entry.value ), }; @@ -129,7 +110,7 @@ export const entriesToConditionEntriesMap = ( ...result, [ConditionEntryField.SIGNER]: createConditionEntry( ConditionEntryField.SIGNER, - OperatorEntryField.included, + entry.type, subjectNameCondition.value ), }; @@ -200,12 +181,15 @@ const hashType = (hash: string): 'md5' | 'sha256' | 'sha1' | undefined => { } }; -export const createEntryMatch = ( +export const createEntryMatch = (field: string, value: string): EntryMatch => { + return { field, value, type: 'match', operator: OPERATOR_VALUE }; +}; + +export const createEntryMatchWildcardCaseless = ( field: string, - operator: OperatorEntryField, value: string -): EntryMatch => { - return { field, value, type: 'match', operator }; +): EntriesMatchWildcardCaseless => { + return { field, value, type: 'wildcard', operator: OPERATOR_VALUE }; }; export const createEntryNested = (field: string, entries: NestedEntriesArray): EntryNested => { @@ -225,29 +209,20 @@ export const conditionEntriesToEntries = (conditionEntries: ConditionEntry[]): E if (conditionEntry.field === ConditionEntryField.HASH) { return createEntryMatch( `process.hash.${hashType(conditionEntry.value)}`, - OperatorEntryField.included, conditionEntry.value.toLowerCase() ); } else if (conditionEntry.field === ConditionEntryField.SIGNER) { return createEntryNested(`process.Ext.code_signature`, [ - createEntryMatch('trusted', OperatorEntryField.included, 'true'), - createEntryMatch('subject_name', OperatorEntryField.included, conditionEntry.value), + createEntryMatch('trusted', 'true'), + createEntryMatch('subject_name', conditionEntry.value), ]); } else if ( conditionEntry.field === ConditionEntryField.PATH && - conditionEntry.operator === OperatorEntryField.wildcard_caseless + conditionEntry.type === 'wildcard' ) { - return createEntryMatch( - `process.executable.caseless`, - OperatorEntryField.wildcard_caseless, - conditionEntry.value - ); + return createEntryMatchWildcardCaseless(`process.executable.caseless`, conditionEntry.value); } else { - return createEntryMatch( - `process.executable.caseless`, - OperatorEntryField.included, - conditionEntry.value - ); + return createEntryMatch(`process.executable.caseless`, conditionEntry.value); } }); };