Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Endpoint] Allow wildcard in trusted app paths #97623

Merged
merged 39 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
57bf657
show operator dropdown for path field
ashokaditya Apr 20, 2021
dd84507
update translation to use consistent values
ashokaditya Apr 20, 2021
9f72250
update schema to validate path values
ashokaditya Apr 20, 2021
fa9e3ce
add tests for field and operator values
ashokaditya Apr 20, 2021
8c19290
review changes
ashokaditya Apr 21, 2021
1deab39
update schema to enforce dropdown validation for PATH field
ashokaditya Apr 21, 2021
baac452
add tests for schema updates
ashokaditya Apr 21, 2021
5d9e848
optimise dropdown list for re-renders
ashokaditya Apr 21, 2021
9badfcc
Merge branch 'master' into sec-team-543/allow-wildcard-for-paths
kibanamachine Apr 21, 2021
f657b80
align input fields and keep alignments when resized
ashokaditya Apr 22, 2021
2ac56ee
correctly enter operator data on trusted app CRUD
ashokaditya Apr 22, 2021
c047025
update tests
ashokaditya Apr 22, 2021
74d5193
remove redundant code
ashokaditya Apr 22, 2021
dbd0933
better type assertion
ashokaditya Apr 23, 2021
f6cd3a8
move operator options out of component
ashokaditya Apr 23, 2021
a6725b9
derive keys from operator entry field
ashokaditya Apr 23, 2021
4def289
update type
ashokaditya Apr 23, 2021
0a60598
use custom styles for aligning input fields
ashokaditya Apr 23, 2021
c3643f0
add a custom type for trusted_apps operator
ashokaditya Apr 23, 2021
f9cb7ed
add wildcard entry type
ashokaditya Apr 26, 2021
b3f5dc4
use the new entry type
ashokaditya Apr 26, 2021
617aa7e
update tests
ashokaditya Apr 26, 2021
cc001e7
update name for wildcard type so that it can be used also for cased i…
ashokaditya Apr 26, 2021
f744808
Merge branch 'master' into sec-team-543/allow-wildcard-for-paths
kibanamachine Apr 27, 2021
6a874c0
update artifacts to support wildcard entries
ashokaditya Apr 27, 2021
b2cf223
add tests for list schemas
ashokaditya Apr 27, 2021
6f2d0d7
add placeholders for path values
ashokaditya Apr 27, 2021
284352e
ignore type check for now
ashokaditya Apr 27, 2021
bcf615a
add type assertion
ashokaditya Apr 27, 2021
835812f
remove unnecessary test
ashokaditya Apr 27, 2021
dbd3532
fix types
ashokaditya Apr 27, 2021
ddc3d72
add a note to entries
ashokaditya Apr 28, 2021
0bf8199
remove redundant type assertions
ashokaditya Apr 28, 2021
2dc4fd3
move placeholder text logic to utils
ashokaditya Apr 28, 2021
eb8c572
pass the style as prop
ashokaditya Apr 28, 2021
d7cb47b
update api doc
ashokaditya Apr 28, 2021
e8e9ce5
Merge branch 'master' into sec-team-543/allow-wildcard-for-paths
kibanamachine Apr 29, 2021
330731e
make placeholderText a function expression
ashokaditya Apr 29, 2021
4e7dce9
use semantic names for functions
ashokaditya Apr 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { schema } from '@kbn/config-schema';
import { ConditionEntry, ConditionEntryField, OperatingSystem } from '../types';
import { ConditionEntry, ConditionEntryField, OperatingSystem, OperatorEntryField } from '../types';
import { getDuplicateFields, isValidHash } from '../service/trusted_apps/validations';

export const DeleteTrustedAppsRequestSchema = {
Expand All @@ -30,7 +30,16 @@ export const GetTrustedAppsRequestSchema = {
};

const ConditionEntryTypeSchema = schema.literal('match');
const ConditionEntryOperatorSchema = schema.literal('included');
// when field === PATH -> operator in ('included', 'wildcard_caseless') else operator === 'included'
const ConditionEntryOperatorSchema = schema.conditional(
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved
schema.siblingRef('field'),
ConditionEntryField.PATH,
schema.oneOf([
schema.literal(OperatorEntryField.included),
schema.literal(OperatorEntryField.wildcard_caseless),
]),
schema.literal(OperatorEntryField.included)
);

/*
* A generic Entry schema to be used for a specific entry schema depending on the OS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,14 @@ export enum ConditionEntryField {
SIGNER = 'process.Ext.code_signature',
}

export enum OperatorEntryField {
included = 'included',
wildcard_caseless = 'wildcard_caseless',
}
export interface ConditionEntry<T extends ConditionEntryField = ConditionEntryField> {
field: T;
type: 'match';
operator: 'included';
operator: keyof typeof OperatorEntryField;
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved
value: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let onRemoveMock: jest.Mock;
let onChangeMock: jest.Mock;
let onVisitedMock: jest.Mock;

const entry: Readonly<ConditionEntry> = {
const baseEntry: Readonly<ConditionEntry> = {
field: ConditionEntryField.HASH,
type: 'match',
operator: 'included',
Expand All @@ -38,7 +38,8 @@ describe('Condition entry input', () => {
const getElement = (
subject: string,
os: OperatingSystem = OperatingSystem.WINDOWS,
isRemoveDisabled: boolean = false
isRemoveDisabled: boolean = false,
entry: ConditionEntry = baseEntry
) => (
<ConditionEntryInput
os={os}
Expand All @@ -64,10 +65,10 @@ describe('Condition entry input', () => {
expect(onChangeMock).toHaveBeenCalledTimes(1);
expect(onChangeMock).toHaveBeenCalledWith(
{
...entry,
...baseEntry,
field: { target: { value: field } },
},
entry
baseEntry
);
}
);
Expand All @@ -77,7 +78,7 @@ describe('Condition entry input', () => {
expect(onRemoveMock).toHaveBeenCalledTimes(0);
element.find('[data-test-subj="testOnRemove-remove"]').first().simulate('click');
expect(onRemoveMock).toHaveBeenCalledTimes(1);
expect(onRemoveMock).toHaveBeenCalledWith(entry);
expect(onRemoveMock).toHaveBeenCalledWith(baseEntry);
});

it('should not be able to call on remove for field input because disabled', () => {
Expand All @@ -92,7 +93,7 @@ describe('Condition entry input', () => {
expect(onVisitedMock).toHaveBeenCalledTimes(0);
element.find('[data-test-subj="testOnVisited-value"]').first().simulate('blur');
expect(onVisitedMock).toHaveBeenCalledTimes(1);
expect(onVisitedMock).toHaveBeenCalledWith(entry);
expect(onVisitedMock).toHaveBeenCalledWith(baseEntry);
});

it('should change value for field input', () => {
Expand All @@ -105,10 +106,10 @@ describe('Condition entry input', () => {
expect(onChangeMock).toHaveBeenCalledTimes(1);
expect(onChangeMock).toHaveBeenCalledWith(
{
...entry,
...baseEntry,
value: 'new value',
},
entry
baseEntry
);
});

Expand Down Expand Up @@ -138,4 +139,24 @@ describe('Condition entry input', () => {
.props() as EuiSuperSelectProps<string>;
expect(superSelectProps.options.length).toBe(2);
});

it('should have operator value selected when field is HASH', () => {
const element = shallow(getElement('testOperatorOptions'));
const inputField = element.find('[data-test-subj="testOperatorOptions-operator"]');
expect(inputField.contains('is'));
});

it('should show operator dorpdown with two values when field is PATH', () => {
const element = shallow(
getElement('testOperatorOptions', undefined, undefined, {
...baseEntry,
field: ConditionEntryField.PATH,
})
);
const superSelectProps = element
.find('[data-test-subj="testOperatorOptions-operator"]')
.first()
.props() as EuiSuperSelectProps<string>;
expect(superSelectProps.options.length).toBe(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import {
import {
ConditionEntry,
ConditionEntryField,
OperatorEntryField,
OperatingSystem,
} from '../../../../../../../common/endpoint/types';

import {
CONDITION_FIELD_DESCRIPTION,
CONDITION_FIELD_TITLE,
ENTRY_PROPERTY_TITLES,
OPERATOR_TITLE,
OPERATOR_TITLES,
} from '../../translations';

const ConditionEntryCell = memo<{
Expand Down Expand Up @@ -113,6 +114,14 @@ export const ConditionEntryInput = memo<ConditionEntryInputProps>(
];
}, [os]);

const operatorOptions = (Object.keys(OperatorEntryField) as [ConditionEntry['operator']]).map(
(value) => ({
dropdownDisplay: OPERATOR_TITLES[value],
inputDisplay: OPERATOR_TITLES[value],
value,
})
);

const handleValueUpdate = useCallback<ChangeEventHandler<HTMLInputElement>>(
(ev) => onChange({ ...entry, value: ev.target.value }, entry),
[entry, onChange]
Expand All @@ -123,6 +132,11 @@ export const ConditionEntryInput = memo<ConditionEntryInputProps>(
[entry, onChange]
);

const handleOperatorUpdate = useCallback(
(newOperator) => onChange({ ...entry, operator: newOperator }, entry),
[entry, onChange]
);

const handleRemoveClick = useCallback(() => onRemove(entry), [entry, onRemove]);

const handleValueOnBlur = useCallback(() => {
Expand All @@ -149,11 +163,29 @@ export const ConditionEntryInput = memo<ConditionEntryInputProps>(
/>
</ConditionEntryCell>
</EuiFlexItem>
<EuiFlexItem>
<ConditionEntryCell showLabel={showLabels} label={ENTRY_PROPERTY_TITLES.operator}>
<EuiFieldText name="operator" value={OPERATOR_TITLE.included} readOnly />
</ConditionEntryCell>
</EuiFlexItem>
{entry.field === ConditionEntryField.PATH ? (
<EuiFlexItem>
<ConditionEntryCell showLabel={showLabels} label={ENTRY_PROPERTY_TITLES.operator}>
<EuiSuperSelect
options={operatorOptions}
onChange={handleOperatorUpdate}
valueOfSelected={entry.operator}
data-test-subj={getTestId('operator')}
/>
</ConditionEntryCell>
</EuiFlexItem>
) : (
<EuiFlexItem>
<ConditionEntryCell showLabel={showLabels} label={ENTRY_PROPERTY_TITLES.operator}>
<EuiFieldText
name="operator"
value={OPERATOR_TITLES.included}
data-test-subj={getTestId('operator')}
readOnly
/>
</ConditionEntryCell>
</EuiFlexItem>
)}
<EuiFlexItem grow={3}>
<ConditionEntryCell showLabel={showLabels} label={ENTRY_PROPERTY_TITLES.value}>
<EuiFieldText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
ENTRY_PROPERTY_TITLES,
CARD_DELETE_BUTTON_LABEL,
CONDITION_FIELD_TITLE,
OPERATOR_TITLE,
OPERATOR_TITLES,
CARD_EDIT_BUTTON_LABEL,
} from '../../translations';

Expand All @@ -56,7 +56,7 @@ const getEntriesColumnDefinitions = (): Array<EuiTableFieldDataColumnType<Entry>
truncateText: true,
width: '20%',
render(field: Entry['operator'], entry: Entry) {
return OPERATOR_TITLE[field];
return OPERATOR_TITLES[field];
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,13 @@ export const CONDITION_FIELD_DESCRIPTION: { [K in ConditionEntryField]: string }
),
};

export const OPERATOR_TITLE: { [K in ConditionEntry['operator']]: string } = {
included: i18n.translate('xpack.securitySolution.trustedapps.card.operator.includes', {
export const OPERATOR_TITLES: { [K in ConditionEntry['operator']]: string } = {
included: i18n.translate('xpack.securitySolution.trustedapps.card.operator.is', {
defaultMessage: 'is',
}),
wildcard_caseless: i18n.translate('xpack.securitySolution.trustedapps.card.operator.matches', {
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved
defaultMessage: 'matches',
}),
};

export const PROPERTY_TITLES: Readonly<
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -20513,7 +20513,6 @@
"xpack.securitySolution.topN.closeButtonLabel": "閉じる",
"xpack.securitySolution.topN.rawEventsSelectLabel": "未加工イベント",
"xpack.securitySolution.trustedapps.aboutInfo": "パフォーマンスを改善したり、ホストで実行されている他のアプリケーションとの競合を解消したりするには、信頼できるアプリケーションを追加します。信頼できるアプリケーションは、Endpoint Securityを実行しているホストに適用されます。",
"xpack.securitySolution.trustedapps.card.operator.includes": "is",
"xpack.securitySolution.trustedapps.card.removeButtonLabel": "削除",
"xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg": "[{row}] フィールドエントリには値が必要です",
"xpack.securitySolution.trustedapps.create.conditionRequiredMsg": "1つ以上のフィールド定義が必要です",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -20840,7 +20840,6 @@
"xpack.securitySolution.topN.closeButtonLabel": "关闭",
"xpack.securitySolution.topN.rawEventsSelectLabel": "原始事件",
"xpack.securitySolution.trustedapps.aboutInfo": "添加受信任的应用程序,以提高性能或缓解与主机上运行的其他应用程序的冲突。受信任的应用程序将应用于运行 Endpoint Security 的主机。",
"xpack.securitySolution.trustedapps.card.operator.includes": "是",
"xpack.securitySolution.trustedapps.card.removeButtonLabel": "移除",
"xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg": "[{row}] 字段条目必须包含值",
"xpack.securitySolution.trustedapps.create.conditionRequiredMsg": "至少需要一个字段定义",
Expand Down