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 31 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
1 change: 1 addition & 0 deletions x-pack/plugins/lists/common/constants.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const OPERATOR_EXCLUDED = 'excluded';
export const ENTRY_VALUE = 'some host name';
export const MATCH = 'match';
export const MATCH_ANY = 'match_any';
export const WILDCARD = 'wildcard';
export const MAX_IMPORT_PAYLOAD_BYTES = 9000000;
export const IMPORT_BUFFER_SIZE = 1000;
export const LIST = 'list';
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lists/common/schemas/common/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export enum OperatorTypeEnum {
NESTED = 'nested',
MATCH = 'match',
MATCH_ANY = 'match_any',
WILDCARD = 'wildcard',
EXISTS = 'exists',
LIST = 'list',
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import * as t from 'io-ts';

import { NonEmptyString } from '../../../shared_imports';
import { operatorIncluded } from '../../common/schemas';

export const endpointEntryMatchWildcard = t.exact(
t.type({
field: NonEmptyString,
operator: operatorIncluded,
type: t.keyof({ wildcard: null }),
value: NonEmptyString,
})
);
export type EndpointEntryMatchWildcard = t.TypeOf<typeof endpointEntryMatchWildcard>;
18 changes: 16 additions & 2 deletions x-pack/plugins/lists/common/schemas/types/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,26 @@ import { entriesMatch } from './entry_match';
import { entriesExists } from './entry_exists';
import { entriesList } from './entry_list';
import { entriesNested } from './entry_nested';
import { entriesMatchWildcard } from './entry_match_wildcard';

export const entry = t.union([entriesMatch, entriesMatchAny, entriesList, entriesExists]);
export const entry = t.union([
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved
entriesMatch,
entriesMatchAny,
entriesList,
entriesExists,
entriesMatchWildcard,
]);
export type Entry = t.TypeOf<typeof entry>;

export const entriesArray = t.array(
t.union([entriesMatch, entriesMatchAny, entriesList, entriesExists, entriesNested])
t.union([
entriesMatch,
entriesMatchAny,
entriesList,
entriesExists,
entriesNested,
entriesMatchWildcard,
])
);
export type EntriesArray = t.TypeOf<typeof entriesArray>;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { ENTRY_VALUE, FIELD, OPERATOR, WILDCARD } from '../../constants.mock';

import { EntryMatchWildcard } from './entry_match_wildcard';

export const getEntryMatchWildcardMock = (): EntryMatchWildcard => ({
field: FIELD,
operator: OPERATOR,
type: WILDCARD,
value: ENTRY_VALUE,
});

export const getEntryMatchWildcardExcludeMock = (): EntryMatchWildcard => ({
...getEntryMatchWildcardMock(),
operator: 'excluded',
});
106 changes: 106 additions & 0 deletions x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';

import { foldLeftRight, getPaths } from '../../shared_imports';

import { getEntryMatchWildcardMock } from './entry_match_wildcard.mock';
import { EntryMatchWildcard, entriesMatchWildcard } from './entry_match_wildcard';

describe('entriesMatchWildcard', () => {
test('it should validate an entry', () => {
const payload = getEntryMatchWildcardMock();
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it should validate when operator is "included"', () => {
const payload = getEntryMatchWildcardMock();
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it should validate when "operator" is "excluded"', () => {
const payload = getEntryMatchWildcardMock();
payload.operator = 'excluded';
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it should FAIL validation when "field" is empty string', () => {
const payload: Omit<EntryMatchWildcard, 'field'> & { field: string } = {
...getEntryMatchWildcardMock(),
field: '',
};
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "field"']);
expect(message.schema).toEqual({});
});

test('it should FAIL validation when "value" is not string', () => {
const payload: Omit<EntryMatchWildcard, 'value'> & { value: string[] } = {
...getEntryMatchWildcardMock(),
value: ['some value'],
};
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([
'Invalid value "["some value"]" supplied to "value"',
]);
expect(message.schema).toEqual({});
});

test('it should FAIL validation when "value" is empty string', () => {
const payload: Omit<EntryMatchWildcard, 'value'> & { value: string } = {
...getEntryMatchWildcardMock(),
value: '',
};
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(['Invalid value "" supplied to "value"']);
expect(message.schema).toEqual({});
});

test('it should FAIL validation when "type" is not "wildcard"', () => {
const payload: Omit<EntryMatchWildcard, 'type'> & { type: string } = {
...getEntryMatchWildcardMock(),
type: 'match',
};
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(['Invalid value "match" supplied to "type"']);
expect(message.schema).toEqual({});
});

test('it should strip out extra keys', () => {
const payload: EntryMatchWildcard & {
extraKey?: string;
} = getEntryMatchWildcardMock();
payload.extraKey = 'some value';
const decoded = entriesMatchWildcard.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(getEntryMatchWildcardMock());
});
});
21 changes: 21 additions & 0 deletions x-pack/plugins/lists/common/schemas/types/entry_match_wildcard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import * as t from 'io-ts';

import { NonEmptyString } from '../../shared_imports';
import { operator } from '../common/schemas';

export const entriesMatchWildcard = t.exact(
t.type({
field: NonEmptyString,
operator,
type: t.keyof({ wildcard: null }),
value: NonEmptyString,
})
);
export type EntryMatchWildcard = t.TypeOf<typeof entriesMatchWildcard>;
1 change: 1 addition & 0 deletions x-pack/plugins/lists/common/schemas/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from './default_namespace';
export * from './entries';
export * from './entry_match';
export * from './entry_match_any';
export * from './entry_match_wildcard';
export * from './entry_list';
export * from './entry_exists';
export * from './entry_nested';
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/lists/common/shared_exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
EntryExists,
EntryMatch,
EntryMatchAny,
EntryMatchWildcard,
EntryNested,
EntryList,
EntriesArray,
Expand All @@ -39,6 +40,7 @@ export {
nestedEntryItem,
entriesMatch,
entriesMatchAny,
entriesMatchWildcard,
entriesExists,
entriesList,
namespaceType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ export const getEntryOnOperatorChange = (
},
...parent.parent.entries.slice(entryIndex + 1),
],
},
} as BuilderEntry,
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved
};
} else {
return { index: entryIndex, updatedEntry: newEntry };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EntryExists,
EntryMatch,
EntryMatchAny,
EntryMatchWildcard,
EntryNested,
ExceptionListItemSchema,
OperatorEnum,
Expand All @@ -34,7 +35,7 @@ export interface EmptyEntry {
id: string;
field: string | undefined;
operator: OperatorEnum;
type: OperatorTypeEnum.MATCH | OperatorTypeEnum.MATCH_ANY;
type: OperatorTypeEnum.MATCH | OperatorTypeEnum.MATCH_ANY | OperatorTypeEnum.WILDCARD;
value: string | string[] | undefined;
}

Expand All @@ -53,6 +54,7 @@ export interface EmptyNestedEntry {
entries: Array<
| (EntryMatch & { id?: string })
| (EntryMatchAny & { id?: string })
| (EntryMatchWildcard & { id?: string })
| (EntryExists & { id?: string })
>;
}
Expand All @@ -69,6 +71,7 @@ export type BuilderEntryNested = Omit<EntryNested, 'entries'> & {
entries: Array<
| (EntryMatch & { id?: string })
| (EntryMatchAny & { id?: string })
| (EntryMatchWildcard & { id?: string })
| (EntryExists & { id?: string })
>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,30 @@ describe('When invoking Trusted Apps Schema', () => {
expect(() => body.validate(bodyMsg)).not.toThrow();
});

it('should validate `entry.type` does not accept `wildcard` when field is NOT PATH', () => {
const bodyMsg = createNewTrustedApp({
entries: [
createConditionEntry({
field: ConditionEntryField.HASH,
type: 'wildcard',
}),
],
});
expect(() => body.validate(bodyMsg)).toThrow();
});

it('should validate `entry.type` accepts `wildcard` when field is PATH', () => {
const bodyMsg = createNewTrustedApp({
entries: [
createConditionEntry({
field: ConditionEntryField.PATH,
type: 'wildcard',
}),
],
});
expect(() => body.validate(bodyMsg)).not.toThrow();
});

it('should validate `entry.value` required', () => {
const { value, ...entry } = createConditionEntry();
expect(() => body.validate(createNewTrustedApp({ entries: [entry] }))).toThrow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ export const GetTrustedAppsRequestSchema = {
}),
};

const ConditionEntryTypeSchema = schema.literal('match');
const ConditionEntryOperatorSchema = schema.literal('included');
const ConditionEntryTypeSchema = schema.conditional(
schema.siblingRef('field'),
ConditionEntryField.PATH,
schema.oneOf([schema.literal('match'), schema.literal('wildcard')]),
schema.literal('match')
);
const ConditionEntryOperatorSchema = schema.literal('included' as ConditionEntry['operator']);
ashokaditya marked this conversation as resolved.
Show resolved Hide resolved

/*
* 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 @@ -7,6 +7,7 @@

import { TypeOf } from '@kbn/config-schema';
import { ApplicationStart } from 'kibana/public';

import {
DeleteTrustedAppsRequestSchema,
GetOneTrustedAppRequestSchema,
Expand Down Expand Up @@ -69,9 +70,15 @@ export enum ConditionEntryField {
SIGNER = 'process.Ext.code_signature',
}

export enum OperatorFieldIds {
is = 'is',
matches = 'matches',
}

export type TrustedAppEntryTypes = 'match' | 'wildcard';
export interface ConditionEntry<T extends ConditionEntryField = ConditionEntryField> {
field: T;
type: 'match';
type: TrustedAppEntryTypes;
operator: 'included';
value: string;
}
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/security_solution/common/shared_imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
EntryExists,
EntryMatch,
EntryMatchAny,
EntryMatchWildcard,
EntryNested,
EntryList,
EntriesArray,
Expand All @@ -38,6 +39,7 @@ export {
nestedEntryItem,
entriesMatch,
entriesMatchAny,
entriesMatchWildcard,
entriesExists,
entriesList,
namespaceType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Entry,
EntryMatch,
EntryMatchAny,
EntryMatchWildcard,
EntryExists,
ExceptionListItemSchema,
CreateExceptionListItemSchema,
Expand Down Expand Up @@ -92,6 +93,7 @@ export interface EmptyNestedEntry {
type: OperatorTypeEnum.NESTED;
entries: Array<
| (EntryMatch & { id?: string })
| (EntryMatchWildcard & { id?: string })
| (EntryMatchAny & { id?: string })
| (EntryExists & { id?: string })
>;
Expand All @@ -108,6 +110,7 @@ export type BuilderEntryNested = Omit<EntryNested, 'entries'> & {
id?: string;
entries: Array<
| (EntryMatch & { id?: string })
| (EntryMatchWildcard & { id?: string })
| (EntryMatchAny & { id?: string })
| (EntryExists & { id?: string })
>;
Expand Down
Loading