Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Security Solution][Exceptions] - Fixes exceptions builder UI where invalid values can cause overwrites of other values #90634

Merged
merged 21 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7a35075
added ids to incoming exception item entries, no ui component changes…
yctercero Feb 5, 2021
ec75e51
added code to strip ids from outgoing exception items, no ui componen…
yctercero Feb 5, 2021
76e2f62
made updates to UI portion to now make use of the entry ids
yctercero Feb 5, 2021
2320f62
finicky types, attempting to get types right
yctercero Feb 8, 2021
520a662
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 10, 2021
6c9d3ec
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 11, 2021
a76e1b2
updating to work with nested
yctercero Feb 11, 2021
fe06f3a
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 12, 2021
5c6b7a8
updated types to remove so many castings
yctercero Feb 12, 2021
65ee43e
finalizing tests
yctercero Feb 12, 2021
d640f50
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 13, 2021
d63064b
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 23, 2021
602cfb6
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 23, 2021
259fc9d
fixed up tests
yctercero Feb 23, 2021
39933a3
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 23, 2021
a12f211
merged with master and fixed tests
yctercero Feb 23, 2021
e679318
cleanup of types
yctercero Feb 24, 2021
a4fd2f3
updated to use i18n in one throw
yctercero Feb 24, 2021
7825c7b
updated types to remove casting
yctercero Feb 24, 2021
ffb1bd2
Merge branch 'master' of github.com:elastic/kibana into react_keys_ex…
yctercero Feb 24, 2021
3e6e8b6
adding feedback
yctercero Feb 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion x-pack/plugins/lists/common/constants.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import moment from 'moment';

import { OsTypeArray } from './schemas/common';
import { EntriesArray } from './schemas/types';
import { EntriesArray, Entry, EntryMatch, EntryNested } from './schemas/types';
import { EndpointEntriesArray } from './schemas/types/endpoint';
export const DATE_NOW = '2020-04-20T15:25:31.830Z';
export const OLD_DATE_RELATIVE_TO_DATE_NOW = '2020-04-19T15:25:31.830Z';
Expand Down Expand Up @@ -72,6 +72,34 @@ export const ENDPOINT_ENTRIES: EndpointEntriesArray = [
},
{ field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' },
];
// ENTRIES_WITH_IDS should only be used to mock out functionality of a collection of transforms
// that are UI specific and useful for UI concerns that are inserted between the
// API and the actual user interface. In some ways these might be viewed as
// technical debt or to compensate for the differences and preferences
// of how ReactJS might prefer data vs. how we want to model data.
export const ENTRIES_WITH_IDS: EntriesArray = [
{
entries: [
{
field: 'nested.field',
id: '123',
operator: 'included',
type: 'match',
value: 'some value',
} as EntryMatch & { id: string },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, but you could create an EntryId to reuse the { id: string } pattern that occurs frequently. May not be worth it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, when I fixed mine id's up, I got the same feedback where the suggestion was I just do a cast and very carefully do the cast in a few areas from @rylnd as the lesser of the evils and only add the extra id in only a few typings and only where I needed it. That felt more right to get past this issue.

I did this type of thing very carefully only where I needed the id:

const maybeId: typeof item & { id?: string } = item;

Everywhere else I left the typings alone. I think @madirey 's suggestions are good as well, it's a bit of a balancing act and your choice in these areas.

Later if we push the id down to the data layer or in the REST routes we can make the id's part of the schema and be ok as well. This all looks good to me here as is though too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, yea I could definitely see that it could maybe be cleaned up. I tried to add comments where there were casts to give an explanation of it.

There's a number of refactors that I'd like to do to follow up. If it's ok, I think it could be addressed then?

],
field: 'some.parentField',
id: '123',
type: 'nested',
} as EntryNested & { id: string },
{
field: 'some.not.nested.field',
id: '123',
operator: 'included',
type: 'match',
value: 'some value',
} as Entry & { id: string },
];
export const ITEM_TYPE = 'simple';
export const OS_TYPES: OsTypeArray = ['windows'];
export const TAGS = [];
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/lists/common/shared_imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export {
DefaultStringArray,
DefaultVersionNumber,
DefaultVersionNumberDecoded,
addIdToItem,
removeIdFromItem,
exactCheck,
getPaths,
foldLeftRight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { act, renderHook } from '@testing-library/react-hooks';

import { ENTRIES_WITH_IDS } from '../../../common/constants.mock';
import { coreMock } from '../../../../../../src/core/public/mocks';
import * as api from '../api';
import { getCreateExceptionListItemSchemaMock } from '../../../common/schemas/request/create_exception_list_item_schema.mock';
Expand Down Expand Up @@ -69,21 +70,54 @@ describe('usePersistExceptionItem', () => {
});

test('it invokes "updateExceptionListItem" when payload has "id"', async () => {
const addExceptionItem = jest.spyOn(api, 'addExceptionListItem');
const updateExceptionItem = jest.spyOn(api, 'updateExceptionListItem');
const addExceptionListItem = jest.spyOn(api, 'addExceptionListItem');
const updateExceptionListItem = jest.spyOn(api, 'updateExceptionListItem');
await act(async () => {
const { result, waitForNextUpdate } = renderHook<
PersistHookProps,
ReturnPersistExceptionItem
>(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError }));

await waitForNextUpdate();
result.current[1](getUpdateExceptionListItemSchemaMock());
// NOTE: Take note here passing in an exception item where it's
// entries have been enriched with ids to ensure that they get stripped
// before the call goes through
result.current[1]({ ...getUpdateExceptionListItemSchemaMock(), entries: ENTRIES_WITH_IDS });
await waitForNextUpdate();

expect(result.current).toEqual([{ isLoading: false, isSaved: true }, result.current[1]]);
expect(addExceptionItem).not.toHaveBeenCalled();
expect(updateExceptionItem).toHaveBeenCalled();
expect(addExceptionListItem).not.toHaveBeenCalled();
expect(updateExceptionListItem).toHaveBeenCalledWith({
http: mockKibanaHttpService,
listItem: getUpdateExceptionListItemSchemaMock(),
signal: new AbortController().signal,
});
});
});

