mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
use the new entry type
refs elastic/security-team/issues/543 refs https://github.com/elastic/kibana/pull/97623#pullrequestreview-642618462
This commit is contained in:
parent
f9cb7eddda
commit
b3f5dc4553
8 changed files with 58 additions and 85 deletions
|
@ -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
|
||||
|
|
|
@ -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<T extends ConditionEntryField = ConditionEntryField> {
|
||||
field: T;
|
||||
type: 'match';
|
||||
operator: keyof typeof OperatorEntryField;
|
||||
type: Entry['type'];
|
||||
operator: 'included';
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ export {
|
|||
EntryExists,
|
||||
EntryMatch,
|
||||
EntryMatchAny,
|
||||
EntriesMatchWildcardCaseless,
|
||||
EntryNested,
|
||||
EntryList,
|
||||
EntriesArray,
|
||||
|
@ -38,6 +39,7 @@ export {
|
|||
nestedEntryItem,
|
||||
entriesMatch,
|
||||
entriesMatchAny,
|
||||
entriesMatchWildcardCaseless,
|
||||
entriesExists,
|
||||
entriesList,
|
||||
namespaceType,
|
||||
|
|
|
@ -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<EntryNested, 'entries'> & {
|
|||
id?: string;
|
||||
entries: Array<
|
||||
| (EntryMatch & { id?: string })
|
||||
| (EntriesMatchWildcardCaseless & { id?: string })
|
||||
| (EntryMatchAny & { id?: string })
|
||||
| (EntryExists & { id?: string })
|
||||
>;
|
||||
|
|
|
@ -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<ConditionEntryInputProps>(
|
||||
|
@ -144,7 +144,7 @@ export const ConditionEntryInput = memo<ConditionEntryInputProps>(
|
|||
);
|
||||
|
||||
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<ConditionEntryInputProps>(
|
|||
<EuiSuperSelect
|
||||
options={operatorOptions}
|
||||
onChange={handleOperatorUpdate}
|
||||
valueOfSelected={entry.operator}
|
||||
valueOfSelected={entry.type}
|
||||
data-test-subj={getTestId('operator')}
|
||||
/>
|
||||
) : (
|
||||
<EuiFieldText
|
||||
name="operator"
|
||||
value={OPERATOR_TITLES.included}
|
||||
value={OPERATOR_TITLES.is}
|
||||
data-test-subj={getTestId('operator')}
|
||||
readOnly
|
||||
/>
|
||||
|
|
|
@ -45,7 +45,7 @@ const getEntriesColumnDefinitions = (): Array<EuiTableFieldDataColumnType<Entry>
|
|||
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<EuiTableFieldDataColumnType<Entry>
|
|||
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;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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<OperatingSystem, OsType> = {
|
|||
};
|
||||
|
||||
const POLICY_REFERENCE_PREFIX = 'policy:';
|
||||
const OPERATOR_VALUE = 'included';
|
||||
|
||||
const filterUndefined = <T>(list: Array<T | undefined>): T[] => {
|
||||
return list.filter((item: T | undefined): item is T => item !== undefined);
|
||||
};
|
||||
|
||||
export const createConditionEntry = <T extends ConditionEntryField, O extends OperatorEntryField>(
|
||||
export const createConditionEntry = <T extends ConditionEntryField>(
|
||||
field: T,
|
||||
operator: O,
|
||||
type: Entry['type'],
|
||||
value: string
|
||||
): ConditionEntry<T> => {
|
||||
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<EntriesArray, 'operator'> & {
|
||||
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.type,
|
||||
entry.value
|
||||
),
|
||||
};
|
||||
} else if (
|
||||
entry.field === 'process.executable.caseless' &&
|
||||
entry.type === 'match' &&
|
||||
entry.operator === OperatorEntryField.wildcard_caseless
|
||||
(entry.type === 'match' || entry.type === 'wildcard')
|
||||
) {
|
||||
return {
|
||||
...result,
|
||||
[ConditionEntryField.PATH]: createConditionEntry(
|
||||
ConditionEntryField.PATH,
|
||||
OperatorEntryField.wildcard_caseless,
|
||||
entry.value
|
||||
),
|
||||
};
|
||||
} else if (
|
||||
entry.field === 'process.executable.caseless' &&
|
||||
entry.type === 'match' &&
|
||||
entry.operator === OperatorEntryField.included
|
||||
) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue