diff --git a/packages/kbn-securitysolution-exception-list-components/index.ts b/packages/kbn-securitysolution-exception-list-components/index.ts index 5811612d8a3a4..0e11a4694384d 100644 --- a/packages/kbn-securitysolution-exception-list-components/index.ts +++ b/packages/kbn-securitysolution-exception-list-components/index.ts @@ -19,3 +19,4 @@ export * from './src/list_header'; export * from './src/header_menu'; export * from './src/generate_linked_rules_menu_item'; export * from './src/wildcard_with_wrong_operator_callout'; +export * from './src/partial_code_signature_callout'; diff --git a/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx b/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx new file mode 100644 index 0000000000000..72836fc78fb55 --- /dev/null +++ b/packages/kbn-securitysolution-exception-list-components/src/partial_code_signature_callout/index.tsx @@ -0,0 +1,34 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { EuiCallOut } from '@elastic/eui'; + +export const PartialCodeSignatureCallout = () => { + return ( + + + + ); +}; diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts index 087b27f136591..fffcabbf41575 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.test.ts @@ -7,7 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getMappingConflictsInfo, fieldSupportsMatches, hasWrongOperatorWithWildcard } from '.'; +import { + getMappingConflictsInfo, + fieldSupportsMatches, + hasWrongOperatorWithWildcard, + hasPartialCodeSignatureEntry, +} from '.'; describe('Helpers', () => { describe('getMappingConflictsInfo', () => { @@ -261,4 +266,112 @@ describe('Helpers', () => { ).toBeTruthy(); }); }); + + describe('hasPartialCodeSignatureEntry', () => { + it('returns false if the entry has neither code signature subject name nor trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [{ type: 'match', value: 'asdf', field: 'someField', operator: 'excluded' }], + }, + ]) + ).toBeFalsy(); + }); + it('returns true if the entry has code signature subject name but not trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.subject_name', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeTruthy(); + }); + it('returns true if the entry has code signature trusted but not the subject name field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeTruthy(); + }); + it('returns false if the entry has both code signature subject name and trusted field', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['windows'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.subject_name', + operator: 'excluded', + }, + { + type: 'match', + value: 'true', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeFalsy(); + }); + it('returns false if the entry has both code signature team_id and trusted fields for mac os', () => { + expect( + hasPartialCodeSignatureEntry([ + { + description: '', + name: '', + type: 'simple', + os_types: ['macos'], + entries: [ + { + type: 'match', + value: 'asdf', + field: 'process.code_signature.team_id', + operator: 'excluded', + }, + { + type: 'match', + value: 'true', + field: 'process.code_signature.trusted', + operator: 'excluded', + }, + ], + }, + ]) + ).toBeFalsy(); + }); + }); }); diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts index a625d694961ab..8031cfba14f2d 100644 --- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts +++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts @@ -1052,3 +1052,38 @@ export const hasWrongOperatorWithWildcard = ( } }); }; + +/** + * Event filters helper where given an exceptions list, + * determine if both 'subject_name' and 'trusted' are + * included in an entry with 'code_signature' + */ +export const hasPartialCodeSignatureEntry = ( + items: ExceptionsBuilderReturnExceptionItem[] +): boolean => { + const { os_types: os = ['windows'], entries = [] } = items[0] || {}; + let name = false; + let trusted = false; + + for (const e of entries) { + if (e.type === 'nested' && e.field === 'process.Ext.code_signature') { + const includesNestedName = e.entries.some( + (nestedEntry) => nestedEntry.field === 'subject_name' + ); + const includesNestedTrusted = e.entries.some( + (nestedEntry) => nestedEntry.field === 'trusted' + ); + if (includesNestedName !== includesNestedTrusted) { + return true; + } + } else if ( + e.field === 'process.code_signature.subject_name' || + (os.includes('macos') && e.field === 'process.code_signature.team_id') + ) { + name = true; + } else if (e.field === 'process.code_signature.trusted') { + trusted = true; + } + } + return name !== trusted; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx index 173645eae203e..783fda9a806b2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.tsx @@ -30,8 +30,12 @@ import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-t import { EVENT_FILTERS_OPERATORS, hasWrongOperatorWithWildcard, + hasPartialCodeSignatureEntry, } from '@kbn/securitysolution-list-utils'; -import { WildCardWithWrongOperatorCallout } from '@kbn/securitysolution-exception-list-components'; +import { + WildCardWithWrongOperatorCallout, + PartialCodeSignatureCallout, +} from '@kbn/securitysolution-exception-list-components'; import { OperatingSystem } from '@kbn/securitysolution-utils'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; @@ -171,6 +175,9 @@ export const EventFiltersForm: React.FC(false); + // This value has to be memoized to avoid infinite useEffect loop on useFetchIndex const indexNames = useMemo(() => [eventsIndexPattern], []); const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex( @@ -563,6 +570,7 @@ export const EventFiltersForm: React.FC = arg.exceptionItems[0] !== undefined @@ -725,6 +733,7 @@ export const EventFiltersForm: React.FC {criteriaSection} {hasWildcardWithWrongOperator && } + {hasPartialCodeSignatureWarning && } {hasDuplicateFields && ( <>