[Security Solution][Detections] Adds more granular validation for nested fields (#92041) (#92108)

Co-authored-by: Davis Plumlee <56367316+dplumlee@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2021-02-19 21:30:22 -05:00 committed by GitHub
parent 970938c124
commit 9e13ac7aac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 10 deletions

View file

@ -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<typeof nestedEntriesArray>;
/**

View file

@ -36,6 +36,7 @@ export {
listSchema,
entry,
entriesNested,
nestedEntryItem,
entriesMatch,
entriesMatchAny,
entriesExists,

View file

@ -35,6 +35,7 @@ export {
listSchema,
entry,
entriesNested,
nestedEntryItem,
entriesMatch,
entriesMatchAny,
entriesExists,

View file

@ -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',

View file

@ -32,6 +32,7 @@ import {
comment,
entry,
entriesNested,
nestedEntryItem,
createExceptionListItemSchema,
exceptionListItemSchema,
UpdateExceptionListItemSchema,
@ -173,16 +174,31 @@ export const filterExceptionItems = (
): Array<ExceptionListItemSchema | CreateExceptionListItemSchema> => {
return exceptions.reduce<Array<ExceptionListItemSchema | CreateExceptionListItemSchema>>(
(acc, exception) => {
const entries = exception.entries.filter((t) => {
const [validatedEntry] = validate(t, entry);
const [validatedNestedEntry] = validate(t, entriesNested);
const entries = exception.entries.reduce<BuilderEntry[]>((nestedAcc, singleEntry) => {
if (singleEntry.type === 'nested') {
const nestedEntriesArray = singleEntry.entries.filter((singleNestedEntry) => {
const [validatedNestedEntry] = validate(singleNestedEntry, nestedEntryItem);
return validatedNestedEntry != null;
});
if (validatedEntry != null || validatedNestedEntry != null) {
return true;
const [validatedNestedEntry] = validate(
{ ...singleEntry, entries: nestedEntriesArray },
entriesNested
);
if (validatedNestedEntry != null) {
return [...nestedAcc, validatedNestedEntry];
}
return nestedAcc;
} else {
const [validatedEntry] = validate(singleEntry, entry);
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 {