From 83a59bc5562d18150139c593a4ce896400836406 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 19 Feb 2021 21:35:15 -0500 Subject: [PATCH] [Security Solution][Detections] Adds more granular validation for nested fields (#92041) (#92107) Co-authored-by: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> --- .../types/non_empty_nested_entries_array.ts | 3 +- x-pack/plugins/lists/common/shared_exports.ts | 1 + .../common/shared_imports.ts | 1 + .../components/exceptions/helpers.test.tsx | 46 +++++++++++++++++++ .../common/components/exceptions/helpers.tsx | 34 ++++++++++---- 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts index 0b5b49c2715d4..722f5dd600eec 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_nested_entries_array.ts @@ -12,7 +12,8 @@ import { entriesMatchAny } from './entry_match_any'; import { entriesMatch } from './entry_match'; import { entriesExists } from './entry_exists'; -export const nestedEntriesArray = t.array(t.union([entriesMatch, entriesMatchAny, entriesExists])); +export const nestedEntryItem = t.union([entriesMatch, entriesMatchAny, entriesExists]); +export const nestedEntriesArray = t.array(nestedEntryItem); export type NestedEntriesArray = t.TypeOf; /** diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts index 6dcda5d1f8c24..4c4ee19d29bcd 100644 --- a/x-pack/plugins/lists/common/shared_exports.ts +++ b/x-pack/plugins/lists/common/shared_exports.ts @@ -36,6 +36,7 @@ export { listSchema, entry, entriesNested, + nestedEntryItem, entriesMatch, entriesMatchAny, entriesExists, diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index 988f0ad0c125d..94afbb948bf42 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -35,6 +35,7 @@ export { listSchema, entry, entriesNested, + nestedEntryItem, entriesMatch, entriesMatchAny, entriesExists, 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 b09ad60b239de..fdf7594a550a2 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 @@ -326,6 +326,52 @@ describe('Exception helpers', () => { expect(output).toEqual([{ ...getExceptionListItemSchemaMock() }]); }); + test('it removes the "nested" entry entries with "value" of empty string', () => { + const { entries, ...rest } = { ...getExceptionListItemSchemaMock() }; + const mockEmptyException: EntryNested = { + field: 'host.name', + type: OperatorTypeEnum.NESTED, + entries: [getEntryMatchMock(), { ...getEntryMatchMock(), value: '' }], + }; + const output: Array< + ExceptionListItemSchema | CreateExceptionListItemSchema + > = filterExceptionItems([ + { + ...rest, + entries: [...entries, mockEmptyException], + }, + ]); + + expect(output).toEqual([ + { + ...getExceptionListItemSchemaMock(), + entries: [ + ...getExceptionListItemSchemaMock().entries, + { ...mockEmptyException, entries: [getEntryMatchMock()] }, + ], + }, + ]); + }); + + test('it removes the "nested" entry item if all its entries are invalid', () => { + const { entries, ...rest } = { ...getExceptionListItemSchemaMock() }; + const mockEmptyException: EntryNested = { + field: 'host.name', + type: OperatorTypeEnum.NESTED, + entries: [{ ...getEntryMatchMock(), value: '' }], + }; + const output: Array< + ExceptionListItemSchema | CreateExceptionListItemSchema + > = filterExceptionItems([ + { + ...rest, + entries: [...entries, mockEmptyException], + }, + ]); + + expect(output).toEqual([{ ...getExceptionListItemSchemaMock() }]); + }); + test('it removes `temporaryId` from items', () => { const { meta, ...rest } = getNewExceptionItem({ listId: '123', 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 507fd51a90486..13ee06e8cbac9 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 @@ -32,6 +32,7 @@ import { comment, entry, entriesNested, + nestedEntryItem, createExceptionListItemSchema, exceptionListItemSchema, UpdateExceptionListItemSchema, @@ -173,16 +174,31 @@ export const filterExceptionItems = ( ): Array => { return exceptions.reduce>( (acc, exception) => { - const entries = exception.entries.filter((t) => { - const [validatedEntry] = validate(t, entry); - const [validatedNestedEntry] = validate(t, entriesNested); + const entries = exception.entries.reduce((nestedAcc, singleEntry) => { + if (singleEntry.type === 'nested') { + const nestedEntriesArray = singleEntry.entries.filter((singleNestedEntry) => { + const [validatedNestedEntry] = validate(singleNestedEntry, nestedEntryItem); + return validatedNestedEntry != null; + }); + + const [validatedNestedEntry] = validate( + { ...singleEntry, entries: nestedEntriesArray }, + entriesNested + ); + + if (validatedNestedEntry != null) { + return [...nestedAcc, validatedNestedEntry]; + } + return nestedAcc; + } else { + const [validatedEntry] = validate(singleEntry, entry); - if (validatedEntry != null || validatedNestedEntry != null) { - return true; + if (validatedEntry != null) { + return [...nestedAcc, validatedEntry]; + } + return nestedAcc; } - - return false; - }); + }, []); const item = { ...exception, entries }; @@ -401,7 +417,7 @@ export const getCodeSignatureValue = ( return codeSignature.map((signature) => { return { subjectName: signature.subject_name ?? '', - trusted: signature.trusted ?? '', + trusted: signature.trusted.toString() ?? '', }; }); } else {