diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts index 99e568bf771f8..cc1aa79c7491c 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts @@ -212,6 +212,37 @@ test('tests processing keyword field with multi fields with analyzed text field' expect(mappings).toEqual(keywordWithAnalyzedMultiFieldsMapping); }); +test('tests processing keyword field with multi fields with normalized keyword field', () => { + const keywordWithNormalizedMultiFieldsLiteralYml = ` + - name: keywordWithNormalizedMultiField + type: keyword + multi_fields: + - name: normalized + type: keyword + normalizer: lowercase + `; + + const keywordWithNormalizedMultiFieldsMapping = { + properties: { + keywordWithNormalizedMultiField: { + ignore_above: 1024, + type: 'keyword', + fields: { + normalized: { + type: 'keyword', + ignore_above: 1024, + normalizer: 'lowercase', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(keywordWithNormalizedMultiFieldsLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(keywordWithNormalizedMultiFieldsMapping); +}); + test('tests processing object field with no other attributes', () => { const objectFieldLiteralYml = ` - name: objectField diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 00c2e873ba129..e0fea59107c26 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -189,6 +189,9 @@ function generateKeywordMapping(field: Field): IndexTemplateMapping { if (field.ignore_above) { mapping.ignore_above = field.ignore_above; } + if (field.normalizer) { + mapping.normalizer = field.normalizer; + } return mapping; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts index a44e5e4221f9f..5913302e77ba6 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts @@ -20,6 +20,7 @@ export interface Field { index?: boolean; required?: boolean; multi_fields?: Fields; + normalizer?: string; doc_values?: boolean; copy_to?: string; analyzer?: string; diff --git a/x-pack/plugins/lists/README.md b/x-pack/plugins/lists/README.md index dac6e8bb78fa5..02be757303417 100644 --- a/x-pack/plugins/lists/README.md +++ b/x-pack/plugins/lists/README.md @@ -113,12 +113,6 @@ You should see the new exception list created like so: ```sh { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "created_at": "2020-05-28T19:16:31.052Z", "created_by": "yo", "description": "This is a sample endpoint type exception", @@ -141,12 +135,6 @@ And you can attach exception list items like so: ```ts { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "comments": [], "created_at": "2020-05-28T19:17:21.099Z", "created_by": "yo", @@ -173,6 +161,7 @@ And you can attach exception list items like so: "list_id": "endpoint_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", + "os_types": ["linux"], "tags": [ "user added string for a tag", "malware" @@ -222,12 +211,6 @@ or for finding exception lists: { "data": [ { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "created_at": "2020-05-28T19:16:31.052Z", "created_by": "yo", "description": "This is a sample endpoint type exception", @@ -235,6 +218,7 @@ or for finding exception lists: "list_id": "endpoint_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", + "os_types": ["linux"], "tags": [ "user added string for a tag", "malware" diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/plugins/lists/common/constants.mock.ts index 46ed524ff33e3..c712af83dd9b1 100644 --- a/x-pack/plugins/lists/common/constants.mock.ts +++ b/x-pack/plugins/lists/common/constants.mock.ts @@ -5,6 +5,7 @@ */ import moment from 'moment'; +import { OsTypeArray } from './schemas/common'; import { EntriesArray } from './schemas/types'; import { EndpointEntriesArray } from './schemas/types/endpoint'; export const DATE_NOW = '2020-04-20T15:25:31.830Z'; @@ -68,7 +69,7 @@ export const ENDPOINT_ENTRIES: EndpointEntriesArray = [ { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, ]; export const ITEM_TYPE = 'simple'; -export const _TAGS = []; +export const OS_TYPES: OsTypeArray = ['windows']; export const TAGS = []; export const COMMENTS = []; export const FILTER = 'name:Nicolas Bourbaki'; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.test.ts b/x-pack/plugins/lists/common/schemas/common/schemas.test.ts index ec3871b673888..04bdf037c556e 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.test.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.test.ts @@ -27,6 +27,8 @@ import { esDataTypeUnion, exceptionListType, operator, + osType, + osTypeArrayOrUndefined, type, } from './schemas'; @@ -379,4 +381,35 @@ describe('Common schemas', () => { expect(message.schema).toEqual({}); }); }); + + describe('osType', () => { + test('it will validate a correct osType', () => { + const payload = 'windows'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect osType', () => { + const payload = 'foo'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "foo" supplied to ""linux" | "macos" | "windows""', + ]); + expect(message.schema).toEqual({}); + }); + + test('it will default to an empty array when osTypeArrayOrUndefined is used', () => { + const payload = undefined; + const decoded = osTypeArrayOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + }); }); diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index 37da5fbcd1a1b..7497b81fbe91c 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { DefaultNamespace } from '../types/default_namespace'; -import { DefaultStringArray, NonEmptyString } from '../../shared_imports'; +import { DefaultArray, DefaultStringArray, NonEmptyString } from '../../shared_imports'; export const name = t.string; export type Name = t.TypeOf; @@ -211,11 +211,6 @@ export type Tags = t.TypeOf; export const tagsOrUndefined = t.union([tags, t.undefined]); export type TagsOrUndefined = t.TypeOf; -export const _tags = DefaultStringArray; -export type _Tags = t.TypeOf; -export const _tagsOrUndefined = t.union([_tags, t.undefined]); -export type _TagsOrUndefined = t.TypeOf; - export const exceptionListType = t.keyof({ detection: null, endpoint: null }); export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); export type ExceptionListType = t.TypeOf; @@ -317,3 +312,16 @@ export type Immutable = t.TypeOf; export const immutableOrUndefined = t.union([immutable, t.undefined]); export type ImmutableOrUndefined = t.TypeOf; + +export const osType = t.keyof({ + linux: null, + macos: null, + windows: null, +}); +export type OsType = t.TypeOf; + +export const osTypeArray = DefaultArray(osType); +export type OsTypeArray = t.TypeOf; + +export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); +export type OsTypeArrayOrUndefined = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts index 529e173618f15..f292b7c5bc945 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts @@ -11,20 +11,20 @@ import { ITEM_TYPE, META, NAME, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { CreateEndpointListItemSchema } from './create_endpoint_list_item_schema'; export const getCreateEndpointListItemSchemaMock = (): CreateEndpointListItemSchema => ({ - _tags: _TAGS, comments: COMMENTS, description: DESCRIPTION, entries: ENDPOINT_ENTRIES, item_id: undefined, meta: META, name: NAME, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts index 624de2fb30d17..afb0454a79667 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts @@ -174,19 +174,6 @@ describe('create_endpoint_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { const inputPayload = getCreateEndpointListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts index d1fc167f5a92b..611d9a83befc7 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts @@ -8,13 +8,13 @@ import * as t from 'io-ts'; import { ItemId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListItemType, meta, name, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -34,10 +34,10 @@ export const createEndpointListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -48,11 +48,11 @@ export type CreateEndpointListItemSchema = t.OutputOf>, - '_tags' | 'tags' | 'item_id' | 'entries' | 'comments' + 'tags' | 'item_id' | 'entries' | 'comments' | 'os_types' > & { - _tags: _Tags; comments: CreateCommentsArray; tags: Tags; item_id: ItemId; entries: EntriesArray; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts index da22e33dc7b52..9a55e88a7a8fa 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts @@ -14,14 +14,13 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { CreateExceptionListItemSchema } from './create_exception_list_item_schema'; export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ - _tags: _TAGS, comments: COMMENTS, description: DESCRIPTION, entries: ENTRIES, @@ -30,6 +29,7 @@ export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemS meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); @@ -43,6 +43,7 @@ export const getCreateExceptionListItemMinimalSchemaMock = (): CreateExceptionLi item_id: ITEM_ID, list_id: LIST_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); @@ -54,5 +55,6 @@ export const getCreateExceptionListItemMinimalSchemaMockWithoutId = (): CreateEx entries: ENTRIES, list_id: LIST_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts index 4a4c3972dc1e3..e83b2e3010785 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts @@ -176,19 +176,6 @@ describe('create_exception_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { const inputPayload = getCreateExceptionListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts index fd3390721d41e..642a6c549e7fa 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts @@ -8,15 +8,15 @@ import * as t from 'io-ts'; import { ItemId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListItemType, list_id, meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -41,11 +41,11 @@ export const createExceptionListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -56,12 +56,12 @@ export type CreateExceptionListItemSchema = t.OutputOf>, - '_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' + 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' > & { - _tags: _Tags; comments: CreateCommentsArray; tags: Tags; item_id: ItemId; entries: EntriesArray; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts index f8431fcce1bf7..3150cb9975f21 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts @@ -17,12 +17,12 @@ import { import { CreateExceptionListSchema } from './create_exception_list_schema'; export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ - _tags: [], description: DESCRIPTION, list_id: undefined, meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: [], tags: [], type: ENDPOINT_TYPE, version: VERSION, diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts index c9e2aa37a132b..6bcd3bc15a975 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts @@ -50,19 +50,6 @@ describe('create_exception_list_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateExceptionListSchemaMock(); - const outputPayload = getCreateExceptionListSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "list_id" and auto generate a uuid', () => { const inputPayload = getCreateExceptionListSchemaMock(); delete inputPayload.list_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts index ffec974602714..4eae11081454c 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts @@ -8,14 +8,14 @@ import * as t from 'io-ts'; import { ListId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListType, meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -36,10 +36,10 @@ export const createExceptionListSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode }) @@ -51,11 +51,11 @@ export type CreateExceptionListSchema = t.OutputOf>, - '_tags' | 'tags' | 'list_id' | 'namespace_type' + 'tags' | 'list_id' | 'namespace_type' | 'os_types' > & { - _tags: _Tags; tags: Tags; list_id: ListId; namespace_type: NamespaceType; + os_types: OsTypeArray; version: DefaultVersionNumberDecoded; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts index 0847389dac922..8c999332e8893 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts @@ -13,14 +13,13 @@ import { LIST_ITEM_ID, META, NAME, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { UpdateEndpointListItemSchema } from './update_endpoint_list_item_schema'; export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSchema => ({ - _tags: _TAGS, _version: undefined, comments: COMMENTS, description: DESCRIPTION, @@ -29,6 +28,7 @@ export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSch item_id: LIST_ITEM_ID, meta: META, name: NAME, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts index 671e38ceb5266..c7be8b78d54a6 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts @@ -127,18 +127,6 @@ describe('update_endpoint_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should not allow an extra key to be sent in', () => { const payload: UpdateEndpointListItemSchema & { extraKey?: string; diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts index 6ce5ad7858b78..f6ced91cd4010 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts @@ -7,15 +7,15 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListItemType, id, meta, name, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -37,12 +37,12 @@ export const updateEndpointListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode item_id: t.union([t.string, t.undefined]), meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -53,10 +53,10 @@ export type UpdateEndpointListItemSchema = t.OutputOf>, - '_tags' | 'tags' | 'entries' | 'comments' + 'tags' | 'entries' | 'comments' > & { - _tags: _Tags; comments: UpdateCommentsArray; tags: Tags; entries: EntriesArray; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts index 4673c0fe7629d..e65b37b48545e 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts @@ -15,14 +15,13 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { UpdateExceptionListItemSchema } from './update_exception_list_item_schema'; export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - _tags: _TAGS, _version: undefined, comments: COMMENTS, description: DESCRIPTION, @@ -32,6 +31,7 @@ export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemS meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], tags: TAGS, type: ITEM_TYPE, }); @@ -45,5 +45,6 @@ export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionLi entries: ENTRIES, item_id: ITEM_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts index da320a4983de3..387c29ad2d190 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts @@ -139,18 +139,6 @@ describe('update_exception_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "item_id" and generate a correct body not counting the uuid', () => { const inputPayload = getUpdateExceptionListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts index 659dde0b5b533..14cac2bb93fe0 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts @@ -7,9 +7,8 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListItemType, @@ -17,6 +16,7 @@ import { meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -39,13 +39,13 @@ export const updateExceptionListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode item_id: t.union([t.string, t.undefined]), meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -56,11 +56,11 @@ export type UpdateExceptionListItemSchema = t.OutputOf>, - '_tags' | 'tags' | 'entries' | 'namespace_type' | 'comments' + 'tags' | 'entries' | 'namespace_type' | 'comments' | 'os_types' > & { - _tags: _Tags; comments: UpdateCommentsArray; tags: Tags; entries: EntriesArray; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts index b7dc2d9e0c948..fdefa6fe9b2c5 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE, _TAGS } from '../../constants.mock'; +import { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE } from '../../constants.mock'; import { UpdateExceptionListSchema } from './update_exception_list_schema'; export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ - _tags: _TAGS, _version: undefined, description: DESCRIPTION, id: ID, @@ -17,6 +16,7 @@ export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: [], tags: ['malware'], type: 'endpoint', }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts index 32f114ae34d8e..4afd1aa442aa7 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts @@ -100,18 +100,6 @@ describe('update_exception_list_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateExceptionListSchemaMock(); - const outputPayload = getUpdateExceptionListSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { const inputPayload = getUpdateExceptionListSchemaMock(); delete inputPayload.list_id; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts index 54e0bbafe4981..37ba21bcfc424 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts @@ -7,9 +7,8 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListType, @@ -18,6 +17,7 @@ import { meta, name, namespace_type, + osTypeArrayOrUndefined, tags, version, } from '../common/schemas'; @@ -34,12 +34,12 @@ export const updateExceptionListSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode id, // defaults to undefined if not set during decode list_id, // defaults to undefined if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode version, // defaults to undefined if not set during decode }) @@ -51,9 +51,9 @@ export type UpdateExceptionListSchema = t.OutputOf>, - '_tags | tags | namespace_type' + 'tags | namespace_type' | 'os_types' > & { - _tags: _Tags; tags: Tags; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts index 380a1e3a4cfd5..ebae189ca7d06 100644 --- a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts @@ -42,7 +42,7 @@ describe('create_endpoint_list_schema', () => { const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "_tags,["endpoint","process","malware","os:linux"],_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', + 'invalid keys "_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,os_types,["linux"],tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts index 1a8f21a5232f8..c2a751c03ee13 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts @@ -15,6 +15,7 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TIE_BREAKER, USER, } from '../../constants.mock'; @@ -22,7 +23,6 @@ import { import { ExceptionListItemSchema } from './exception_list_item_schema'; export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ - _tags: ['endpoint', 'process', 'malware', 'os:linux'], _version: undefined, comments: COMMENTS, created_at: DATE_NOW, @@ -35,6 +35,7 @@ export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], tie_breaker_id: TIE_BREAKER, type: ITEM_TYPE, @@ -49,7 +50,6 @@ export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = (): Partial< ExceptionListItemSchema > => ({ - _tags: [], comments: [], created_by: ELASTIC_USER, description: DESCRIPTION, @@ -58,6 +58,7 @@ export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = (): Pa list_id: LIST_ID, name: NAME, namespace_type: 'single', + os_types: OS_TYPES, tags: [], type: ITEM_TYPE, updated_by: ELASTIC_USER, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts index 65a1a26eaa622..f5ee12e098d17 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts @@ -7,7 +7,6 @@ import * as t from 'io-ts'; import { - _tags, _versionOrUndefined, created_at, created_by, @@ -19,6 +18,7 @@ import { metaOrUndefined, name, namespace_type, + osTypeArray, tags, tie_breaker_id, updated_at, @@ -28,7 +28,6 @@ import { commentsArray, entriesArray } from '../types'; export const exceptionListItemSchema = t.exact( t.type({ - _tags, _version: _versionOrUndefined, comments: commentsArray, created_at, @@ -41,6 +40,7 @@ export const exceptionListItemSchema = t.exact( meta: metaOrUndefined, name, namespace_type, + os_types: osTypeArray, tags, tie_breaker_id, type: exceptionListItemType, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts index 6df051e83b97c..7371a9d16fd4d 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts @@ -28,7 +28,6 @@ import { import { ExceptionListSchema } from './exception_list_schema'; export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ - _tags: ['endpoint', 'process', 'malware', 'os:linux'], _version: _VERSION, created_at: DATE_NOW, created_by: USER, @@ -39,6 +38,7 @@ export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ meta: META, name: 'Sample Endpoint Exception List', namespace_type: 'agnostic', + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], tie_breaker_id: TIE_BREAKER, type: ENDPOINT_TYPE, @@ -63,13 +63,13 @@ export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => { export const getExceptionResponseMockWithoutAutoGeneratedValues = (): Partial< ExceptionListSchema > => ({ - _tags: [], created_by: ELASTIC_USER, description: DESCRIPTION, immutable: IMMUTABLE, list_id: LIST_ID, name: NAME, namespace_type: 'single', + os_types: [], tags: [], type: ENDPOINT_TYPE, updated_by: ELASTIC_USER, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts index 6597cb20508ca..ec03467c64e5c 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts @@ -7,7 +7,6 @@ import * as t from 'io-ts'; import { - _tags, _versionOrUndefined, created_at, created_by, @@ -19,6 +18,7 @@ import { metaOrUndefined, name, namespace_type, + osTypeArray, tags, tie_breaker_id, updated_at, @@ -28,7 +28,6 @@ import { export const exceptionListSchema = t.exact( t.type({ - _tags, _version: _versionOrUndefined, created_at, created_by, @@ -39,6 +38,7 @@ export const exceptionListSchema = t.exact( meta: metaOrUndefined, name, namespace_type, + os_types: osTypeArray, tags, tie_breaker_id, type: exceptionListType, diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts index f4db77f4ee057..16c43e4611edb 100644 --- a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts +++ b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts @@ -8,7 +8,6 @@ import * as t from 'io-ts'; import { commentsArrayOrUndefined, entriesArrayOrUndefined } from '../types'; import { - _tags, created_at, created_by, description, @@ -20,6 +19,7 @@ import { list_type, metaOrUndefined, name, + osTypeArray, tags, tie_breaker_id, updated_by, @@ -31,7 +31,6 @@ import { */ export const exceptionListSoSchema = t.exact( t.type({ - _tags, comments: commentsArrayOrUndefined, created_at, created_by, @@ -43,6 +42,7 @@ export const exceptionListSoSchema = t.exact( list_type, meta: metaOrUndefined, name, + os_types: osTypeArray, tags, tie_breaker_id, type: t.union([exceptionListType, exceptionListItemType]), diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts index 361837bdef229..ec9358c2cb503 100644 --- a/x-pack/plugins/lists/common/shared_exports.ts +++ b/x-pack/plugins/lists/common/shared_exports.ts @@ -41,6 +41,8 @@ export { namespaceType, ExceptionListType, Type, + osTypeArray, + OsTypeArray, } from './schemas'; export { ENDPOINT_LIST_ID } from './constants'; diff --git a/x-pack/plugins/lists/common/shared_imports.ts b/x-pack/plugins/lists/common/shared_imports.ts index e5302b5cd5d88..9fe37465519ea 100644 --- a/x-pack/plugins/lists/common/shared_imports.ts +++ b/x-pack/plugins/lists/common/shared_imports.ts @@ -6,6 +6,7 @@ export { NonEmptyString, + DefaultArray, DefaultUuid, DefaultStringArray, DefaultVersionNumber, diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts index 7fd07ed5fb8cd..cce4038ff48d6 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts @@ -37,13 +37,13 @@ export const createEndpointListItemRoute = (router: IRouter): void => { try { const { name, - _tags, tags, meta, comments, description, entries, item_id: itemId, + os_types: osTypes, type, } = request.body; const exceptionLists = getExceptionListClient(context); @@ -58,13 +58,13 @@ export const createEndpointListItemRoute = (router: IRouter): void => { }); } else { const createdList = await exceptionLists.createEndpointListItem({ - _tags, comments, description, entries, itemId, meta, name, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index e51e113239f20..afcb0f99c8a35 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -39,7 +39,6 @@ export const createExceptionListItemRoute = (router: IRouter): void => { const { namespace_type: namespaceType, name, - _tags, tags, meta, comments, @@ -47,6 +46,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => { entries, item_id: itemId, list_id: listId, + os_types: osTypes, type, } = request.body; const exceptionLists = getExceptionListClient(context); @@ -87,7 +87,6 @@ export const createExceptionListItemRoute = (router: IRouter): void => { } } const createdList = await exceptionLists.createExceptionListItem({ - _tags, comments, description, entries, @@ -96,6 +95,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index 08db0825e07bd..fd2ba6340009c 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -36,7 +36,6 @@ export const createExceptionListRoute = (router: IRouter): void => { try { const { name, - _tags, tags, meta, namespace_type: namespaceType, @@ -58,7 +57,6 @@ export const createExceptionListRoute = (router: IRouter): void => { }); } else { const createdList = await exceptionLists.createExceptionList({ - _tags, description, immutable: false, listId, diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index e0d6a0ffffa6b..8312f2fc87b98 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -38,9 +38,9 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { description, id, name, + os_types: osTypes, meta, type, - _tags, _version, comments, entries, @@ -49,7 +49,6 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { } = request.body; const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateEndpointListItem({ - _tags, _version, comments, description, @@ -58,6 +57,7 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { itemId, meta, name, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 745ad0735a174..9ad563724b860 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -46,12 +46,12 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { name, meta, type, - _tags, _version, comments, entries, item_id: itemId, namespace_type: namespaceType, + os_types: osTypes, tags, } = request.body; if (id == null && itemId == null) { @@ -62,7 +62,6 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { } else { const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateExceptionListItem({ - _tags, _version, comments, description, @@ -72,6 +71,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index 1903d0f601d1d..47008e3b78fae 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -35,7 +35,6 @@ export const updateExceptionListRoute = (router: IRouter): void => { const siemResponse = buildSiemResponse(response); try { const { - _tags, _version, tags, name, @@ -44,6 +43,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { list_id: listId, meta, namespace_type: namespaceType, + os_types: osTypes, type, version, } = request.body; @@ -55,7 +55,6 @@ export const updateExceptionListRoute = (router: IRouter): void => { }); } else { const list = await exceptionLists.updateExceptionList({ - _tags, _version, description, id, @@ -63,6 +62,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, version, diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts index f9e408833e069..b3fd2c0eced98 100644 --- a/x-pack/plugins/lists/server/saved_objects/exception_list.ts +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -6,6 +6,8 @@ import { SavedObjectsType } from 'kibana/server'; +import { migrations } from './migrations'; + export const exceptionListSavedObjectType = 'exception-list'; export const exceptionListAgnosticSavedObjectType = 'exception-list-agnostic'; export type SavedObjectType = 'exception-list' | 'exception-list-agnostic'; @@ -149,6 +151,9 @@ export const exceptionListItemMapping: SavedObjectsType['mappings'] = { item_id: { type: 'keyword', }, + os_types: { + type: 'keyword', + }, }, }; @@ -163,6 +168,7 @@ const combinedMappings: SavedObjectsType['mappings'] = { export const exceptionListType: SavedObjectsType = { hidden: false, mappings: combinedMappings, + migrations, name: exceptionListSavedObjectType, namespaceType: 'single', }; @@ -170,6 +176,7 @@ export const exceptionListType: SavedObjectsType = { export const exceptionListAgnosticType: SavedObjectsType = { hidden: false, mappings: combinedMappings, + migrations, name: exceptionListAgnosticSavedObjectType, namespaceType: 'agnostic', }; diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.test.ts b/x-pack/plugins/lists/server/saved_objects/migrations.test.ts new file mode 100644 index 0000000000000..cd7ef0f37d505 --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/migrations.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectUnsanitizedDoc } from 'kibana/server'; + +import { ENDPOINT_LIST_ID } from '../../common/constants'; + +import { OldExceptionListSoSchema, migrations } from './migrations'; + +describe('7.10.0 lists migrations', () => { + const migration = migrations['7.10.0']; + + test('properly converts .text fields to .caseless', () => { + const doc = { + attributes: { + entries: [ + { + field: 'file.path.text', + operator: 'included', + type: 'match', + value: 'C:\\Windows\\explorer.exe', + }, + { + field: 'host.os.name', + operator: 'included', + type: 'match', + value: 'my-host', + }, + { + entries: [ + { + field: 'process.command_line.text', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + { + field: 'process.parent.command_line.text', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + ], + field: 'nested.field', + type: 'nested', + }, + ], + list_id: ENDPOINT_LIST_ID, + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }; + expect( + migration((doc as unknown) as SavedObjectUnsanitizedDoc) + ).toEqual({ + attributes: { + entries: [ + { + field: 'file.path.caseless', + operator: 'included', + type: 'match', + value: 'C:\\Windows\\explorer.exe', + }, + { + field: 'host.os.name', + operator: 'included', + type: 'match', + value: 'my-host', + }, + { + entries: [ + { + field: 'process.command_line.caseless', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + { + field: 'process.parent.command_line.caseless', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + ], + field: 'nested.field', + type: 'nested', + }, + ], + list_id: ENDPOINT_LIST_ID, + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }); + }); + + test('properly copies os tags to os_types', () => { + const doc = { + attributes: { + _tags: ['1234', 'os:windows'], + comments: [], + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }; + expect( + migration((doc as unknown) as SavedObjectUnsanitizedDoc) + ).toEqual({ + attributes: { + _tags: ['1234', 'os:windows'], + comments: [], + os_types: ['windows'], + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }); + }); +}); diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.ts b/x-pack/plugins/lists/server/saved_objects/migrations.ts new file mode 100644 index 0000000000000..2e9792cd8eb3c --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/migrations.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc } from 'kibana/server'; + +import { ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../common/constants'; +import { + EntriesArray, + ExceptionListSoSchema, + NonEmptyNestedEntriesArray, + OsTypeArray, + entriesNested, + entry, +} from '../../common/schemas'; + +const entryType = t.union([entry, entriesNested]); +type EntryType = t.TypeOf; + +const migrateEntry = (entryToMigrate: EntryType): EntryType => { + const newEntry = entryToMigrate; + if (entriesNested.is(entryToMigrate) && entriesNested.is(newEntry)) { + newEntry.entries = entryToMigrate.entries.map((nestedEntry) => + migrateEntry(nestedEntry) + ) as NonEmptyNestedEntriesArray; + } + newEntry.field = entryToMigrate.field.replace('.text', '.caseless'); + return newEntry; +}; + +const reduceOsTypes = (acc: string[], tag: string): string[] => { + if (tag.startsWith('os:')) { + // TODO: check OS against type + return [...acc, tag.replace('os:', '')]; + } + return [...acc]; +}; + +export type OldExceptionListSoSchema = ExceptionListSoSchema & { + _tags: string[]; +}; + +export const migrations = { + '7.10.0': ( + doc: SavedObjectUnsanitizedDoc + ): SavedObjectSanitizedDoc => ({ + ...doc, + ...{ + attributes: { + ...doc.attributes, + ...(doc.attributes.entries && + [ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID].includes(doc.attributes.list_id) && { + entries: (doc.attributes.entries as EntriesArray).map(migrateEntry), + }), + ...(doc.attributes._tags && + doc.attributes._tags.reduce(reduceOsTypes, []).length > 0 && { + os_types: doc.attributes._tags.reduce(reduceOsTypes, []) as OsTypeArray, + }), + }, + }, + references: doc.references || [], + }), +}; diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json index 6999441d21941..5e7dee83776bf 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json @@ -1,10 +1,10 @@ { "item_id": "simple_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json index 19027ac189a47..73271514269da 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json @@ -1,6 +1,5 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "detection", "description": "This is a sample endpoint type exception", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json index 4121b13880660..9987f5d46af1b 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json @@ -1,6 +1,5 @@ { "list_id": "endpoint_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "endpoint", "description": "This is a sample agnostic endpoint type exception", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json index 306195f4226e3..986c368bd2de3 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json @@ -1,6 +1,5 @@ { "list_id": "detection_list", - "_tags": ["detection"], "tags": ["detection", "sample_tag"], "type": "detection", "description": "This is a sample detection type exception list", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json index eede855aab199..e7eed0a56cb6d 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json @@ -1,11 +1,11 @@ { "list_id": "simple_list", "item_id": "simple_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json index 9cda9c12d6b30..d57fb19955e34 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json @@ -1,12 +1,12 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic endpoint type exception", "name": "Sample Endpoint Exception List", "namespace_type": "agnostic", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json index f1281e2ea0560..9cc73577818c5 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json @@ -1,10 +1,10 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception that has no item_id so it creates a new id each time", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json index 833f6c023c5d9..e65f818c1df85 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json @@ -1,6 +1,5 @@ { "list_id": "detection_list", - "_tags": ["detection"], "tags": ["test_tag", "detection", "no_more_bad_guys"], "type": "simple", "description": "This is a sample detection type exception that has no item_id so it creates a new id each time", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json index bab435487ec25..9a5f6e888e6e4 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json @@ -1,11 +1,11 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item_good_rock01", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "Don't signal when agent.name is rock01 and source.ip is in the goodguys.txt list", "name": "Filter out good guys ip and agent.name rock01", + "os_types": ["windows"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json index e0d401eff9269..d0756b990aad0 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json @@ -1,11 +1,11 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item_lg_val_list", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample exception list item with a large value list included", "name": "Sample Endpoint Exception List Item with large value list", + "os_types": ["windows"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json index 9f0c306a408f0..293ca14d323f7 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json @@ -1,12 +1,12 @@ { "list_id": "endpoint_trusted_apps", "item_id": "endpoint_trusted_apps_item", - "_tags": ["endpoint", "os:linux", "os:windows", "os:macos", "trusted-app"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic endpoint trusted app entry", "name": "Sample Endpoint Trusted App Entry", "namespace_type": "agnostic", + "os_types": ["linux", "windows", "macos"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json index 8d07b29d7b428..15a6f495b7a8f 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json @@ -1,8 +1,8 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "endpoint", + "os_types": ["linux"], "description": "Different description", "name": "Sample Endpoint Exception List" } diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json index 90d5e0846e53a..fe29ca80c632e 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json @@ -1,11 +1,11 @@ { "item_id": "endpoint_list_item", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic change here this list", "name": "Sample Endpoint Exception List update change", "namespace_type": "agnostic", + "os_types": ["windows"], "entries": [ { "field": "event.category", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json index 81db909277595..d55f121253406 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json @@ -1,5 +1,4 @@ { - "_tags": ["detection"], "comments": [], "description": "Test comments - exception list item", "entries": [ diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts index 2e9bb1325632e..fb2b637657bb6 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts @@ -35,7 +35,6 @@ export const createEndpointList = async ({ const savedObject = await savedObjectsClient.create( savedObjectType, { - _tags: [], comments: undefined, created_at: dateNow, created_by: user, @@ -47,6 +46,7 @@ export const createEndpointList = async ({ list_type: 'list', meta: undefined, name: ENDPOINT_LIST_NAME, + os_types: [], tags: [], tie_breaker_id: tieBreaker ?? uuid.v4(), type: 'endpoint', diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts index c782cdd302666..d9eedb0af4e77 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts @@ -43,7 +43,6 @@ export const createEndpointTrustedAppsList = async ({ const savedObject = await savedObjectsClient.create( savedObjectType, { - _tags: [], comments: undefined, created_at: dateNow, created_by: user, @@ -55,6 +54,7 @@ export const createEndpointTrustedAppsList = async ({ list_type: 'list', meta: undefined, name: ENDPOINT_TRUSTED_APPS_LIST_NAME, + os_types: [], tags: [], tie_breaker_id: tieBreaker ?? uuid.v4(), type: 'endpoint', diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts index c8d709ca340ad..91a0506ad06e3 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts @@ -19,13 +19,11 @@ import { NamespaceType, Tags, Version, - _Tags, } from '../../../common/schemas'; import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; interface CreateExceptionListOptions { - _tags: _Tags; listId: ListId; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; @@ -41,7 +39,6 @@ interface CreateExceptionListOptions { } export const createExceptionList = async ({ - _tags, listId, immutable, savedObjectsClient, @@ -58,7 +55,6 @@ export const createExceptionList = async ({ const savedObjectType = getSavedObjectType({ namespaceType }); const dateNow = new Date().toISOString(); const savedObject = await savedObjectsClient.create(savedObjectType, { - _tags, comments: undefined, created_at: dateNow, created_by: user, @@ -70,6 +66,7 @@ export const createExceptionList = async ({ list_type: 'list', meta, name, + os_types: [], tags, tie_breaker_id: tieBreaker ?? uuid.v4(), type, diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts index 47c21735b45f4..9f331362cdd44 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts @@ -19,8 +19,8 @@ import { MetaOrUndefined, Name, NamespaceType, + OsTypeArray, Tags, - _Tags, } from '../../../common/schemas'; import { @@ -30,7 +30,6 @@ import { } from './utils'; interface CreateExceptionListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; listId: ListId; itemId: ItemId; @@ -44,10 +43,10 @@ interface CreateExceptionListItemOptions { tags: Tags; tieBreaker?: string; type: ExceptionListItemType; + osTypes: OsTypeArray; } export const createExceptionListItem = async ({ - _tags, comments, entries, itemId, @@ -55,6 +54,7 @@ export const createExceptionListItem = async ({ savedObjectsClient, namespaceType, name, + osTypes, description, meta, user, @@ -69,7 +69,6 @@ export const createExceptionListItem = async ({ user, }); const savedObject = await savedObjectsClient.create(savedObjectType, { - _tags, comments: transformedComments, created_at: dateNow, created_by: user, @@ -81,6 +80,7 @@ export const createExceptionListItem = async ({ list_type: 'item', meta, name, + os_types: osTypes as OsTypeArray, tags, tie_breaker_id: tieBreaker ?? uuid.v4(), type, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 747458175e3b8..9747c58d1cd0f 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -109,20 +109,19 @@ export class ExceptionListClient { * being there and existing before the item is inserted into the agnostic endpoint list. */ public createEndpointListItem = async ({ - _tags, comments, description, entries, itemId, meta, name, + osTypes, tags, type, }: CreateEndpointListItemOptions): Promise => { const { savedObjectsClient, user } = this; await this.createEndpointList(); return createExceptionListItem({ - _tags, comments, description, entries, @@ -131,6 +130,7 @@ export class ExceptionListClient { meta, name, namespaceType: 'agnostic', + osTypes, savedObjectsClient, tags, type, @@ -145,7 +145,6 @@ export class ExceptionListClient { * return of null but at least the list exists again. */ public updateEndpointListItem = async ({ - _tags, _version, comments, description, @@ -154,13 +153,13 @@ export class ExceptionListClient { itemId, meta, name, + osTypes, tags, type, }: UpdateEndpointListItemOptions): Promise => { const { savedObjectsClient, user } = this; await this.createEndpointList(); return updateExceptionListItem({ - _tags, _version, comments, description, @@ -170,6 +169,7 @@ export class ExceptionListClient { meta, name, namespaceType: 'agnostic', + osTypes, savedObjectsClient, tags, type, @@ -189,7 +189,6 @@ export class ExceptionListClient { }; public createExceptionList = async ({ - _tags, description, immutable, listId, @@ -202,7 +201,6 @@ export class ExceptionListClient { }: CreateExceptionListOptions): Promise => { const { savedObjectsClient, user } = this; return createExceptionList({ - _tags, description, immutable, listId, @@ -218,7 +216,6 @@ export class ExceptionListClient { }; public updateExceptionList = async ({ - _tags, _version, id, description, @@ -226,13 +223,13 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, version, }: UpdateExceptionListOptions): Promise => { const { savedObjectsClient, user } = this; return updateExceptionList({ - _tags, _version, description, id, @@ -240,6 +237,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, @@ -263,7 +261,6 @@ export class ExceptionListClient { }; public createExceptionListItem = async ({ - _tags, comments, description, entries, @@ -272,12 +269,12 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, }: CreateExceptionListItemOptions): Promise => { const { savedObjectsClient, user } = this; return createExceptionListItem({ - _tags, comments, description, entries, @@ -286,6 +283,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, @@ -294,7 +292,6 @@ export class ExceptionListClient { }; public updateExceptionListItem = async ({ - _tags, _version, comments, description, @@ -304,12 +301,12 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, }: UpdateExceptionListItemOptions): Promise => { const { savedObjectsClient, user } = this; return updateExceptionListItem({ - _tags, _version, comments, description, @@ -319,6 +316,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index 963716b55ea77..1fef2da5d975e 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -30,6 +30,7 @@ import { Name, NameOrUndefined, NamespaceType, + OsTypeArray, PageOrUndefined, PerPageOrUndefined, SortFieldOrUndefined, @@ -39,8 +40,6 @@ import { UpdateCommentsArray, Version, VersionOrUndefined, - _Tags, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -56,7 +55,6 @@ export interface GetExceptionListOptions { } export interface CreateExceptionListOptions { - _tags: _Tags; listId: ListId; namespaceType: NamespaceType; name: Name; @@ -69,12 +67,12 @@ export interface CreateExceptionListOptions { } export interface UpdateExceptionListOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; id: IdOrUndefined; listId: ListIdOrUndefined; namespaceType: NamespaceType; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; @@ -116,13 +114,13 @@ export interface GetEndpointListItemOptions { } export interface CreateExceptionListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; entries: EntriesArray; itemId: ItemId; listId: ListId; namespaceType: NamespaceType; name: Name; + osTypes: OsTypeArray; description: Description; meta: MetaOrUndefined; tags: Tags; @@ -130,19 +128,18 @@ export interface CreateExceptionListItemOptions { } export interface CreateEndpointListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; entries: EntriesArray; itemId: ItemId; name: Name; description: Description; meta: MetaOrUndefined; + osTypes: OsTypeArray; tags: Tags; type: ExceptionListItemType; } export interface UpdateExceptionListItemOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArray; @@ -150,6 +147,7 @@ export interface UpdateExceptionListItemOptions { itemId: ItemIdOrUndefined; namespaceType: NamespaceType; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; @@ -157,13 +155,13 @@ export interface UpdateExceptionListItemOptions { } export interface UpdateEndpointListItemOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArray; id: IdOrUndefined; itemId: ItemIdOrUndefined; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts index c26ff1bca4484..a9a666672d7bb 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts @@ -16,9 +16,9 @@ import { MetaOrUndefined, NameOrUndefined, NamespaceType, + OsTypeArray, TagsOrUndefined, VersionOrUndefined, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -27,12 +27,12 @@ import { getExceptionList } from './get_exception_list'; interface UpdateExceptionListOptions { id: IdOrUndefined; - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; + osTypes: OsTypeArray; listId: ListIdOrUndefined; meta: MetaOrUndefined; user: string; @@ -43,7 +43,6 @@ interface UpdateExceptionListOptions { } export const updateExceptionList = async ({ - _tags, _version, id, savedObjectsClient, @@ -67,7 +66,6 @@ export const updateExceptionList = async ({ savedObjectType, exceptionList.id, { - _tags, description, meta, name, diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts index ccb74e8796705..9c3399b7509a5 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts @@ -17,9 +17,9 @@ import { MetaOrUndefined, NameOrUndefined, NamespaceType, + OsTypeArray, TagsOrUndefined, UpdateCommentsArrayOrUndefined, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -33,13 +33,13 @@ import { getExceptionListItem } from './get_exception_list_item'; interface UpdateExceptionListItemOptions { id: IdOrUndefined; comments: UpdateCommentsArrayOrUndefined; - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; entries: EntriesArray; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; + osTypes: OsTypeArray; itemId: ItemIdOrUndefined; meta: MetaOrUndefined; user: string; @@ -49,7 +49,6 @@ interface UpdateExceptionListItemOptions { } export const updateExceptionListItem = async ({ - _tags, _version, comments, entries, @@ -57,6 +56,7 @@ export const updateExceptionListItem = async ({ savedObjectsClient, namespaceType, name, + osTypes, description, itemId, meta, @@ -83,12 +83,12 @@ export const updateExceptionListItem = async ({ savedObjectType, exceptionListItem.id, { - _tags, comments: transformedComments, description, entries, meta, name, + os_types: osTypes, tags, type, updated_by: user, diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts index 2989a09b0ce00..6a7bd249bf62a 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/utils.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts @@ -71,7 +71,6 @@ export const transformSavedObjectToExceptionList = ({ version: _version, attributes: { /* eslint-disable @typescript-eslint/naming-convention */ - _tags, created_at, created_by, description, @@ -79,6 +78,7 @@ export const transformSavedObjectToExceptionList = ({ list_id, meta, name, + os_types, tags, tie_breaker_id, type, @@ -93,7 +93,6 @@ export const transformSavedObjectToExceptionList = ({ // TODO: Change this to do a decode and throw if the saved object is not as expected. // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { - _tags, _version, created_at, created_by, @@ -104,6 +103,7 @@ export const transformSavedObjectToExceptionList = ({ meta, name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types, tags, tie_breaker_id, type: exceptionListType.is(type) ? type : 'detection', @@ -124,11 +124,11 @@ export const transformSavedObjectUpdateToExceptionList = ({ const { version: _version, attributes: { - _tags, description, immutable, meta, name, + os_types: osTypes, tags, type, updated_by: updatedBy, @@ -141,7 +141,6 @@ export const transformSavedObjectUpdateToExceptionList = ({ // TODO: Change this to do a decode and throw if the saved object is not as expected. // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { - _tags: _tags ?? exceptionList._tags, _version, created_at: exceptionList.created_at, created_by: exceptionList.created_by, @@ -152,6 +151,7 @@ export const transformSavedObjectUpdateToExceptionList = ({ meta: meta ?? exceptionList.meta, name: name ?? exceptionList.name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types: osTypes ?? exceptionList.os_types, tags: tags ?? exceptionList.tags, tie_breaker_id: exceptionList.tie_breaker_id, type: exceptionListType.is(type) ? type : exceptionList.type, @@ -171,7 +171,6 @@ export const transformSavedObjectToExceptionListItem = ({ version: _version, attributes: { /* eslint-disable @typescript-eslint/naming-convention */ - _tags, comments, created_at, created_by, @@ -181,6 +180,7 @@ export const transformSavedObjectToExceptionListItem = ({ list_id, meta, name, + os_types, tags, tie_breaker_id, type, @@ -194,7 +194,6 @@ export const transformSavedObjectToExceptionListItem = ({ // TODO: Do a throw if after the decode this is not the correct "list_type: item" // TODO: Do a throw if item_id or entries is not defined. return { - _tags, _version, comments: comments ?? [], created_at, @@ -207,6 +206,7 @@ export const transformSavedObjectToExceptionListItem = ({ meta, name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types, tags, tie_breaker_id, type: exceptionListItemType.is(type) ? type : 'simple', @@ -226,12 +226,12 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ const { version: _version, attributes: { - _tags, comments, description, entries, meta, name, + os_types: osTypes, tags, type, updated_by: updatedBy, @@ -245,7 +245,6 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ // TODO: Update exception list and item types (perhaps separating out) so as to avoid // defaulting return { - _tags: _tags ?? exceptionListItem._tags, _version, comments: comments ?? exceptionListItem.comments, created_at: exceptionListItem.created_at, @@ -258,6 +257,7 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ meta: meta ?? exceptionListItem.meta, name: name ?? exceptionListItem.name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types: osTypes ?? exceptionListItem.os_types, tags: tags ?? exceptionListItem.tags, tie_breaker_id: exceptionListItem.tie_breaker_id, type: exceptionListItemType.is(type) ? type : exceptionListItem.type, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts new file mode 100644 index 0000000000000..6e23f31e8a994 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +import { DefaultArray } from './default_array'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../test_utils'; + +const testSchema = t.keyof({ + valid: true, + also_valid: true, +}); +type TestSchema = t.TypeOf; + +const defaultArraySchema = DefaultArray(testSchema); + +describe('default_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of testSchema', () => { + const payload: TestSchema[] = ['valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of valid testSchema strings', () => { + const payload = ['valid', 'also_valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['valid', 123]; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "123" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an array with an invalid string', () => { + const payload = ['valid', 'invalid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts new file mode 100644 index 0000000000000..8388eb315b8f4 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultArray as: + * - If undefined, then a default array will be set + * - If an array is sent in, then the array will be validated to ensure all elements are type C + */ +export const DefaultArray = (codec: C) => { + const arrType = t.array(codec); + type ArrType = t.TypeOf; + return new t.Type( + 'DefaultArray', + arrType.is, + (input, context): Either => + input == null ? t.success([]) : arrType.validate(input, context), + t.identity + ); +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts index 28a66d2948a92..e76dd3fca3740 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts @@ -5,6 +5,7 @@ */ export * from './default_actions_array'; +export * from './default_array'; export * from './default_boolean_false'; export * from './default_boolean_true'; export * from './default_empty_string'; diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index ef1d9a99b0aeb..352c628f9fa23 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => { os: 'windows', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -204,7 +204,7 @@ describe('When invoking Trusted Apps Schema', () => { field: 'process.hash.*', value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', }, - { field: 'process.executable.text', value: '/tmp/dir1' }, + { field: 'process.executable.caseless', value: '/tmp/dir1' }, ].forEach((partialEntry) => { const bodyMsg3 = { ...getCreateTrustedAppItem(), 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 25456115b3713..b4e837c472915 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 @@ -35,7 +35,7 @@ export const PostTrustedAppCreateRequestSchema = { schema.object({ field: schema.oneOf([ schema.literal('process.hash.*'), - schema.literal('process.executable.text'), + schema.literal('process.executable.caseless'), ]), type: schema.literal('match'), operator: schema.literal('included'), 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 75e0347b10078..3568136dd0e7b 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 @@ -33,7 +33,7 @@ export interface PostTrustedAppCreateResponse { } export interface MacosLinuxConditionEntry { - field: 'process.hash.*' | 'process.executable.text'; + field: 'process.hash.*' | 'process.executable.caseless'; type: 'match'; operator: 'included'; value: string; diff --git a/x-pack/plugins/security_solution/common/shared_exports.ts b/x-pack/plugins/security_solution/common/shared_exports.ts index bd1086a3f21e9..6269c3cee999c 100644 --- a/x-pack/plugins/security_solution/common/shared_exports.ts +++ b/x-pack/plugins/security_solution/common/shared_exports.ts @@ -5,6 +5,7 @@ */ export { NonEmptyString } from './detection_engine/schemas/types/non_empty_string'; +export { DefaultArray } from './detection_engine/schemas/types/default_array'; export { DefaultUuid } from './detection_engine/schemas/types/default_uuid'; export { DefaultStringArray } from './detection_engine/schemas/types/default_string_array'; export { diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index 564254b6a7596..bfe77d2f9e626 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -42,4 +42,6 @@ export { ExceptionListType, Type, ENDPOINT_LIST_ID, + osTypeArray, + OsTypeArray, } from '../../lists/common'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index ef2a5770eee8d..037462839c72d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -257,7 +257,7 @@ describe('When the add exception modal is opened', () => { indexPatterns: { ...stubIndexPattern, fields: [ - { name: 'file.path.text', type: 'string' }, + { name: 'file.path.caseless', type: 'string' }, { name: 'subject_name', type: 'string' }, { name: 'trusted', type: 'string' }, { name: 'file.hash.sha256', type: 'string' }, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index dee1db6482067..ad5bc98243467 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -30,6 +30,7 @@ import * as i18nCommon from '../../../translations'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; import { Ecs } from '../../../../../common/ecs'; +import { osTypeArray, OsTypeArray } from '../../../../../common/shared_imports'; import { useAppToasts } from '../../../hooks/use_app_toasts'; import { useKibana } from '../../../lib/kibana'; import { ExceptionBuilderComponent } from '../builder'; @@ -211,12 +212,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const initialExceptionItems = useMemo((): ExceptionsBuilderExceptionItem[] => { if (exceptionListType === 'endpoint' && alertData != null && ruleExceptionList) { - return defaultEndpointExceptionItems( - exceptionListType, - ruleExceptionList.list_id, - ruleName, - alertData - ); + return defaultEndpointExceptionItems(ruleExceptionList.list_id, ruleName, alertData); } else { return []; } @@ -265,11 +261,11 @@ export const AddExceptionModal = memo(function AddExceptionModal({ [setShouldBulkCloseAlert] ); - const retrieveAlertOsTypes = useCallback((): string[] => { - const osDefaults = ['windows', 'macos']; + const retrieveAlertOsTypes = useCallback((): OsTypeArray => { + const osDefaults: OsTypeArray = ['windows', 'macos']; if (alertData != null) { const osTypes = alertData.host && alertData.host.os && alertData.host.os.family; - if (osTypes != null && osTypes.length > 0) { + if (osTypeArray.is(osTypes) && osTypes != null && osTypes.length > 0) { return osTypes; } return osDefaults; @@ -316,13 +312,14 @@ export const AddExceptionModal = memo(function AddExceptionModal({ [fetchOrCreateListError, exceptionItemsToAdd] ); + const addExceptionMessage = + exceptionListType === 'endpoint' ? i18n.ADD_ENDPOINT_EXCEPTION : i18n.ADD_EXCEPTION; + return ( - - {exceptionListType === 'endpoint' ? i18n.ADD_ENDPOINT_EXCEPTION : i18n.ADD_EXCEPTION} - + {addExceptionMessage} {ruleName} @@ -429,7 +426,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ isDisabled={isSubmitButtonDisabled} fill > - {i18n.ADD_EXCEPTION} + {addExceptionMessage} )} diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx index 9bfd04cc19d72..2ee0fe88f73f7 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx @@ -90,9 +90,9 @@ const getMockNestedParentBuilderEntry = (): FormattedBuilderEntry => ({ const mockEndpointFields = [ { - name: 'file.path.text', + name: 'file.path.caseless', type: 'string', - esTypes: ['text'], + esTypes: ['keyword'], count: 0, scripted: false, searchable: true, @@ -303,8 +303,8 @@ describe('Exception builder helpers', () => { { aggregatable: false, count: 0, - esTypes: ['text'], - name: 'file.path.text', + esTypes: ['keyword'], + name: 'file.path.caseless', readFromDocValues: false, scripted: false, searchable: true, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx index 165f3314c2f15..5904e0034a51c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx @@ -234,13 +234,12 @@ export const ExceptionBuilderComponent = ({ // empty `entries` array. Thought about appending an entry item to one, but that // would then be arbitrary, decided to just create a new exception list item const newException = getNewExceptionItem({ - listType, listId, namespaceType: listNamespaceType, ruleName, }); setUpdateExceptions([...exceptions, { ...newException }]); - }, [setUpdateExceptions, exceptions, listType, listId, listNamespaceType, ruleName]); + }, [setUpdateExceptions, exceptions, listId, listNamespaceType, ruleName]); // The builder can have existing exception items, or new exception items that have yet // to be created (and thus lack an id), this was creating some React bugs with relying diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 128686428598c..08f7e3af90d0c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -40,7 +40,6 @@ import { AddExceptionComments } from '../add_exception_comments'; import { enrichExistingExceptionItemWithComments, enrichExceptionItemsWithOS, - getOperatingSystems, entryHasListType, entryHasNonEcsType, lowercaseHashValues, @@ -228,8 +227,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({ }, ]; if (exceptionListType === 'endpoint') { - const osTypes = exceptionItem._tags ? getOperatingSystems(exceptionItem._tags) : []; - enriched = lowercaseHashValues(enrichExceptionItemsWithOS(enriched, osTypes)); + enriched = lowercaseHashValues(enrichExceptionItemsWithOS(enriched, exceptionItem.os_types)); } return enriched; }, [exceptionItemsToAdd, exceptionItem, comment, exceptionListType]); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json index 037e340ee7fa2..2ea200466445b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json @@ -6,33 +6,33 @@ "Target.process.Ext.code_signature.valid", "Target.process.Ext.services", "Target.process.Ext.user", - "Target.process.command_line.text", - "Target.process.executable.text", + "Target.process.command_line.caseless", + "Target.process.executable.caseless", "Target.process.hash.md5", "Target.process.hash.sha1", "Target.process.hash.sha256", "Target.process.hash.sha512", - "Target.process.name.text", + "Target.process.name.caseless", "Target.process.parent.Ext.code_signature.status", "Target.process.parent.Ext.code_signature.subject_name", "Target.process.parent.Ext.code_signature.trusted", "Target.process.parent.Ext.code_signature.valid", - "Target.process.parent.command_line.text", - "Target.process.parent.executable.text", + "Target.process.parent.command_line.caseless", + "Target.process.parent.executable.caseless", "Target.process.parent.hash.md5", "Target.process.parent.hash.sha1", "Target.process.parent.hash.sha256", "Target.process.parent.hash.sha512", - "Target.process.parent.name.text", + "Target.process.parent.name.caseless", "Target.process.parent.pgid", - "Target.process.parent.working_directory.text", + "Target.process.parent.working_directory.caseless", "Target.process.pe.company", "Target.process.pe.description", "Target.process.pe.file_version", "Target.process.pe.original_file_name", "Target.process.pe.product", "Target.process.pgid", - "Target.process.working_directory.text", + "Target.process.working_directory.caseless", "agent.id", "agent.type", "agent.version", @@ -66,14 +66,14 @@ "file.mode", "file.name", "file.owner", - "file.path.text", + "file.path.caseless", "file.pe.company", "file.pe.description", "file.pe.file_version", "file.pe.original_file_name", "file.pe.product", "file.size", - "file.target_path.text", + "file.target_path.caseless", "file.type", "file.uid", "group.Ext.real.id", @@ -84,9 +84,9 @@ "host.id", "host.os.Ext.variant", "host.os.family", - "host.os.full.text", + "host.os.full.caseless", "host.os.kernel", - "host.os.name.text", + "host.os.name.caseless", "host.os.platform", "host.os.version", "host.type", @@ -96,33 +96,33 @@ "process.Ext.code_signature.valid", "process.Ext.services", "process.Ext.user", - "process.command_line.text", - "process.executable.text", + "process.command_line.caseless", + "process.executable.caseless", "process.hash.md5", "process.hash.sha1", "process.hash.sha256", "process.hash.sha512", - "process.name.text", + "process.name.caseless", "process.parent.Ext.code_signature.status", "process.parent.Ext.code_signature.subject_name", "process.parent.Ext.code_signature.trusted", "process.parent.Ext.code_signature.valid", - "process.parent.command_line.text", - "process.parent.executable.text", + "process.parent.command_line.caseless", + "process.parent.executable.caseless", "process.parent.hash.md5", "process.parent.hash.sha1", "process.parent.hash.sha256", "process.parent.hash.sha512", - "process.parent.name.text", + "process.parent.name.caseless", "process.parent.pgid", - "process.parent.working_directory.text", + "process.parent.working_directory.caseless", "process.pe.company", "process.pe.description", "process.pe.file_version", "process.pe.original_file_name", "process.pe.product", "process.pgid", - "process.working_directory.text", + "process.working_directory.caseless", "rule.uuid", "user.domain", "user.email", diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index 26fb460aee382..c89bde6d04dd3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -10,8 +10,6 @@ import moment from 'moment-timezone'; import { getOperatorType, getExceptionOperatorSelect, - getOperatingSystems, - getTagsInclude, getFormattedComments, filterExceptionItems, getNewExceptionItem, @@ -52,6 +50,7 @@ import { CreateExceptionListItemSchema, ExceptionListItemSchema, EntriesArray, + OsTypeArray, } from '../../../../../lists/common/schemas'; import { IIndexPattern } from 'src/plugins/data/common'; @@ -186,76 +185,18 @@ describe('Exception helpers', () => { }); }); - describe('#getOperatingSystems', () => { - test('it returns null if no operating system tag specified', () => { - const result = getOperatingSystems(['some tag', 'some other tag']); - - expect(result).toEqual([]); - }); - - test('it returns null if operating system tag malformed', () => { - const result = getOperatingSystems(['some tag', 'jibberos:mac,windows', 'some other tag']); - - expect(result).toEqual([]); - }); - - test('it returns operating systems if space included in os tag', () => { - const result = getOperatingSystems(['some tag', 'os: macos', 'some other tag']); - expect(result).toEqual(['macos']); - }); - - test('it returns operating systems if multiple os tags specified', () => { - const result = getOperatingSystems(['some tag', 'os: macos', 'some other tag', 'os:windows']); - expect(result).toEqual(['macos', 'windows']); - }); - }); - describe('#formatOperatingSystems', () => { test('it returns null if no operating system tag specified', () => { - const result = formatOperatingSystems(getOperatingSystems(['some tag', 'some other tag'])); - - expect(result).toEqual(''); - }); - - test('it returns null if operating system tag malformed', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'jibberos:mac,windows', 'some other tag']) - ); - + const result = formatOperatingSystems(['some os', 'some other os']); expect(result).toEqual(''); }); - test('it returns formatted operating systems if space included in os tag', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'os: macos', 'some other tag']) - ); - - expect(result).toEqual('macOS'); - }); - - test('it returns formatted operating systems if multiple os tags specified', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'os: macos', 'some other tag', 'os:windows']) - ); - + test('it returns formatted operating systems if multiple specified', () => { + const result = formatOperatingSystems(['some tag', 'macos', 'some other tag', 'windows']); expect(result).toEqual('macOS, Windows'); }); }); - describe('#getTagsInclude', () => { - test('it returns a tuple of "false" and "null" if no matches found', () => { - const result = getTagsInclude({ tags: ['some', 'tags', 'here'], regex: /(no match)/ }); - - expect(result).toEqual([false, null]); - }); - - test('it returns a tuple of "true" and matching string if matches found', () => { - const result = getTagsInclude({ tags: ['some', 'tags', 'here'], regex: /(some)/ }); - - expect(result).toEqual([true, 'some']); - }); - }); - describe('#getFormattedComments', () => { test('it returns formatted comment object with username and timestamp', () => { const payload = getCommentsArrayMock(); @@ -384,7 +325,6 @@ describe('Exception helpers', () => { test('it removes `temporaryId` from items', () => { const { meta, ...rest } = getNewExceptionItem({ - listType: 'detection', listId: '123', namespaceType: 'single', ruleName: 'rule name', @@ -400,7 +340,6 @@ describe('Exception helpers', () => { const payload = getExceptionListItemSchemaMock(); const result = formatExceptionItemForUpdate(payload); const expected = { - _tags: ['endpoint', 'process', 'malware', 'os:linux'], comments: [], description: 'some description', entries: ENTRIES, @@ -409,6 +348,7 @@ describe('Exception helpers', () => { meta: {}, name: 'some name', namespace_type: 'single', + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], type: 'simple', }; @@ -489,14 +429,14 @@ describe('Exception helpers', () => { }); describe('#enrichExceptionItemsWithOS', () => { - test('it should add an os tag to an exception item', () => { + test('it should add an os to an exception item', () => { const payload = [getExceptionListItemSchemaMock()]; - const osTypes = ['windows']; + const osTypes: OsTypeArray = ['windows']; const result = enrichExceptionItemsWithOS(payload, osTypes); const expected = [ { ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows'], + os_types: ['windows'], }, ]; expect(result).toEqual(expected); @@ -504,36 +444,16 @@ describe('Exception helpers', () => { test('it should add multiple os tags to all exception items', () => { const payload = [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()]; - const osTypes = ['windows', 'macos']; - const result = enrichExceptionItemsWithOS(payload, osTypes); - const expected = [ - { - ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows', 'os:macos'], - }, - { - ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows', 'os:macos'], - }, - ]; - expect(result).toEqual(expected); - }); - - test('it should add os tag to all exception items without duplication', () => { - const payload = [ - { ...getExceptionListItemSchemaMock(), _tags: ['os:linux', 'os:windows'] }, - { ...getExceptionListItemSchemaMock(), _tags: ['os:linux'] }, - ]; - const osTypes = ['windows']; + const osTypes: OsTypeArray = ['windows', 'macos']; const result = enrichExceptionItemsWithOS(payload, osTypes); const expected = [ { ...getExceptionListItemSchemaMock(), - _tags: ['os:linux', 'os:windows'], + os_types: ['windows', 'macos'], }, { ...getExceptionListItemSchemaMock(), - _tags: ['os:linux', 'os:windows'], + os_types: ['windows', 'macos'], }, ]; expect(result).toEqual(expected); @@ -715,7 +635,6 @@ describe('Exception helpers', () => { describe('getPrepopulatedItem', () => { test('it returns prepopulated items', () => { const prepopulatedItem = getPrepopulatedItem({ - listType: 'endpoint', listId: 'some_id', ruleName: 'my rule', codeSignature: { subjectName: '', trusted: '' }, @@ -733,7 +652,7 @@ describe('Exception helpers', () => { field: 'file.Ext.code_signature', type: 'nested', }, - { field: 'file.path.text', operator: 'included', type: 'match', value: '' }, + { field: 'file.path.caseless', operator: 'included', type: 'match', value: '' }, { field: 'file.hash.sha256', operator: 'included', type: 'match', value: '' }, { field: 'event.code', operator: 'included', type: 'match', value: '' }, ]); @@ -741,7 +660,6 @@ describe('Exception helpers', () => { test('it returns prepopulated items with values', () => { const prepopulatedItem = getPrepopulatedItem({ - listType: 'endpoint', listId: 'some_id', ruleName: 'my rule', codeSignature: { subjectName: 'someSubjectName', trusted: 'false' }, @@ -764,7 +682,12 @@ describe('Exception helpers', () => { field: 'file.Ext.code_signature', type: 'nested', }, - { field: 'file.path.text', operator: 'included', type: 'match', value: 'some-file-path' }, + { + field: 'file.path.caseless', + operator: 'included', + type: 'match', + value: 'some-file-path', + }, { field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some-hash' }, { field: 'event.code', operator: 'included', type: 'match', value: 'some-event-code' }, ]); @@ -847,7 +770,7 @@ describe('Exception helpers', () => { describe('defaultEndpointExceptionItems', () => { test('it should return pre-populated items', () => { - const defaultItems = defaultEndpointExceptionItems('endpoint', 'list_id', 'my_rule', { + const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', { _id: '123', file: { Ext: { @@ -881,7 +804,7 @@ describe('Exception helpers', () => { type: 'nested', }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: 'some file path', @@ -904,7 +827,7 @@ describe('Exception helpers', () => { type: 'nested', }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: 'some file path', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index d4acfa39f995d..684f3390ae41a 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiText, EuiCommentProps, EuiAvatar } from '@elastic/eui'; -import { capitalize, union } from 'lodash'; +import { capitalize } from 'lodash'; import moment from 'moment'; import uuid from 'uuid'; @@ -33,8 +33,8 @@ import { createExceptionListItemSchema, exceptionListItemSchema, UpdateExceptionListItemSchema, - ExceptionListType, EntryNested, + OsTypeArray, } from '../../../shared_imports'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; import { validate } from '../../../../common/validate'; @@ -98,20 +98,12 @@ export const getEntryValue = (item: BuilderEntry): string | string[] | undefined } }; -/** - * Retrieves the values of tags marked as os - * - * @param tags an ExceptionItem's tags - */ -export const getOperatingSystems = (tags: string[]): string[] => { - return tags.filter((tag) => tag.startsWith('os:')).map((os) => os.substring(3).trim()); -}; - /** * Formats os value array to a displayable string */ export const formatOperatingSystems = (osTypes: string[]): string => { return osTypes + .filter((os) => ['linux', 'macos', 'windows'].includes(os)) .map((os) => { if (os === 'macos') { return 'macOS'; @@ -121,21 +113,6 @@ export const formatOperatingSystems = (osTypes: string[]): string => { .join(', '); }; -/** - * Returns all tags that match a given regex - */ -export const getTagsInclude = ({ - tags, - regex, -}: { - tags: string[]; - regex: RegExp; -}): [boolean, string | null] => { - const matches: string[] | null = tags.join(';').match(regex); - const match = matches != null ? matches[1] : null; - return [matches != null, match]; -}; - /** * Formats ExceptionItem.comments into EuiCommentList format * @@ -158,18 +135,15 @@ export const getFormattedComments = (comments: CommentsArray): EuiCommentProps[] })); export const getNewExceptionItem = ({ - listType, listId, namespaceType, ruleName, }: { - listType: ExceptionListType; listId: string; namespaceType: NamespaceType; ruleName: string; }): CreateExceptionListItemBuilderSchema => { return { - _tags: [listType], comments: [], description: `${ruleName} - exception list item`, entries: [ @@ -326,14 +300,12 @@ export const enrichExistingExceptionItemWithComments = ( */ export const enrichExceptionItemsWithOS = ( exceptionItems: Array, - osTypes: string[] + osTypes: OsTypeArray ): Array => { - const osTags = osTypes.map((os) => `os:${os}`); return exceptionItems.map((item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { - const newTags = item._tags ? union(item._tags, osTags) : [...osTags]; return { ...item, - _tags: newTags, + os_types: osTypes, }; }); }; @@ -419,7 +391,6 @@ export const getCodeSignatureValue = ( * Returns the default values from the alert data to autofill new endpoint exceptions */ export const getPrepopulatedItem = ({ - listType, listId, ruleName, codeSignature, @@ -428,7 +399,6 @@ export const getPrepopulatedItem = ({ eventCode, listNamespace = 'agnostic', }: { - listType: ExceptionListType; listId: string; listNamespace?: NamespaceType; ruleName: string; @@ -438,7 +408,7 @@ export const getPrepopulatedItem = ({ eventCode: string; }): ExceptionsBuilderExceptionItem => { return { - ...getNewExceptionItem({ listType, listId, namespaceType: listNamespace, ruleName }), + ...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }), entries: [ { field: 'file.Ext.code_signature', @@ -459,7 +429,7 @@ export const getPrepopulatedItem = ({ ], }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: filePath ?? '', @@ -514,7 +484,6 @@ export const entryHasNonEcsType = ( * Returns the default values from the alert data to autofill new endpoint exceptions */ export const defaultEndpointExceptionItems = ( - listType: ExceptionListType, listId: string, ruleName: string, alertEcsData: Ecs @@ -523,7 +492,6 @@ export const defaultEndpointExceptionItems = ( return getCodeSignatureValue(alertEcsData).map((codeSignature) => getPrepopulatedItem({ - listType, listId, ruleName, filePath: file && file.path ? file.path[0] : '', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx index 944631d4e9fb5..38cf5722fa894 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx @@ -82,7 +82,6 @@ export const useFetchOrCreateRuleExceptionList = ({ type: exceptionListType, namespace_type: 'single', list_id: undefined, - _tags: undefined, tags: undefined, meta: undefined, }; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx index a540a34b70677..e9148ca44d706 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx @@ -43,7 +43,6 @@ storiesOf('Components|ExceptionItem', module) }) .add('with description', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.comments = []; payload.entries = [ { @@ -66,7 +65,6 @@ storiesOf('Components|ExceptionItem', module) }) .add('with comments', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; payload.comments = getCommentsArrayMock(); payload.entries = [ @@ -90,7 +88,6 @@ storiesOf('Components|ExceptionItem', module) }) .add('with nested entries', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; payload.comments = []; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx index 5f6e54b0d3cff..dbd4c805aa950 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx @@ -175,10 +175,13 @@ describe('Exception viewer helpers', () => { test('it returns formatted description list with a description if one specified', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = 'Im a description'; const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ + { + description: 'Linux', + title: 'OS', + }, { description: 'April 20th 2020 @ 15:25:31', title: 'Date created', @@ -198,10 +201,13 @@ describe('Exception viewer helpers', () => { test('it returns just user and date created if no other fields specified', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ + { + description: 'Linux', + title: 'OS', + }, { description: 'April 20th 2020 @ 15:25:31', title: 'Date created', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx index 86b0512410e6f..edc3d20b03e5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx @@ -6,12 +6,7 @@ import moment from 'moment'; import { entriesNested, ExceptionListItemSchema } from '../../../../lists_plugin_deps'; -import { - getEntryValue, - getExceptionOperatorSelect, - formatOperatingSystems, - getOperatingSystems, -} from '../helpers'; +import { getEntryValue, getExceptionOperatorSelect, formatOperatingSystems } from '../helpers'; import { FormattedEntry, BuilderEntry, DescriptionListItem } from '../types'; import * as i18n from '../translations'; @@ -80,7 +75,7 @@ export const getDescriptionListContent = ( const details = [ { title: i18n.OPERATING_SYSTEM, - value: formatOperatingSystems(getOperatingSystems(exceptionItem._tags ?? [])), + value: formatOperatingSystems(exceptionItem.os_types), }, { title: i18n.DATE_CREATED, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx index 7d30e81898cf2..60abcc2eeeefc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx @@ -83,7 +83,7 @@ export const ConditionEntry = memo( 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.path', { defaultMessage: 'Path' } ), - value: 'process.executable.text', + value: 'process.executable.caseless', }, ]; }, []); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx index 4b64030a702c5..1959e040d1860 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx @@ -30,7 +30,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: '/some/path/on/file/system', @@ -44,7 +44,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: '/some/path/on/file/system', diff --git a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts index 3bd27259ad80c..03f0bf94a4264 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts @@ -67,7 +67,7 @@ const generateTrustedAppEntry: (options?: GenerateTrustedAppEntryOptions) => obj return { list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, item_id: `generator_endpoint_trusted_apps_${generateUUID()}`, - _tags: ['endpoint', `os:${os}`], + os_types: [os], tags: ['user added string for a tag', 'malware'], type: 'simple', description: 'This is a sample agnostic endpoint trusted app entry', diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts index a10ba9d6be38c..c1b97f2adfeab 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts @@ -62,7 +62,7 @@ describe('buildEventTypeSignal', () => { test('it should convert simple fields', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { field: 'host.hostname', operator: 'included', type: 'match', value: 'estc' }, ]; @@ -71,10 +71,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_cased', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -108,10 +108,10 @@ describe('buildEventTypeSignal', () => { test('it should convert fields case sensitive', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { - field: 'host.hostname.text', + field: 'host.hostname.caseless', operator: 'included', type: 'match_any', value: ['estc', 'kibana'], @@ -122,10 +122,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -159,12 +159,12 @@ describe('buildEventTypeSignal', () => { test('it should deduplicate exception entries', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { - field: 'host.hostname.text', + field: 'host.hostname', operator: 'included', type: 'match_any', value: ['estc', 'kibana'], @@ -175,10 +175,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -189,7 +189,7 @@ describe('buildEventTypeSignal', () => { { field: 'host.hostname', operator: 'included', - type: 'exact_caseless_any', + type: 'exact_cased_any', value: ['estc', 'kibana'], }, ], @@ -264,7 +264,7 @@ describe('buildEventTypeSignal', () => { test('it should deduplicate exception items', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, ]; @@ -272,10 +272,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -308,9 +308,9 @@ describe('buildEventTypeSignal', () => { test('it should ignore unsupported entries', async () => { // Lists and exists are not supported by the Endpoint const testEntries: EntriesArray = [ - { field: 'server.domain', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full', operator: 'included', type: 'match', value: 'windows' }, { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'list', list: { @@ -325,10 +325,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_cased', - value: 'DOMAIN', + value: 'windows', }, ], }; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts index 731b083f3293c..d0fd38c4f1af1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts @@ -88,7 +88,7 @@ export async function getFullEndpointExceptionList( const response = await eClient.findExceptionListItem({ listId, namespaceType: 'agnostic', - filter: `exception-list-agnostic.attributes._tags:\"os:${os}\"`, + filter: `exception-list-agnostic.attributes.os_types:\"${os}\"`, perPage: 100, page, sortField: 'created_at', @@ -141,16 +141,16 @@ export function translateToEndpointExceptions( function getMatcherFunction(field: string, matchAny?: boolean): TranslatedEntryMatcher { return matchAny - ? field.endsWith('.text') + ? field.endsWith('.caseless') ? 'exact_caseless_any' : 'exact_cased_any' - : field.endsWith('.text') + : field.endsWith('.caseless') ? 'exact_caseless' : 'exact_cased'; } function normalizeFieldName(field: string): string { - return field.endsWith('.text') ? field.substring(0, field.length - 5) : field; + return field.endsWith('.caseless') ? field.substring(0, field.lastIndexOf('.')) : field; } function translateItem( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts index 9e9a35ea35318..0fc469fa62a80 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts @@ -133,7 +133,6 @@ describe('when invoking endpoint trusted apps route handlers', () => { const emptyResponse: FoundExceptionListItemSchema = { data: [ { - _tags: ['os:windows'], _version: undefined, comments: [], created_at: '2020-09-21T19:43:48.240Z', @@ -165,6 +164,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { meta: undefined, name: 'test', namespace_type: 'agnostic', + os_types: ['windows'], tags: [], tie_breaker_id: '1', type: 'simple', @@ -240,7 +240,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { os: 'windows', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -267,6 +267,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { return ({ ...getExceptionListItemSchemaMock(), ...newExceptionItem, + os_types: newExceptionItem.osTypes, } as unknown) as ExceptionListItemSchema; }); }); @@ -288,12 +289,11 @@ describe('when invoking endpoint trusted apps route handlers', () => { const request = createPostRequest(); await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0]).toEqual({ - _tags: ['os:windows'], comments: [], description: 'this one is ok', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -304,6 +304,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { meta: undefined, name: 'Some Anti-Virus App', namespaceType: 'agnostic', + osTypes: ['windows'], tags: [], type: 'simple', }); @@ -320,7 +321,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { description: 'this one is ok', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -357,7 +358,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { it('should trim condition entry values', async () => { const newTrustedApp = createNewTrustedAppBody(); newTrustedApp.entries.push({ - field: 'process.executable.text', + field: 'process.executable.caseless', value: '\n some value \r\n ', operator: 'included', type: 'match', @@ -366,13 +367,13 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', }, { - field: 'process.executable.text', + field: 'process.executable.caseless', value: 'some value', operator: 'included', type: 'match', @@ -392,7 +393,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts index 2b8129ab950c6..322d9a65162c0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts @@ -20,8 +20,8 @@ export const exceptionItemToTrustedAppItem = ( exceptionListItem: ExceptionListItemSchema ): TrustedApp => { // eslint-disable-next-line @typescript-eslint/naming-convention - const { entries, description, created_by, created_at, name, _tags, id } = exceptionListItem; - const os = osFromTagsList(_tags); + const { entries, description, created_by, created_at, name, os_types, id } = exceptionListItem; + const os = os_types.length ? os_types[0] : 'unknown'; return { entries: entries.map((entry) => { if (entry.field.startsWith('process.hash')) { @@ -41,19 +41,6 @@ export const exceptionItemToTrustedAppItem = ( } as TrustedApp; }; -/** - * Retrieves the OS entry from a list of tags (property returned with ExcptionListItem). - * For Trusted Apps each entry must have at MOST 1 OS. - * */ -const osFromTagsList = (tags: string[]): TrustedApp['os'] | 'unknown' => { - for (const tag of tags) { - if (tag.startsWith('os:')) { - return tag.substr(3) as TrustedApp['os']; - } - } - return 'unknown'; -}; - export const newTrustedAppItemToExceptionItem = ({ os, entries, @@ -61,7 +48,6 @@ export const newTrustedAppItemToExceptionItem = ({ description = '', }: NewTrustedApp): NewExceptionItem => { return { - _tags: tagsListFromOs(os), comments: [], description, // @ts-ignore @@ -83,15 +69,12 @@ export const newTrustedAppItemToExceptionItem = ({ meta: undefined, name: name.trim(), namespaceType: 'agnostic', + osTypes: [os], tags: [], type: 'simple', }; }; -const tagsListFromOs = (os: NewTrustedApp['os']): NewExceptionItem['_tags'] => { - return [`os:${os}`]; -}; - const hashType = (hash: string): 'md5' | 'sha256' | 'sha1' | undefined => { switch (hash.length) { case 32: