[Security Solution][Endpoint] Adapt exception list api calls to versioned router (#165658)

## Summary

- Adds version to http calls for endpoint exceptions at Lists API.
- Fixes unit test.

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
David Sánchez 2023-09-07 17:35:15 +02:00 committed by GitHub
parent 84275f0c29
commit 6a0fa94cbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 85 additions and 3 deletions

View file

@ -43,6 +43,8 @@ import {
} from '@kbn/securitysolution-list-constants';
import { toError, toPromise } from '../fp_utils';
const version = '2023-10-31';
/**
* Add new ExceptionList
*
@ -62,6 +64,7 @@ const addExceptionList = async ({
body: JSON.stringify(list),
method: 'POST',
signal,
version,
});
const addExceptionListWithValidation = async ({
@ -105,6 +108,7 @@ const addExceptionListItem = async ({
body: JSON.stringify(listItem),
method: 'POST',
signal,
version,
});
const addExceptionListItemWithValidation = async ({
@ -148,6 +152,7 @@ const updateExceptionList = async ({
body: JSON.stringify(list),
method: 'PUT',
signal,
version,
});
const updateExceptionListWithValidation = async ({
@ -191,6 +196,7 @@ const updateExceptionListItem = async ({
body: JSON.stringify(listItem),
method: 'PUT',
signal,
version,
});
const updateExceptionListItemWithValidation = async ({
@ -247,6 +253,7 @@ const fetchExceptionLists = async ({
method: 'GET',
query,
signal,
version,
});
};
@ -298,6 +305,7 @@ const fetchExceptionListById = async ({
method: 'GET',
query: { id, namespace_type: namespaceType },
signal,
version,
});
const fetchExceptionListByIdWithValidation = async ({
@ -361,6 +369,7 @@ const fetchExceptionListsItemsByListIds = async ({
method: 'GET',
query,
signal,
version,
});
};
@ -414,6 +423,7 @@ const fetchExceptionListItemById = async ({
method: 'GET',
query: { id, namespace_type: namespaceType },
signal,
version,
});
const fetchExceptionListItemByIdWithValidation = async ({
@ -450,6 +460,7 @@ const deleteExceptionListById = async ({
method: 'DELETE',
query: { id, namespace_type: namespaceType },
signal,
version,
});
const deleteExceptionListByIdWithValidation = async ({
@ -486,6 +497,7 @@ const deleteExceptionListItemById = async ({
method: 'DELETE',
query: { id, namespace_type: namespaceType },
signal,
version,
});
const deleteExceptionListItemByIdWithValidation = async ({
@ -518,6 +530,7 @@ const addEndpointExceptionList = async ({
http.fetch<ExceptionListItemSchema>(ENDPOINT_LIST_URL, {
method: 'POST',
signal,
version,
});
const addEndpointExceptionListWithValidation = async ({
@ -561,6 +574,7 @@ export const exportExceptionList = async ({
include_expired_exceptions: includeExpiredExceptions,
},
signal,
version,
});
/**
@ -647,4 +661,5 @@ export const duplicateExceptionList = async ({
include_expired_exceptions: includeExpiredExceptions,
},
signal,
version,
});

View file

@ -57,6 +57,8 @@ export type {
ImportListParams,
} from './types';
const version = '2023-10-31';
const findLists = async ({
http,
cursor,
@ -79,6 +81,7 @@ const findLists = async ({
sort_order,
},
signal,
version,
});
};
@ -167,6 +170,7 @@ const importList = async ({
method: 'POST',
query: { list_id, type },
signal,
version,
});
};
@ -207,6 +211,7 @@ const deleteList = async ({
method: 'DELETE',
query: { deleteReferences, id, ignoreReferences },
signal,
version,
});
const deleteListWithValidation = async ({
@ -236,6 +241,7 @@ const exportList = async ({
method: 'POST',
query: { list_id },
signal,
version,
});
const exportListWithValidation = async ({
@ -256,6 +262,7 @@ const readListIndex = async ({ http, signal }: ApiParams): Promise<ListItemIndex
http.fetch<ListItemIndexExistSchema>(LIST_INDEX, {
method: 'GET',
signal,
version,
});
const readListIndexWithValidation = async ({
@ -273,15 +280,16 @@ export { readListIndexWithValidation as readListIndex };
// TODO add types and validation
export const readListPrivileges = async ({ http, signal }: ApiParams): Promise<unknown> =>
http.fetch<unknown>(LIST_PRIVILEGES_URL, {
version: '2023-10-31',
method: 'GET',
signal,
version,
});
const createListIndex = async ({ http, signal }: ApiParams): Promise<AcknowledgeSchema> =>
http.fetch<AcknowledgeSchema>(LIST_INDEX, {
method: 'POST',
signal,
version,
});
const createListIndexWithValidation = async ({

View file

@ -35,6 +35,7 @@ import { getFoundExceptionListSchemaMock } from '../../common/schemas/response/f
// TODO: This really belongs as: kbn-securitysolution-list-api/src/api/index.test.ts as soon as we can.
const abortCtrl = new AbortController();
const apiVersion = '2023-10-31';
describe('Exceptions Lists API', () => {
let httpMock: ReturnType<typeof coreMock.createStart>['http'];
@ -61,6 +62,7 @@ describe('Exceptions Lists API', () => {
body: JSON.stringify(payload),
method: 'POST',
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -109,6 +111,7 @@ describe('Exceptions Lists API', () => {
body: JSON.stringify(payload),
method: 'POST',
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -157,6 +160,7 @@ describe('Exceptions Lists API', () => {
body: JSON.stringify(payload),
method: 'PUT',
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -205,6 +209,7 @@ describe('Exceptions Lists API', () => {
body: JSON.stringify(payload),
method: 'PUT',
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -262,6 +267,7 @@ describe('Exceptions Lists API', () => {
sort_order: 'desc',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -300,6 +306,7 @@ describe('Exceptions Lists API', () => {
sort_order: 'desc',
},
signal: abortCtrl.signal,
version: apiVersion,
});
expect(exceptionResponse.data).toEqual([getExceptionListSchemaMock()]);
});
@ -344,6 +351,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -402,6 +410,7 @@ describe('Exceptions Lists API', () => {
sort_order: 'desc',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -430,6 +439,7 @@ describe('Exceptions Lists API', () => {
sort_order: 'desc',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -458,6 +468,7 @@ describe('Exceptions Lists API', () => {
sort_order: 'desc',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -519,6 +530,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -568,6 +580,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -617,6 +630,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -660,6 +674,7 @@ describe('Exceptions Lists API', () => {
expect(httpMock.fetch).toHaveBeenCalledWith('/api/endpoint_list', {
method: 'POST',
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -714,6 +729,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
@ -752,6 +768,7 @@ describe('Exceptions Lists API', () => {
namespace_type: 'single',
},
signal: abortCtrl.signal,
version: apiVersion,
});
});
});

View file

@ -17,6 +17,8 @@ import {
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks';
const apiVersion = '2023-10-31';
describe('Bulk delete artifact hook', () => {
let result: ReturnType<typeof useBulkDeleteArtifact>;
@ -56,6 +58,7 @@ describe('Bulk delete artifact hook', () => {
expect(onSuccessMock).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.delete).toHaveBeenCalledTimes(2);
expect(fakeHttpServices.delete).toHaveBeenNthCalledWith(1, '/api/exception_lists/items', {
version: apiVersion,
query: {
id: 'fakeId-1',
item_id: undefined,
@ -63,6 +66,7 @@ describe('Bulk delete artifact hook', () => {
},
});
expect(fakeHttpServices.delete).toHaveBeenNthCalledWith(2, '/api/exception_lists/items', {
version: apiVersion,
query: {
id: undefined,
item_id: 'fakeId-2',

View file

@ -17,6 +17,8 @@ import {
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks';
const apiVersion = '2023-10-31';
describe('Bulk update artifact hook', () => {
let result: ReturnType<typeof useBulkUpdateArtifact>;
@ -57,9 +59,11 @@ describe('Bulk update artifact hook', () => {
expect(fakeHttpServices.put).toHaveBeenCalledTimes(2);
expect(fakeHttpServices.put).toHaveBeenNthCalledWith(1, '/api/exception_lists/items', {
body: JSON.stringify(ExceptionsListApiClient.cleanExceptionsBeforeUpdate(exceptionItem1)),
version: apiVersion,
});
expect(fakeHttpServices.put).toHaveBeenNthCalledWith(2, '/api/exception_lists/items', {
body: JSON.stringify(ExceptionsListApiClient.cleanExceptionsBeforeUpdate(exceptionItem2)),
version: apiVersion,
});
});
});

View file

@ -54,6 +54,7 @@ describe('Create artifact hook', () => {
expect(onSuccessMock).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.post).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/exception_lists/items', {
version: '2023-10-31',
body: JSON.stringify(exceptionItem),
});
});

View file

@ -54,6 +54,7 @@ describe('Delete artifact hook', () => {
expect(onSuccessMock).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.delete).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.delete).toHaveBeenCalledWith('/api/exception_lists/items', {
version: '2023-10-31',
query: {
id: 'fakeId',
namespace_type: 'agnostic',

View file

@ -49,6 +49,7 @@ describe('Get artifact hook', () => {
expect(result.data).toBe(apiResponse);
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/items', {
version: '2023-10-31',
query: {
item_id: 'fakeId',
namespace_type: 'agnostic',

View file

@ -68,6 +68,7 @@ describe('List artifact hook', () => {
expect(result.data).toBe(apiResponse);
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/items/_find', {
version: '2023-10-31',
query: {
filter:
'((exception-list-agnostic.attributes.tags:"policy:policy-1" OR exception-list-agnostic.attributes.tags:"policy:all")) AND ((exception-list-agnostic.attributes.field-1:(*test*) OR exception-list-agnostic.attributes.field-1.field-2:(*test*) OR exception-list-agnostic.attributes.field-2:(*test*)))',

View file

@ -62,6 +62,7 @@ describe('Summary artifact hook', () => {
expect(result.data).toBe(apiResponse);
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/summary', {
version: '2023-10-31',
query: {
filter:
'((exception-list-agnostic.attributes.tags:"policy:policy-1" OR exception-list-agnostic.attributes.tags:"policy:all")) AND ((exception-list-agnostic.attributes.field-1:(*test*) OR exception-list-agnostic.attributes.field-1.field-2:(*test*) OR exception-list-agnostic.attributes.field-2:(*test*)))',

View file

@ -54,6 +54,7 @@ describe('Update artifact hook', () => {
expect(onSuccessMock).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.put).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.put).toHaveBeenCalledWith('/api/exception_lists/items', {
version: '2023-10-31',
body: JSON.stringify(ExceptionsListApiClient.cleanExceptionsBeforeUpdate(exceptionItem)),
});
});

View file

@ -115,6 +115,7 @@ describe('links', () => {
});
describe('Host Isolation Exception', () => {
const apiVersion = '2023-10-31';
it('should return HIE if user has access permission (licensed)', async () => {
(calculateEndpointAuthz as jest.Mock).mockReturnValue(
getEndpointAuthzInitialStateMock({ canAccessHostIsolationExceptions: true })
@ -154,6 +155,7 @@ describe('links', () => {
expect(filteredLinks).toEqual(getLinksWithout(SecurityPageName.hostIsolationExceptions));
expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/items/_find', {
version: apiVersion,
query: expect.objectContaining({
list_id: [ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id],
}),
@ -174,6 +176,7 @@ describe('links', () => {
expect(filteredLinks).toEqual(links);
expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/items/_find', {
version: apiVersion,
query: expect.objectContaining({
list_id: [ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id],
}),

View file

@ -57,7 +57,7 @@ export class EventFiltersApiClient extends ExceptionsListApiClient {
const result: string[] = await this.getHttp().post(
resolvePathVariables(SUGGESTIONS_ROUTE, { suggestion_type: 'eventFilters' }),
{
version: '2023-10-31',
version: this.version,
body: JSON.stringify(body),
}
);

View file

@ -110,6 +110,7 @@ describe.each(listType)('Policy details %s artifact delete modal', (type) => {
})
),
path: '/api/exception_lists/items',
version: '2023-10-31',
});
});
});

View file

@ -28,8 +28,11 @@ import { cleanEventFilterToUpdate } from '../../../../event_filters/service/serv
import { EventFiltersApiClient } from '../../../../event_filters/service/api_client';
import { POLICY_ARTIFACT_FLYOUT_LABELS } from './translations';
const apiVersion = '2023-10-31';
const getDefaultQueryParameters = (customFilter: string | undefined = '') => ({
path: '/api/exception_lists/items/_find',
version: apiVersion,
query: {
filter: customFilter,
list_id: ['endpoint_event_filters'],
@ -217,6 +220,7 @@ describe('Policy details artifacts flyout', () => {
// verify the request with the new tag
await waitFor(() => {
expect(mockedApi.responseProvider.eventFiltersUpdateOne).toHaveBeenCalledWith({
version: apiVersion,
body: JSON.stringify(
getCleanedExceptionWithNewTags(exceptions.data[0], testTags, policy)
),
@ -244,6 +248,7 @@ describe('Policy details artifacts flyout', () => {
await waitFor(() => {
// first exception
expect(mockedApi.responseProvider.eventFiltersUpdateOne).toHaveBeenCalledWith({
version: apiVersion,
body: JSON.stringify(
getCleanedExceptionWithNewTags(exceptions.data[0], testTags, policy)
),
@ -251,6 +256,7 @@ describe('Policy details artifacts flyout', () => {
});
// second exception
expect(mockedApi.responseProvider.eventFiltersUpdateOne).toHaveBeenCalledWith({
version: apiVersion,
body: JSON.stringify(
getCleanedExceptionWithNewTags(exceptions.data[0], testTags, policy)
),

View file

@ -25,6 +25,7 @@ import { EventFiltersApiClient } from '../../../../event_filters/service/api_cli
const endpointGenerator = new EndpointDocGenerator('seed');
const getDefaultQueryParameters = (customFilter: string | undefined = '') => ({
path: '/api/exception_lists/items/_find',
version: '2023-10-31',
query: {
filter: customFilter,
list_id: ['endpoint_event_filters'],

View file

@ -32,6 +32,8 @@ const getQueryParams = () => ({
sortOrder: 'asc',
});
const apiVersion = '2023-10-31';
describe('Exceptions List Api Client', () => {
let fakeCoreStart: jest.Mocked<CoreStart>;
let fakeHttpServices: jest.Mocked<HttpSetup>;
@ -136,6 +138,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
const expectedQueryParams = getQueryParams();
expect(fakeHttpServices.get).toHaveBeenCalledWith(`${EXCEPTION_LIST_ITEM_URL}/_find`, {
version: apiVersion,
query: {
page: expectedQueryParams.page,
per_page: expectedQueryParams.perPage,
@ -156,6 +159,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.get).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, {
version: apiVersion,
query: {
item_id: fakeItemId,
id: undefined,
@ -175,6 +179,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.post).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.post).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, {
version: apiVersion,
body: JSON.stringify(exceptionItem),
});
});
@ -202,6 +207,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.put).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.put).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, {
version: apiVersion,
body: JSON.stringify(ExceptionsListApiClient.cleanExceptionsBeforeUpdate(exceptionItem)),
});
});
@ -214,6 +220,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.delete).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.delete).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, {
version: apiVersion,
query: {
item_id: fakeItemId,
id: undefined,
@ -230,6 +237,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.get).toHaveBeenCalledTimes(1);
expect(fakeHttpServices.get).toHaveBeenCalledWith(`${EXCEPTION_LIST_URL}/summary`, {
version: apiVersion,
query: {
filter: fakeQklFilter,
list_id: getFakeListId(),
@ -248,6 +256,7 @@ describe('Exceptions List Api Client', () => {
await expect(exceptionsListApiClientInstance.hasData()).resolves.toBe(true);
expect(fakeHttpServices.get).toHaveBeenCalledWith(`${EXCEPTION_LIST_ITEM_URL}/_find`, {
version: apiVersion,
query: expect.objectContaining({
page: 1,
per_page: 1,

View file

@ -43,9 +43,11 @@ export class ExceptionsListApiClient {
T extends CreateExceptionListItemSchema | UpdateExceptionListItemSchema
>(
item: T
) => T
) => T,
public readonly version?: string
) {
this.ensureListExists = this.createExceptionList();
this.version = version ?? '2023-10-31';
}
/**
@ -184,6 +186,7 @@ export class ExceptionsListApiClient {
const result = await this.http.get<FoundExceptionListItemSchema>(
`${EXCEPTION_LIST_ITEM_URL}/_find`,
{
version: this.version,
query: {
page,
per_page: perPage,
@ -214,6 +217,7 @@ export class ExceptionsListApiClient {
await this.ensureListExists;
let result = await this.http.get<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, {
version: this.version,
query: {
id,
item_id: itemId,
@ -243,6 +247,7 @@ export class ExceptionsListApiClient {
}
return this.http.post<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, {
version: this.version,
body: JSON.stringify(transformedException),
});
}
@ -260,6 +265,7 @@ export class ExceptionsListApiClient {
}
return this.http.put<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, {
version: this.version,
body: JSON.stringify(
ExceptionsListApiClient.cleanExceptionsBeforeUpdate(transformedException)
),
@ -277,6 +283,7 @@ export class ExceptionsListApiClient {
await this.ensureListExists;
return this.http.delete<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, {
version: this.version,
query: {
id,
item_id: itemId,
@ -292,6 +299,7 @@ export class ExceptionsListApiClient {
async summary(filter?: string): Promise<ExceptionListSummarySchema> {
await this.ensureListExists;
return this.http.get<ExceptionListSummarySchema>(`${EXCEPTION_LIST_URL}/summary`, {
version: this.version,
query: {
filter,
list_id: this.listId,