test('it invokes "addExceptionListItem" when payload does not have "id"', async () => {
const updateExceptionListItem = jest.spyOn(api, 'updateExceptionListItem');
const addExceptionListItem = jest.spyOn(api, 'addExceptionListItem');
await act(async () => {
const { result, waitForNextUpdate } = renderHook<
PersistHookProps,
ReturnPersistExceptionItem
>(() => usePersistExceptionItem({ http: mockKibanaHttpService, onError }));

await waitForNextUpdate();
// NOTE: Take note here passing in an exception item where it's
// entries have been enriched with ids to ensure that they get stripped
// before the call goes through
result.current[1]({ ...getCreateExceptionListItemSchemaMock(), entries: ENTRIES_WITH_IDS });
await waitForNextUpdate();

expect(result.current).toEqual([{ isLoading: false, isSaved: true }, result.current[1]]);
expect(updateExceptionListItem).not.toHaveBeenCalled();
expect(addExceptionListItem).toHaveBeenCalledWith({
http: mockKibanaHttpService,
listItem: getCreateExceptionListItemSchemaMock(),
signal: new AbortController().signal,
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@

import { Dispatch, useEffect, useState } from 'react';

import { UpdateExceptionListItemSchema } from '../../../common/schemas';
import {
CreateExceptionListItemSchema,
UpdateExceptionListItemSchema,
} from '../../../common/schemas';
import { addExceptionListItem, updateExceptionListItem } from '../api';
import { AddExceptionListItem, PersistHookProps } from '../types';
import { transformNewItemOutput, transformOutput } from '../transforms';
import { PersistHookProps } from '../types';

interface PersistReturnExceptionItem {
isLoading: boolean;
Expand All @@ -18,7 +22,7 @@ interface PersistReturnExceptionItem {

export type ReturnPersistExceptionItem = [
PersistReturnExceptionItem,
Dispatch<AddExceptionListItem | null>
Dispatch<CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null>
];

/**
Expand All @@ -32,7 +36,9 @@ export const usePersistExceptionItem = ({
http,
onError,
}: PersistHookProps): ReturnPersistExceptionItem => {
const [exceptionListItem, setExceptionItem] = useState<AddExceptionListItem | null>(null);
const [exceptionListItem, setExceptionItem] = useState<
CreateExceptionListItemSchema | UpdateExceptionListItemSchema | null
>(null);
const [isSaved, setIsSaved] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const isUpdateExceptionItem = (item: unknown): item is UpdateExceptionListItemSchema =>
Expand All @@ -47,16 +53,25 @@ export const usePersistExceptionItem = ({
if (exceptionListItem != null) {
try {
setIsLoading(true);

if (isUpdateExceptionItem(exceptionListItem)) {
// Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes
// for context around the temporary `id`
const transformedList = transformOutput(exceptionListItem);

await updateExceptionListItem({
http,
listItem: exceptionListItem,
listItem: transformedList,
signal: abortCtrl.signal,
});
} else {
// Please see `x-pack/plugins/lists/public/exceptions/transforms.ts` doc notes
// for context around the temporary `id`
const transformedList = transformNewItemOutput(exceptionListItem);

await addExceptionListItem({
http,
listItem: exceptionListItem,
listItem: transformedList,
signal: abortCtrl.signal,
});
}
Expand Down
Loading