Skip to content

Commit

Permalink
[Security Solution][Exceptions] - Initial updates to exceptions viewe…
Browse files Browse the repository at this point in the history
…r UX (elastic#138770)

## Summary

**API changes**
- Adds API for determining the list-rule references. 
- Updates the exception items find api to include the `search` param which allows for simple search queries - used with the EUI search bar

**UI updates**
- Moved the exception components into new `rule_exceptions` folder per suggested folder structure updates listed [here](elastic#138600)
- Updates the rule details tabs to split endpoint and rule exceptions into their own tabs
- Updates the viewer utilities header now that these different exception types are split
- Updates exception item UI to match new designs
- Updates the UI for when there are no items
- Removes `use_exception_list_items` hook as it is no longer in use
- Flyouts (add/edit) remain untouched
  • Loading branch information
yctercero authored and guskovaue committed Sep 12, 2022
1 parent 35593a8 commit 1e032df
Show file tree
Hide file tree
Showing 129 changed files with 4,861 additions and 3,941 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export * from './os_type';
export * from './page';
export * from './per_page';
export * from './pit';
export * from './search';
export * from './search_after';
export * from './serializer';
export * from './sort_field';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { exactCheck } from '@kbn/securitysolution-io-ts-utils';
import { searchOrUndefined } from '.';

import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';

import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';

describe('search', () => {
test('it will validate a correct search', () => {
const payload = 'name:foo';
const decoded = searchOrUndefined.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 validate with the value of "undefined"', () => {
const obj = t.exact(
t.type({
search: searchOrUndefined,
})
);
const payload: t.TypeOf<typeof obj> = {
search: undefined,
};
const decoded = obj.decode({
pit_id: undefined,
});
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 search', () => {
const payload = ['foo'];
const decoded = searchOrUndefined.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "["foo"]" supplied to "(string | undefined)"',
]);
expect(message.schema).toEqual({});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
* Side Public License, v 1.
*/

describe('useExceptionListItems', () => {
test('Tests should be ported', () => {
// TODO: Port all the tests from: x-pack/plugins/lists/public/exceptions/hooks/use_exception_list_items.test.ts here once mocks are figured out and kbn package mocks are figured out
expect(true).toBe(true);
});
});
import * as t from 'io-ts';

export const search = t.string;
export type Search = t.TypeOf<typeof search>;

export const searchOrUndefined = t.union([search, t.undefined]);
export type SearchOrUndefined = t.TypeOf<typeof searchOrUndefined>;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchem
namespace_type: NAMESPACE_TYPE,
page: '1',
per_page: '25',
search: undefined,
sort_field: undefined,
sort_order: undefined,
});
Expand All @@ -26,6 +27,7 @@ export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListI
namespace_type: 'single,single,agnostic',
page: '1',
per_page: '25',
search: undefined,
sort_field: undefined,
sort_order: undefined,
});
Expand All @@ -37,6 +39,7 @@ export const getFindExceptionListItemSchemaDecodedMock =
namespace_type: [NAMESPACE_TYPE],
page: 1,
per_page: 25,
search: undefined,
sort_field: undefined,
sort_order: undefined,
});
Expand All @@ -48,6 +51,7 @@ export const getFindExceptionListItemSchemaDecodedMultipleMock =
namespace_type: ['single', 'single', 'agnostic'],
page: 1,
per_page: 25,
search: undefined,
sort_field: undefined,
sort_order: undefined,
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('find_list_item_schema', () => {
namespace_type: ['single'],
page: undefined,
per_page: undefined,
search: undefined,
sort_field: undefined,
sort_order: undefined,
};
Expand All @@ -73,7 +74,7 @@ describe('find_list_item_schema', () => {
expect(message.schema).toEqual(expected);
});

test('it should validate with pre_page missing', () => {
test('it should validate with per_page missing', () => {
const payload = getFindExceptionListItemSchemaMock();
delete payload.per_page;
const decoded = findExceptionListItemSchema.decode(payload);
Expand Down Expand Up @@ -123,6 +124,18 @@ describe('find_list_item_schema', () => {
expect(message.schema).toEqual(expected);
});

test('it should validate with search missing', () => {
const payload = getFindExceptionListItemSchemaMock();
delete payload.search;
const decoded = findExceptionListItemSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
const expected = getFindExceptionListItemSchemaDecodedMock();
delete expected.search;
expect(message.schema).toEqual(expected);
});

test('it should not allow an extra key to be sent in', () => {
const payload: FindExceptionListItemSchema & {
extraKey: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { RequiredKeepUndefined } from '../../common/required_keep_undefined';
import { sort_field } from '../../common/sort_field';
import { sort_order } from '../../common/sort_order';
import { search } from '../../common/search';

export const findExceptionListItemSchema = t.intersection([
t.exact(
Expand All @@ -34,6 +35,7 @@ export const findExceptionListItemSchema = t.intersection([
namespace_type: DefaultNamespaceArray, // defaults to ['single'] if not set during decode
page: StringToPositiveNumber, // defaults to undefined if not set during decode
per_page: StringToPositiveNumber, // defaults to undefined if not set during decode
search,
sort_field, // defaults to undefined if not set during decode
sort_order, // defaults to undefined if not set during decode
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ export interface ExceptionListIdentifiers {

export interface ApiCallFindListsItemsMemoProps {
lists: ExceptionListIdentifiers[];
filterOptions: FilterExceptionsOptions[];
pagination: Partial<Pagination>;
showDetectionsListsOnly: boolean;
showEndpointListsOnly: boolean;
filter?: string;
onError: (arg: string[]) => void;
onSuccess: (arg: UseExceptionListItemsSuccess) => void;
}
Expand Down Expand Up @@ -168,8 +168,9 @@ export interface ApiCallByListIdProps {
http: HttpStart;
listIds: string[];
namespaceTypes: NamespaceType[];
filterOptions: FilterExceptionsOptions[];
pagination: Partial<Pagination>;
search?: string;
filter?: string;
signal: AbortSignal;
}

Expand Down
35 changes: 10 additions & 25 deletions packages/kbn-securitysolution-list-api/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ import {
import {
ENDPOINT_LIST_URL,
EXCEPTION_LIST_ITEM_URL,
EXCEPTION_LIST_NAMESPACE,
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
EXCEPTION_LIST_URL,
} from '@kbn/securitysolution-list-constants';
import { toError, toPromise } from '../fp_utils';
Expand Down Expand Up @@ -324,7 +322,8 @@ export { fetchExceptionListByIdWithValidation as fetchExceptionListById };
* @param http Kibana http service
* @param listIds ExceptionList list_ids (not ID)
* @param namespaceTypes ExceptionList namespace_types
* @param filterOptions optional - filter by field or tags
* @param search optional - simple search string
* @param filter optional
* @param pagination optional
* @param signal to cancel request
*
Expand All @@ -334,36 +333,20 @@ const fetchExceptionListsItemsByListIds = async ({
http,
listIds,
namespaceTypes,
filterOptions,
filter,
pagination,
search,
signal,
}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> => {
const filters: string = filterOptions
.map<string>((filter, index) => {
const namespace = namespaceTypes[index];
const filterNamespace =
namespace === 'agnostic' ? EXCEPTION_LIST_NAMESPACE_AGNOSTIC : EXCEPTION_LIST_NAMESPACE;
const formattedFilters = [
...(filter.filter.length
? [`${filterNamespace}.attributes.entries.field:${filter.filter}*`]
: []),
...(filter.tags.length
? filter.tags.map((t) => `${filterNamespace}.attributes.tags:${t}`)
: []),
];

return formattedFilters.join(' AND ');
})
.join(',');

const query = {
list_id: listIds.join(','),
namespace_type: namespaceTypes.join(','),
page: pagination.page ? `${pagination.page}` : '1',
per_page: pagination.perPage ? `${pagination.perPage}` : '20',
search,
sort_field: 'exception-list.created_at',
sort_order: 'desc',
...(filters.trim() !== '' ? { filter: filters } : {}),
filter,
};

return http.fetch<FoundExceptionListItemSchema>(`${EXCEPTION_LIST_ITEM_URL}/_find`, {
Expand All @@ -374,23 +357,25 @@ const fetchExceptionListsItemsByListIds = async ({
};

const fetchExceptionListsItemsByListIdsWithValidation = async ({
filterOptions,
filter,
http,
listIds,
namespaceTypes,
pagination,
search,
signal,
}: ApiCallByListIdProps): Promise<FoundExceptionListItemSchema> =>
flow(
() =>
tryCatch(
() =>
fetchExceptionListsItemsByListIds({
filterOptions,
filter,
http,
listIds,
namespaceTypes,
pagination,
search,
signal,
}),
toError
Expand Down
1 change: 0 additions & 1 deletion packages/kbn-securitysolution-list-hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export * from './src/use_api';
export * from './src/use_create_list_index';
export * from './src/use_cursor';
export * from './src/use_delete_list';
export * from './src/use_exception_list_items';
export * from './src/use_exception_lists';
export * from './src/use_export_list';
export * from './src/use_find_lists';
Expand Down
20 changes: 20 additions & 0 deletions packages/kbn-securitysolution-list-hooks/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './transforms';
export * from './use_api';
export * from './use_create_list_index';
export * from './use_cursor';
export * from './use_delete_list';
export * from './use_exception_lists';
export * from './use_export_list';
export * from './use_find_lists';
export * from './use_import_list';
export * from './use_persist_exception_item';
export * from './use_persist_exception_list';
export * from './use_read_list_index';
export * from './use_read_list_privileges';
4 changes: 2 additions & 2 deletions packages/kbn-securitysolution-list-hooks/src/use_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export const useApi = (http: HttpStart): ExceptionsApi => {
},
async getExceptionListsItems({
lists,
filterOptions,
filter,
pagination,
showDetectionsListsOnly,
showEndpointListsOnly,
Expand All @@ -192,7 +192,7 @@ export const useApi = (http: HttpStart): ExceptionsApi => {
per_page: perPage,
total,
} = await Api.fetchExceptionListsItemsByListIds({
filterOptions,
filter,
http,
listIds: ids,
namespaceTypes: namespaces,
Expand Down
Loading

0 comments on commit 1e032df

Please sign in to comment.