[Security Solution] Migrate lists plugin API to versioned router (#165160)

Closes https://github.com/elastic/security-team/issues/7176
This commit is contained in:
Marshall Main 2023-08-31 07:48:01 -07:00 committed by GitHub
parent deebbadc2d
commit 21879be883
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 2289 additions and 1995 deletions

View file

@ -581,6 +581,7 @@ export const getExceptionFilterFromExceptionListIds = async ({
}: GetExceptionFilterFromExceptionListIdsProps): Promise<ExceptionFilterResponse> =>
http.fetch(INTERNAL_EXCEPTION_FILTER, {
method: 'POST',
version: '1',
body: JSON.stringify({
exception_list_ids: exceptionListIds,
type: 'exception_list_ids',
@ -609,6 +610,7 @@ export const getExceptionFilterFromExceptions = async ({
}: GetExceptionFilterFromExceptionsProps): Promise<ExceptionFilterResponse> =>
http.fetch(INTERNAL_EXCEPTION_FILTER, {
method: 'POST',
version: '1',
body: JSON.stringify({
exceptions,
type: 'exception_items',

View file

@ -117,6 +117,7 @@ const findListsBySize = async ({
}: ApiParams & FindListSchemaEncoded): Promise<FoundListsBySizeSchema> => {
return http.fetch(`${INTERNAL_FIND_LISTS_BY_SIZE}`, {
method: 'GET',
version: '1',
query: {
cursor,
page,
@ -272,6 +273,7 @@ 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,
});

View file

@ -20,81 +20,88 @@ import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from
import { validateExceptionListSize } from './validate';
export const createEndpointListItemRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: ENDPOINT_LIST_ITEM_URL,
validate: {
body: buildRouteValidation<
typeof createEndpointListItemRequest,
CreateEndpointListItemRequestDecoded
>(createEndpointListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof createEndpointListItemRequest,
CreateEndpointListItemRequestDecoded
>(createEndpointListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
name,
tags,
meta,
comments,
description,
entries,
item_id: itemId,
os_types: osTypes,
type,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.getEndpointListItem({
id: undefined,
itemId,
});
if (exceptionListItem != null) {
return siemResponse.error({
body: `exception list item id: "${itemId}" already exists`,
statusCode: 409,
});
} else {
const createdList = await exceptionLists.createEndpointListItem({
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
name,
tags,
meta,
comments,
description,
entries,
itemId,
meta,
name,
osTypes,
tags,
item_id: itemId,
os_types: osTypes,
type,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.getEndpointListItem({
id: undefined,
itemId,
});
const [validated, errors] = validate(createdList, createEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (exceptionListItem != null) {
return siemResponse.error({
body: `exception list item id: "${itemId}" already exists`,
statusCode: 409,
});
} else {
const listSizeError = await validateExceptionListSize(
exceptionLists,
ENDPOINT_LIST_ID,
'agnostic'
);
if (listSizeError != null) {
await exceptionLists.deleteExceptionListItemById({
id: createdList.id,
namespaceType: 'agnostic',
});
return siemResponse.error(listSizeError);
const createdList = await exceptionLists.createEndpointListItem({
comments,
description,
entries,
itemId,
meta,
name,
osTypes,
tags,
type,
});
const [validated, errors] = validate(createdList, createEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
const listSizeError = await validateExceptionListSize(
exceptionLists,
ENDPOINT_LIST_ID,
'agnostic'
);
if (listSizeError != null) {
await exceptionLists.deleteExceptionListItemById({
id: createdList.id,
namespaceType: 'agnostic',
});
return siemResponse.error(listSizeError);
}
return response.ok({ body: validated ?? {} });
}
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -25,38 +25,43 @@ import { getExceptionListClient } from './utils/get_exception_list_client';
* @param router The router to use.
*/
export const createEndpointListRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: ENDPOINT_LIST_URL,
validate: false,
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const createdList = await exceptionLists.createEndpointList();
// We always return ok on a create endpoint list route but with an empty body as
// an additional fetch of the full list would be slower and the UI has everything hard coded
// within it to get the list if it needs details about it. Our goal is to be as fast as possible
// and block the least amount of time with this route since it could end up in various parts of the
// stack at some point such as repeatedly being called by endpoint agents.
const body = createdList ?? {};
const [validated, errors] = validate(body, createEndpointListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
})
.addVersion(
{
validate: false,
version: '2023-10-31',
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const createdList = await exceptionLists.createEndpointList();
// We always return ok on a create endpoint list route but with an empty body as
// an additional fetch of the full list would be slower and the UI has everything hard coded
// within it to get the list if it needs details about it. Our goal is to be as fast as possible
// and block the least amount of time with this route since it could end up in various parts of the
// stack at some point such as repeatedly being called by endpoint agents.
const body = createdList ?? {};
const [validated, errors] = validate(body, createEndpointListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -22,114 +22,121 @@ import { endpointDisallowedFields } from './endpoint_disallowed_fields';
import { validateEndpointExceptionItemEntries, validateExceptionListSize } from './validate';
export const createExceptionListItemRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_ITEM_URL,
validate: {
body: buildRouteValidation<
typeof createExceptionListItemRequest,
CreateExceptionListItemRequestDecoded
>(createExceptionListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof createExceptionListItemRequest,
CreateExceptionListItemRequestDecoded
>(createExceptionListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
namespace_type: namespaceType,
name,
tags,
meta,
comments,
description,
entries,
item_id: itemId,
list_id: listId,
os_types: osTypes,
type,
expire_time: expireTime,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionList = await exceptionLists.getExceptionList({
id: undefined,
listId,
namespaceType,
});
if (exceptionList == null) {
return siemResponse.error({
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
const exceptionListItem = await exceptionLists.getExceptionListItem({
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
namespace_type: namespaceType,
name,
tags,
meta,
comments,
description,
entries,
item_id: itemId,
list_id: listId,
os_types: osTypes,
type,
expire_time: expireTime,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionList = await exceptionLists.getExceptionList({
id: undefined,
itemId,
listId,
namespaceType,
});
if (exceptionListItem != null) {
if (exceptionList == null) {
return siemResponse.error({
body: `exception list item id: "${itemId}" already exists`,
statusCode: 409,
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
if (exceptionList.type === 'endpoint') {
const error = validateEndpointExceptionItemEntries(request.body.entries);
if (error != null) {
return siemResponse.error(error);
}
for (const entry of entries) {
if (endpointDisallowedFields.includes(entry.field)) {
return siemResponse.error({
body: `cannot add endpoint exception item on field ${entry.field}`,
statusCode: 400,
});
const exceptionListItem = await exceptionLists.getExceptionListItem({
id: undefined,
itemId,
namespaceType,
});
if (exceptionListItem != null) {
return siemResponse.error({
body: `exception list item id: "${itemId}" already exists`,
statusCode: 409,
});
} else {
if (exceptionList.type === 'endpoint') {
const error = validateEndpointExceptionItemEntries(request.body.entries);
if (error != null) {
return siemResponse.error(error);
}
for (const entry of entries) {
if (endpointDisallowedFields.includes(entry.field)) {
return siemResponse.error({
body: `cannot add endpoint exception item on field ${entry.field}`,
statusCode: 400,
});
}
}
}
}
const createdList = await exceptionLists.createExceptionListItem({
comments,
description,
entries,
expireTime,
itemId,
listId,
meta,
name,
namespaceType,
osTypes,
tags,
type,
});
const [validated, errors] = validate(createdList, createExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
const listSizeError = await validateExceptionListSize(
exceptionLists,
const createdList = await exceptionLists.createExceptionListItem({
comments,
description,
entries,
expireTime,
itemId,
listId,
namespaceType
);
if (listSizeError != null) {
await exceptionLists.deleteExceptionListItemById({
id: createdList.id,
namespaceType,
});
return siemResponse.error(listSizeError);
meta,
name,
namespaceType,
osTypes,
tags,
type,
});
const [validated, errors] = validate(createdList, createExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
const listSizeError = await validateExceptionListSize(
exceptionLists,
listId,
namespaceType
);
if (listSizeError != null) {
await exceptionLists.deleteExceptionListItemById({
id: createdList.id,
namespaceType,
});
return siemResponse.error(listSizeError);
}
return response.ok({ body: validated ?? {} });
}
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,30 +15,37 @@ import { createExceptionListHandler } from '../handlers/create_exception_list_ha
import { buildRouteValidation, buildSiemResponse } from './utils';
export const createExceptionListRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_URL,
validate: {
body: buildRouteValidation<
typeof createExceptionListRequest,
CreateExceptionListRequestDecoded
>(createExceptionListRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof createExceptionListRequest,
CreateExceptionListRequestDecoded
>(createExceptionListRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
return await createExceptionListHandler(context, request, response, siemResponse);
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
return await createExceptionListHandler(context, request, response, siemResponse);
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
}
);
);
};

View file

@ -24,55 +24,62 @@ import {
} from './utils';
export const deleteEndpointListItemRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: ENDPOINT_LIST_ITEM_URL,
validate: {
query: buildRouteValidation<
typeof deleteEndpointListItemRequestQuery,
DeleteEndpointListItemRequestQueryDecoded
>(deleteEndpointListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof deleteEndpointListItemRequestQuery,
DeleteEndpointListItemRequestQueryDecoded
>(deleteEndpointListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { item_id: itemId, id } = request.query;
if (itemId == null && id == null) {
return siemResponse.error({
body: 'Either "item_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const deleted = await exceptionLists.deleteEndpointListItem({
id,
itemId,
});
if (deleted == null) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { item_id: itemId, id } = request.query;
if (itemId == null && id == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
body: 'Either "item_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const [validated, errors] = validate(deleted, deleteEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const deleted = await exceptionLists.deleteEndpointListItem({
id,
itemId,
});
if (deleted == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(deleted, deleteEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,56 +24,63 @@ import {
} from './utils';
export const deleteExceptionListItemRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_ITEM_URL,
validate: {
query: buildRouteValidation<
typeof deleteExceptionListItemRequestQuery,
DeleteExceptionListItemRequestQueryDecoded
>(deleteExceptionListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof deleteExceptionListItemRequestQuery,
DeleteExceptionListItemRequestQueryDecoded
>(deleteExceptionListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { item_id: itemId, id, namespace_type: namespaceType } = request.query;
if (itemId == null && id == null) {
return siemResponse.error({
body: 'Either "item_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const deleted = await exceptionLists.deleteExceptionListItem({
id,
itemId,
namespaceType,
});
if (deleted == null) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { item_id: itemId, id, namespace_type: namespaceType } = request.query;
if (itemId == null && id == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
body: 'Either "item_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const [validated, errors] = validate(deleted, deleteExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const deleted = await exceptionLists.deleteExceptionListItem({
id,
itemId,
namespaceType,
});
if (deleted == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(deleted, deleteExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,56 +24,63 @@ import {
} from './utils';
export const deleteExceptionListRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_URL,
validate: {
query: buildRouteValidation<
typeof deleteExceptionListRequestQuery,
DeleteExceptionListRequestQueryDecoded
>(deleteExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof deleteExceptionListRequestQuery,
DeleteExceptionListRequestQueryDecoded
>(deleteExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { list_id: listId, id, namespace_type: namespaceType } = request.query;
if (listId == null && id == null) {
return siemResponse.error({
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const deleted = await exceptionLists.deleteExceptionList({
id,
listId,
namespaceType,
});
if (deleted == null) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const { list_id: listId, id, namespace_type: namespaceType } = request.query;
if (listId == null && id == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
});
} else {
const [validated, errors] = validate(deleted, deleteExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const deleted = await exceptionLists.deleteExceptionList({
id,
listId,
namespaceType,
});
if (deleted == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(deleted, deleteExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,76 +19,83 @@ import {
import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils';
export const duplicateExceptionsRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: `${EXCEPTION_LIST_URL}/_duplicate`,
validate: {
query: buildRouteValidation<
typeof duplicateExceptionListRequestQuery,
DuplicateExceptionListRequestQueryDecoded
>(duplicateExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof duplicateExceptionListRequestQuery,
DuplicateExceptionListRequestQueryDecoded
>(duplicateExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
list_id: listId,
namespace_type: namespaceType,
include_expired_exceptions: includeExpiredExceptionsString,
} = request.query;
try {
const {
list_id: listId,
namespace_type: namespaceType,
include_expired_exceptions: includeExpiredExceptionsString,
} = request.query;
const exceptionListsClient = await getExceptionListClient(context);
const exceptionListsClient = await getExceptionListClient(context);
// fetch list container
const listToDuplicate = await exceptionListsClient.getExceptionList({
id: undefined,
listId,
namespaceType,
});
// fetch list container
const listToDuplicate = await exceptionListsClient.getExceptionList({
id: undefined,
listId,
namespaceType,
});
if (listToDuplicate == null) {
if (listToDuplicate == null) {
return siemResponse.error({
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
});
}
// Defaults to including expired exceptions if query param is not present
const includeExpiredExceptions =
includeExpiredExceptionsString !== undefined
? includeExpiredExceptionsString === 'true'
: true;
const duplicatedList = await exceptionListsClient.duplicateExceptionListAndItems({
includeExpiredExceptions,
list: listToDuplicate,
namespaceType,
});
if (duplicatedList == null) {
return siemResponse.error({
body: `unable to duplicate exception list with list_id: ${listId} - action not allowed`,
statusCode: 405,
});
}
const [validated, errors] = validate(duplicatedList, duplicateExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
body: error.message,
statusCode: error.statusCode,
});
}
// Defaults to including expired exceptions if query param is not present
const includeExpiredExceptions =
includeExpiredExceptionsString !== undefined
? includeExpiredExceptionsString === 'true'
: true;
const duplicatedList = await exceptionListsClient.duplicateExceptionListAndItems({
includeExpiredExceptions,
list: listToDuplicate,
namespaceType,
});
if (duplicatedList == null) {
return siemResponse.error({
body: `unable to duplicate exception list with list_id: ${listId} - action not allowed`,
statusCode: 405,
});
}
const [validated, errors] = validate(duplicatedList, duplicateExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -14,61 +14,68 @@ import { exportExceptionListRequestQuery } from '../../common/api';
import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils';
export const exportExceptionsRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${EXCEPTION_LIST_URL}/_export`,
validate: {
query: buildRouteValidation(exportExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(exportExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
id,
list_id: listId,
namespace_type: namespaceType,
include_expired_exceptions: includeExpiredExceptionsString,
} = request.query;
const exceptionListsClient = await getExceptionListClient(context);
try {
const {
id,
list_id: listId,
namespace_type: namespaceType,
include_expired_exceptions: includeExpiredExceptionsString,
} = request.query;
const exceptionListsClient = await getExceptionListClient(context);
// Defaults to including expired exceptions if query param is not present
const includeExpiredExceptions =
includeExpiredExceptionsString !== undefined
? includeExpiredExceptionsString === 'true'
: true;
const exportContent = await exceptionListsClient.exportExceptionListAndItems({
id,
includeExpiredExceptions,
listId,
namespaceType,
});
// Defaults to including expired exceptions if query param is not present
const includeExpiredExceptions =
includeExpiredExceptionsString !== undefined
? includeExpiredExceptionsString === 'true'
: true;
const exportContent = await exceptionListsClient.exportExceptionListAndItems({
id,
includeExpiredExceptions,
listId,
namespaceType,
});
if (exportContent == null) {
if (exportContent == null) {
return siemResponse.error({
body: `exception list with list_id: ${listId} or id: ${id} does not exist`,
statusCode: 400,
});
}
return response.ok({
body: `${exportContent.exportData}${JSON.stringify(exportContent.exportDetails)}\n`,
headers: {
'Content-Disposition': `attachment; filename="${listId}"`,
'Content-Type': 'application/ndjson',
},
});
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `exception list with list_id: ${listId} or id: ${id} does not exist`,
statusCode: 400,
body: error.message,
statusCode: error.statusCode,
});
}
return response.ok({
body: `${exportContent.exportData}${JSON.stringify(exportContent.exportDetails)}\n`,
headers: {
'Content-Disposition': `attachment; filename="${listId}"`,
'Content-Type': 'application/ndjson',
},
});
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,62 +19,69 @@ import {
import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils';
export const findEndpointListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${ENDPOINT_LIST_ITEM_URL}/_find`,
validate: {
query: buildRouteValidation<
typeof findEndpointListItemRequestQuery,
FindEndpointListItemRequestQueryDecoded
>(findEndpointListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof findEndpointListItemRequestQuery,
FindEndpointListItemRequestQueryDecoded
>(findEndpointListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
page,
per_page: perPage,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
page,
per_page: perPage,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const exceptionListItems = await exceptionLists.findEndpointListItem({
filter,
page,
perPage,
pit: undefined,
searchAfter: undefined,
sortField,
sortOrder,
});
if (exceptionListItems == null) {
// Although I have this line of code here, this is an incredibly rare thing to have
// happen as the findEndpointListItem tries to auto-create the endpoint list if
// does not exist.
const exceptionListItems = await exceptionLists.findEndpointListItem({
filter,
page,
perPage,
pit: undefined,
searchAfter: undefined,
sortField,
sortOrder,
});
if (exceptionListItems == null) {
// Although I have this line of code here, this is an incredibly rare thing to have
// happen as the findEndpointListItem tries to auto-create the endpoint list if
// does not exist.
return siemResponse.error({
body: `list id: "${ENDPOINT_LIST_ID}" does not exist`,
statusCode: 404,
});
}
const [validated, errors] = validate(exceptionListItems, findEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `list id: "${ENDPOINT_LIST_ID}" does not exist`,
statusCode: 404,
body: error.message,
statusCode: error.statusCode,
});
}
const [validated, errors] = validate(exceptionListItems, findEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,77 +19,84 @@ import {
import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils';
export const findExceptionListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${EXCEPTION_LIST_ITEM_URL}/_find`,
validate: {
query: buildRouteValidation<
typeof findExceptionListItemRequestQuery,
FindExceptionListItemRequestQueryDecoded
>(findExceptionListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof findExceptionListItemRequestQuery,
FindExceptionListItemRequestQueryDecoded
>(findExceptionListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
list_id: listId,
namespace_type: namespaceType,
page,
per_page: perPage,
search,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
if (listId.length !== namespaceType.length) {
return siemResponse.error({
body: `list_id and namespace_id need to have the same comma separated number of values. Expected list_id length: ${listId.length} to equal namespace_type length: ${namespaceType.length}`,
statusCode: 400,
});
} else if (listId.length !== filter.length && filter.length !== 0) {
return siemResponse.error({
body: `list_id and filter need to have the same comma separated number of values. Expected list_id length: ${listId.length} to equal filter length: ${filter.length}`,
statusCode: 400,
});
} else {
const exceptionListItems = await exceptionLists.findExceptionListsItem({
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
listId,
namespaceType,
list_id: listId,
namespace_type: namespaceType,
page,
perPage,
pit: undefined,
per_page: perPage,
search,
searchAfter: undefined,
sortField,
sortOrder,
});
if (exceptionListItems == null) {
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
if (listId.length !== namespaceType.length) {
return siemResponse.error({
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
body: `list_id and namespace_id need to have the same comma separated number of values. Expected list_id length: ${listId.length} to equal namespace_type length: ${namespaceType.length}`,
statusCode: 400,
});
} else if (listId.length !== filter.length && filter.length !== 0) {
return siemResponse.error({
body: `list_id and filter need to have the same comma separated number of values. Expected list_id length: ${listId.length} to equal filter length: ${filter.length}`,
statusCode: 400,
});
}
const [validated, errors] = validate(exceptionListItems, findExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
const exceptionListItems = await exceptionLists.findExceptionListsItem({
filter,
listId,
namespaceType,
page,
perPage,
pit: undefined,
search,
searchAfter: undefined,
sortField,
sortOrder,
});
if (exceptionListItems == null) {
return siemResponse.error({
body: `exception list id: "${listId}" does not exist`,
statusCode: 404,
});
}
const [validated, errors] = validate(exceptionListItems, findExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,54 +19,61 @@ import {
import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from './utils';
export const findExceptionListRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${EXCEPTION_LIST_URL}/_find`,
validate: {
query: buildRouteValidation<
typeof findExceptionListRequestQuery,
FindExceptionListRequestQueryDecoded
>(findExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof findExceptionListRequestQuery,
FindExceptionListRequestQueryDecoded
>(findExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
page,
namespace_type: namespaceType,
per_page: perPage,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const exceptionListItems = await exceptionLists.findExceptionList({
filter,
namespaceType,
page,
perPage,
pit: undefined,
searchAfter: undefined,
sortField,
sortOrder,
});
const [validated, errors] = validate(exceptionListItems, findExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const exceptionLists = await getExceptionListClient(context);
const {
filter,
page,
namespace_type: namespaceType,
per_page: perPage,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const exceptionListItems = await exceptionLists.findExceptionList({
filter,
namespaceType,
page,
perPage,
pit: undefined,
searchAfter: undefined,
sortField,
sortOrder,
});
const [validated, errors] = validate(exceptionListItems, findExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -28,8 +28,9 @@ import { buildRouteValidation, buildSiemResponse, getExceptionListClient } from
* choice to overwrite any matching lists
*/
export const importExceptionsRoute = (router: ListsPluginRouter, config: ConfigType): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
body: {
maxBytes: config.maxImportPayloadBytes,
@ -38,49 +39,55 @@ export const importExceptionsRoute = (router: ListsPluginRouter, config: ConfigT
tags: ['access:lists-all'],
},
path: `${EXCEPTION_LIST_URL}/_import`,
validate: {
body: schema.any(), // validation on file object is accomplished later in the handler.
query: buildRouteValidation<
typeof importExceptionsRequestQuery,
ImportExceptionsRequestQueryDecoded
>(importExceptionsRequestQuery),
})
.addVersion(
{
validate: {
request: {
body: schema.any(), // validation on file object is accomplished later in the handler.
query: buildRouteValidation<
typeof importExceptionsRequestQuery,
ImportExceptionsRequestQueryDecoded
>(importExceptionsRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const exceptionListsClient = await getExceptionListClient(context);
const siemResponse = buildSiemResponse(response);
async (context, request, response) => {
const exceptionListsClient = await getExceptionListClient(context);
const siemResponse = buildSiemResponse(response);
try {
const { filename } = request.body.file.hapi;
const fileExtension = extname(filename).toLowerCase();
if (fileExtension !== '.ndjson') {
try {
const { filename } = request.body.file.hapi;
const fileExtension = extname(filename).toLowerCase();
if (fileExtension !== '.ndjson') {
return siemResponse.error({
body: `Invalid file extension ${fileExtension}`,
statusCode: 400,
});
}
const importsSummary = await exceptionListsClient.importExceptionListAndItems({
exceptionsToImport: request.body.file,
generateNewListId: request.query.as_new_list,
maxExceptionsImportSize: config.maxExceptionsImportSize,
overwrite: request.query.overwrite,
});
const [validated, errors] = validate(importsSummary, importExceptionsResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `Invalid file extension ${fileExtension}`,
statusCode: 400,
body: error.message,
statusCode: error.statusCode,
});
}
const importsSummary = await exceptionListsClient.importExceptionListAndItems({
exceptionsToImport: request.body.file,
generateNewListId: request.query.as_new_list,
maxExceptionsImportSize: config.maxExceptionsImportSize,
overwrite: request.query.overwrite,
});
const [validated, errors] = validate(importsSummary, importExceptionsResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,79 +19,86 @@ import { getExceptionFilterRequest } from '../../../common/api';
import { buildRouteValidation, buildSiemResponse } from '../utils';
export const getExceptionFilterRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'internal',
options: {
tags: ['access:securitySolution'],
},
path: INTERNAL_EXCEPTION_FILTER,
validate: {
body: buildRouteValidation(getExceptionFilterRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(getExceptionFilterRequest),
},
},
version: '1',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const ctx = await context.resolve(['lists']);
const listClient = ctx.lists?.getListClient();
if (!listClient) {
return siemResponse.error({ body: 'Cannot retrieve list client', statusCode: 500 });
}
const exceptionListClient = ctx.lists?.getExceptionListClient();
const exceptionItems: Array<ExceptionListItemSchema | CreateExceptionListItemSchema> = [];
const {
type,
alias = null,
exclude_exceptions: excludeExceptions = true,
chunk_size: chunkSize = 10,
} = request.body;
if (type === 'exception_list_ids') {
const listIds = request.body.exception_list_ids.map(
({ exception_list_id: listId }) => listId
);
const namespaceTypes = request.body.exception_list_ids.map(
({ namespace_type: namespaceType }) => namespaceType
);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const ctx = await context.resolve(['lists']);
const listClient = ctx.lists?.getListClient();
if (!listClient) {
return siemResponse.error({ body: 'Cannot retrieve list client', statusCode: 500 });
}
const exceptionListClient = ctx.lists?.getExceptionListClient();
const exceptionItems: Array<ExceptionListItemSchema | CreateExceptionListItemSchema> = [];
const {
type,
alias = null,
exclude_exceptions: excludeExceptions = true,
chunk_size: chunkSize = 10,
} = request.body;
if (type === 'exception_list_ids') {
const listIds = request.body.exception_list_ids.map(
({ exception_list_id: listId }) => listId
);
const namespaceTypes = request.body.exception_list_ids.map(
({ namespace_type: namespaceType }) => namespaceType
);
// Stream the results from the Point In Time (PIT) finder into this array
let items: ExceptionListItemSchema[] = [];
const executeFunctionOnStream = (responseBody: FoundExceptionListItemSchema): void => {
items = [...items, ...responseBody.data];
};
// Stream the results from the Point In Time (PIT) finder into this array
let items: ExceptionListItemSchema[] = [];
const executeFunctionOnStream = (responseBody: FoundExceptionListItemSchema): void => {
items = [...items, ...responseBody.data];
};
await exceptionListClient?.findExceptionListsItemPointInTimeFinder({
executeFunctionOnStream,
filter: [],
listId: listIds,
maxSize: undefined, // NOTE: This is unbounded when it is "undefined"
namespaceType: namespaceTypes,
perPage: 1_000, // See https://github.com/elastic/kibana/issues/93770 for choice of 1k
sortField: undefined,
sortOrder: undefined,
await exceptionListClient?.findExceptionListsItemPointInTimeFinder({
executeFunctionOnStream,
filter: [],
listId: listIds,
maxSize: undefined, // NOTE: This is unbounded when it is "undefined"
namespaceType: namespaceTypes,
perPage: 1_000, // See https://github.com/elastic/kibana/issues/93770 for choice of 1k
sortField: undefined,
sortOrder: undefined,
});
exceptionItems.push(...items);
} else {
const { exceptions } = request.body;
exceptionItems.push(...exceptions);
}
const { filter } = await buildExceptionFilter({
alias,
chunkSize,
excludeExceptions,
listClient,
lists: exceptionItems,
startedAt: new Date(),
});
return response.ok({ body: { filter } ?? {} });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
exceptionItems.push(...items);
} else {
const { exceptions } = request.body;
exceptionItems.push(...exceptions);
}
const { filter } = await buildExceptionFilter({
alias,
chunkSize,
excludeExceptions,
listClient,
lists: exceptionItems,
startedAt: new Date(),
});
return response.ok({ body: { filter } ?? {} });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -17,8 +17,9 @@ import type { ListsPluginRouter } from '../../types';
import { buildRouteValidation, buildSiemResponse } from '../utils';
export const internalCreateExceptionListRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'internal',
options: {
// Access control is set to `read` on purpose, as this route is internal and meant to
// ensure we have lists created (if not already) for Endpoint artifacts in order to support
@ -26,26 +27,32 @@ export const internalCreateExceptionListRoute = (router: ListsPluginRouter): voi
tags: ['access:lists-read'],
},
path: INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL,
validate: {
body: buildRouteValidation<
typeof internalCreateExceptionListSchema,
InternalCreateExceptionListSchemaDecoded
>(internalCreateExceptionListSchema),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof internalCreateExceptionListSchema,
InternalCreateExceptionListSchemaDecoded
>(internalCreateExceptionListSchema),
},
},
version: '1',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
return await createExceptionListHandler(context, request, response, siemResponse, {
ignoreExisting: true,
});
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
return await createExceptionListHandler(context, request, response, siemResponse, {
ignoreExisting: true,
});
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
}
);
);
};

View file

@ -20,141 +20,152 @@ import { findListsBySizeRequestQuery, findListsBySizeResponse } from '../../../c
import { buildRouteValidation, buildSiemResponse, getListClient } from '../utils';
export const findListsBySizeRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'internal',
options: {
tags: ['access:lists-read'],
},
path: INTERNAL_FIND_LISTS_BY_SIZE,
validate: {
query: buildRouteValidation(findListsBySizeRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(findListsBySizeRequestQuery),
},
},
version: '1',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const listClient = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const listClient = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter],
} = decodeCursor({
cursor,
page,
perPage,
sortField,
});
if (!isValid) {
return siemResponse.error({
body: errorMessage,
statusCode: 400,
});
} else {
const valueLists = await listClient.findList({
currentIndexPosition,
filter,
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter],
} = decodeCursor({
cursor,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
if (!isValid) {
return siemResponse.error({
body: errorMessage,
statusCode: 400,
});
} else {
const valueLists = await listClient.findList({
currentIndexPosition,
filter,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
const listBooleans: boolean[] = [];
const listBooleans: boolean[] = [];
const chunks = chunk(valueLists.data, 10);
for (const listChunk of chunks) {
const booleans = await Promise.all(
listChunk.map(async (valueList) => {
// Currently the only list types we support for exceptions
if (
valueList.type !== 'ip_range' &&
valueList.type !== 'ip' &&
valueList.type !== 'keyword'
) {
return false;
}
const chunks = chunk(valueLists.data, 10);
for (const listChunk of chunks) {
const booleans = await Promise.all(
listChunk.map(async (valueList) => {
// Currently the only list types we support for exceptions
if (
valueList.type !== 'ip_range' &&
valueList.type !== 'ip' &&
valueList.type !== 'keyword'
) {
return false;
}
const list = await listClient.findListItem({
currentIndexPosition: 0,
filter: '',
listId: valueList.id,
page: 0,
perPage: 0,
runtimeMappings: undefined,
searchAfter: [],
sortField: undefined,
sortOrder: undefined,
});
if (
valueList.type === 'ip_range' &&
list &&
list.total < MAXIMUM_SMALL_VALUE_LIST_SIZE
) {
const rangeList = await listClient.findListItem({
const list = await listClient.findListItem({
currentIndexPosition: 0,
filter: 'is_cidr: false',
filter: '',
listId: valueList.id,
page: 0,
perPage: 0,
runtimeMappings: {
is_cidr: {
script: `
runtimeMappings: undefined,
searchAfter: [],
sortField: undefined,
sortOrder: undefined,
});
if (
valueList.type === 'ip_range' &&
list &&
list.total < MAXIMUM_SMALL_VALUE_LIST_SIZE
) {
const rangeList = await listClient.findListItem({
currentIndexPosition: 0,
filter: 'is_cidr: false',
listId: valueList.id,
page: 0,
perPage: 0,
runtimeMappings: {
is_cidr: {
script: `
if (params._source["ip_range"] instanceof String) {
emit(true);
} else {
emit(false);
}
`,
type: 'boolean',
type: 'boolean',
},
},
},
searchAfter: [],
sortField: undefined,
sortOrder: undefined,
});
searchAfter: [],
sortField: undefined,
sortOrder: undefined,
});
return rangeList && rangeList.total < MAXIMUM_SMALL_IP_RANGE_VALUE_LIST_DASH_SIZE
? true
: false;
}
return list && list.total < MAXIMUM_SMALL_VALUE_LIST_SIZE ? true : false;
})
return rangeList &&
rangeList.total < MAXIMUM_SMALL_IP_RANGE_VALUE_LIST_DASH_SIZE
? true
: false;
}
return list && list.total < MAXIMUM_SMALL_VALUE_LIST_SIZE ? true : false;
})
);
listBooleans.push(...booleans);
}
const smallLists = valueLists.data.filter((valueList, index) => listBooleans[index]);
const largeLists = valueLists.data.filter((valueList, index) => !listBooleans[index]);
const [validated, errors] = validate(
{ largeLists, smallLists },
findListsBySizeResponse
);
listBooleans.push(...booleans);
}
const smallLists = valueLists.data.filter((valueList, index) => listBooleans[index]);
const largeLists = valueLists.data.filter((valueList, index) => !listBooleans[index]);
const [validated, errors] = validate({ largeLists, smallLists }, findListsBySizeResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,71 +19,78 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const createListRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_URL,
validate: {
body: buildRouteValidation<typeof createListRequest, CreateListRequestDecoded>(
createListRequest
),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<typeof createListRequest, CreateListRequestDecoded>(
createListRequest
),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, deserializer, id, serializer, type, meta, version } =
request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListDataStreamExists();
const indexExists = await lists.getListIndexExists();
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, deserializer, id, serializer, type, meta, version } =
request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListDataStreamExists();
const indexExists = await lists.getListIndexExists();
if (!dataStreamExists && !indexExists) {
return siemResponse.error({
body: `To create a list, the data stream must exist first. Data stream "${lists.getListName()}" does not exist`,
statusCode: 400,
});
} else {
// needs to be migrated to data stream
if (!dataStreamExists && indexExists) {
await lists.migrateListIndexToDataStream();
}
if (id != null) {
const list = await lists.getList({ id });
if (list != null) {
return siemResponse.error({
body: `list id: "${id}" already exists`,
statusCode: 409,
});
if (!dataStreamExists && !indexExists) {
return siemResponse.error({
body: `To create a list, the data stream must exist first. Data stream "${lists.getListName()}" does not exist`,
statusCode: 400,
});
} else {
// needs to be migrated to data stream
if (!dataStreamExists && indexExists) {
await lists.migrateListIndexToDataStream();
}
if (id != null) {
const list = await lists.getList({ id });
if (list != null) {
return siemResponse.error({
body: `list id: "${id}" already exists`,
statusCode: 409,
});
}
}
const list = await lists.createList({
description,
deserializer,
id,
immutable: false,
meta,
name,
serializer,
type,
version,
});
const [validated, errors] = validate(list, createListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
const list = await lists.createList({
description,
deserializer,
id,
immutable: false,
meta,
name,
serializer,
type,
version,
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
const [validated, errors] = validate(list, createListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -26,112 +26,122 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getExceptionListClient, getListClient } from '..';
export const deleteListRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_URL,
validate: {
query: buildRouteValidation(deleteListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(deleteListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const exceptionLists = await getExceptionListClient(context);
const { id, deleteReferences, ignoreReferences } = request.query;
let deleteExceptionItemResponses;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const exceptionLists = await getExceptionListClient(context);
const { id, deleteReferences, ignoreReferences } = request.query;
let deleteExceptionItemResponses;
// ignoreReferences=true maintains pre-7.11 behavior of deleting value list without performing any additional checks
if (!ignoreReferences) {
// Stream the results from the Point In Time (PIT) finder into this array
let referencedExceptionListItems: ExceptionListItemSchema[] = [];
const executeFunctionOnStream = (foundResponse: FoundExceptionListItemSchema): void => {
referencedExceptionListItems = [...referencedExceptionListItems, ...foundResponse.data];
};
// ignoreReferences=true maintains pre-7.11 behavior of deleting value list without performing any additional checks
if (!ignoreReferences) {
// Stream the results from the Point In Time (PIT) finder into this array
let referencedExceptionListItems: ExceptionListItemSchema[] = [];
const executeFunctionOnStream = (foundResponse: FoundExceptionListItemSchema): void => {
referencedExceptionListItems = [
...referencedExceptionListItems,
...foundResponse.data,
];
};
await exceptionLists.findValueListExceptionListItemsPointInTimeFinder({
executeFunctionOnStream,
maxSize: undefined, // NOTE: This is unbounded when it is "undefined"
perPage: 1_000, // See https://github.com/elastic/kibana/issues/93770 for choice of 1k
sortField: undefined,
sortOrder: undefined,
valueListId: id,
});
if (referencedExceptionListItems.length) {
// deleteReferences=false to perform dry run and identify referenced exception lists/items
if (deleteReferences) {
// Delete referenced exception list items
// TODO: Create deleteListItems to delete in batch
deleteExceptionItemResponses = await Promise.all(
referencedExceptionListItems.map(async (listItem) => {
// Ensure only the single entry is deleted as there could be a separate value list referenced that is okay to keep // TODO: Add API to delete single entry
const remainingEntries = listItem.entries.filter(
(e) => e.type === 'list' && e.list.id !== id
);
if (remainingEntries.length === 0) {
// All entries reference value list specified in request, delete entire exception list item
return deleteExceptionListItem(exceptionLists, listItem);
} else {
// Contains more entries than just value list specified in request , patch (doesn't exist yet :) exception list item to remove entry
return updateExceptionListItems(exceptionLists, listItem, remainingEntries);
}
})
);
} else {
const referencedExceptionLists = await getReferencedExceptionLists(
exceptionLists,
referencedExceptionListItems
);
const refError = `Value list '${id}' is referenced in existing exception list(s)`;
const references = referencedExceptionListItems.map((item) => ({
exception_item: item,
exception_list: referencedExceptionLists.find((l) => l.list_id === item.list_id),
}));
await exceptionLists.findValueListExceptionListItemsPointInTimeFinder({
executeFunctionOnStream,
maxSize: undefined, // NOTE: This is unbounded when it is "undefined"
perPage: 1_000, // See https://github.com/elastic/kibana/issues/93770 for choice of 1k
sortField: undefined,
sortOrder: undefined,
valueListId: id,
});
if (referencedExceptionListItems.length) {
// deleteReferences=false to perform dry run and identify referenced exception lists/items
if (deleteReferences) {
// Delete referenced exception list items
// TODO: Create deleteListItems to delete in batch
deleteExceptionItemResponses = await Promise.all(
referencedExceptionListItems.map(async (listItem) => {
// Ensure only the single entry is deleted as there could be a separate value list referenced that is okay to keep // TODO: Add API to delete single entry
const remainingEntries = listItem.entries.filter(
(e) => e.type === 'list' && e.list.id !== id
);
if (remainingEntries.length === 0) {
// All entries reference value list specified in request, delete entire exception list item
return deleteExceptionListItem(exceptionLists, listItem);
} else {
// Contains more entries than just value list specified in request , patch (doesn't exist yet :) exception list item to remove entry
return updateExceptionListItems(exceptionLists, listItem, remainingEntries);
}
})
);
} else {
const referencedExceptionLists = await getReferencedExceptionLists(
exceptionLists,
referencedExceptionListItems
);
const refError = `Value list '${id}' is referenced in existing exception list(s)`;
const references = referencedExceptionListItems.map((item) => ({
exception_item: item,
exception_list: referencedExceptionLists.find((l) => l.list_id === item.list_id),
}));
return siemResponse.error({
body: {
error: {
message: refError,
references,
value_list_id: id,
return siemResponse.error({
body: {
error: {
message: refError,
references,
value_list_id: id,
},
},
statusCode: 409,
});
}
}
}
const deleted = await lists.deleteList({ id });
if (deleted == null) {
return siemResponse.error({
body: `list id: "${id}" was not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(deleted, deleteListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({
body: validated ?? {
deleteItemResponses: deleteExceptionItemResponses,
},
statusCode: 409,
});
}
}
}
const deleted = await lists.deleteList({ id });
if (deleted == null) {
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `list id: "${id}" was not found`,
statusCode: 404,
body: error.message,
statusCode: error.statusCode,
});
} else {
const [validated, errors] = validate(deleted, deleteListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({
body: validated ?? {
deleteItemResponses: deleteExceptionItemResponses,
},
});
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};
/**

View file

@ -18,8 +18,9 @@ import { createStreamFromBuffer } from '../utils/create_stream_from_buffer';
import { getListClient } from '..';
export const importListItemRoute = (router: ListsPluginRouter, config: ConfigType): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
body: {
accepts: ['multipart/form-data'],
@ -32,103 +33,109 @@ export const importListItemRoute = (router: ListsPluginRouter, config: ConfigTyp
},
},
path: `${LIST_ITEM_URL}/_import`,
validate: {
body: schema.buffer(),
query: buildRouteValidation(importListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
body: schema.buffer(),
query: buildRouteValidation(importListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const stream = createStreamFromBuffer(request.body);
const { deserializer, list_id: listId, serializer, type } = request.query;
const lists = await getListClient(context);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const stream = createStreamFromBuffer(request.body);
const { deserializer, list_id: listId, serializer, type } = request.query;
const lists = await getListClient(context);
const listDataExists = await lists.getListDataStreamExists();
if (!listDataExists) {
const listIndexExists = await lists.getListIndexExists();
if (!listIndexExists) {
return siemResponse.error({
body: `To import a list item, the data steam must exist first. Data stream "${lists.getListName()}" does not exist`,
statusCode: 400,
});
const listDataExists = await lists.getListDataStreamExists();
if (!listDataExists) {
const listIndexExists = await lists.getListIndexExists();
if (!listIndexExists) {
return siemResponse.error({
body: `To import a list item, the data steam must exist first. Data stream "${lists.getListName()}" does not exist`,
statusCode: 400,
});
}
// otherwise migration is needed
await lists.migrateListIndexToDataStream();
}
// otherwise migration is needed
await lists.migrateListIndexToDataStream();
}
const listItemDataExists = await lists.getListItemDataStreamExists();
if (!listItemDataExists) {
const listItemIndexExists = await lists.getListItemIndexExists();
if (!listItemIndexExists) {
return siemResponse.error({
body: `To import a list item, the data steam must exist first. Data stream "${lists.getListItemName()}" does not exist`,
statusCode: 400,
});
const listItemDataExists = await lists.getListItemDataStreamExists();
if (!listItemDataExists) {
const listItemIndexExists = await lists.getListItemIndexExists();
if (!listItemIndexExists) {
return siemResponse.error({
body: `To import a list item, the data steam must exist first. Data stream "${lists.getListItemName()}" does not exist`,
statusCode: 400,
});
}
// otherwise migration is needed
await lists.migrateListItemIndexToDataStream();
}
// otherwise migration is needed
await lists.migrateListItemIndexToDataStream();
}
if (listId != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 409,
if (listId != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 409,
});
}
await lists.importListItemsToStream({
deserializer: list.deserializer,
listId,
meta: undefined,
serializer: list.serializer,
stream,
type: list.type,
version: 1,
});
}
await lists.importListItemsToStream({
deserializer: list.deserializer,
listId,
meta: undefined,
serializer: list.serializer,
stream,
type: list.type,
version: 1,
});
const [validated, errors] = validate(list, importListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const [validated, errors] = validate(list, importListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else if (type != null) {
const importedList = await lists.importListItemsToStream({
deserializer,
listId: undefined,
meta: undefined,
serializer,
stream,
type,
version: 1,
});
if (importedList == null) {
return siemResponse.error({
body: 'Unable to parse a valid fileName during import',
statusCode: 400,
});
}
const [validated, errors] = validate(importedList, importListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else {
return response.ok({ body: validated ?? {} });
}
} else if (type != null) {
const importedList = await lists.importListItemsToStream({
deserializer,
listId: undefined,
meta: undefined,
serializer,
stream,
type,
version: 1,
});
if (importedList == null) {
return siemResponse.error({
body: 'Unable to parse a valid fileName during import',
body: 'Either type or list_id need to be defined in the query',
statusCode: 400,
});
}
const [validated, errors] = validate(importedList, importListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else {
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: 'Either type or list_id need to be defined in the query',
statusCode: 400,
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,52 +15,59 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const patchListRoute = (router: ListsPluginRouter): void => {
router.patch(
{
router.versioned
.patch({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_URL,
validate: {
body: buildRouteValidation(patchListRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(patchListRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, id, meta, _version, version } = request.body;
const lists = await getListClient(context);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, id, meta, _version, version } = request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListIndexExists();
if (indexExists) {
await lists.migrateListIndexToDataStream();
const dataStreamExists = await lists.getListDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListIndexExists();
if (indexExists) {
await lists.migrateListIndexToDataStream();
}
}
}
const list = await lists.patchList({ _version, description, id, meta, name, version });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(list, patchListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const list = await lists.patchList({ _version, description, id, meta, name, version });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" not found`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(list, patchListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,42 +15,49 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const readListRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: LIST_URL,
validate: {
query: buildRouteValidation(readListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(readListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id } = request.query;
const lists = await getListClient(context);
const list = await lists.getList({ id });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" does not exist`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(list, readListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id } = request.query;
const lists = await getListClient(context);
const list = await lists.getList({ id });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" does not exist`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(list, readListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,52 +15,59 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const updateListRoute = (router: ListsPluginRouter): void => {
router.put(
{
router.versioned
.put({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_URL,
validate: {
body: buildRouteValidation(updateListRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(updateListRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, id, meta, _version, version } = request.body;
const lists = await getListClient(context);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { name, description, id, meta, _version, version } = request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListIndexExists();
if (indexExists) {
await lists.migrateListIndexToDataStream();
const dataStreamExists = await lists.getListDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListIndexExists();
if (indexExists) {
await lists.migrateListIndexToDataStream();
}
}
}
const list = await lists.updateList({ _version, description, id, meta, name, version });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(list, updateListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const list = await lists.updateList({ _version, description, id, meta, name, version });
if (list == null) {
return siemResponse.error({
body: `list id: "${id}" not found`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(list, updateListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,15 +15,15 @@ import { buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const createListIndexRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_INDEX,
validate: false,
},
async (context, _, response) => {
})
.addVersion({ validate: false, version: '2023-10-31' }, async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
@ -77,6 +77,5 @@ export const createListIndexRoute = (router: ListsPluginRouter): void => {
statusCode: error.statusCode,
});
}
}
);
});
};

View file

@ -32,67 +32,72 @@ import { getListClient } from '..';
* And ensuring they're all gone
*/
export const deleteListIndexRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_INDEX,
validate: false,
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const listIndexExists = await lists.getListIndexExists();
const listItemIndexExists = await lists.getListItemIndexExists();
})
.addVersion(
{
validate: false,
version: '2023-10-31',
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const listIndexExists = await lists.getListIndexExists();
const listItemIndexExists = await lists.getListItemIndexExists();
const listDataStreamExists = await lists.getListDataStreamExists();
const listItemDataStreamExists = await lists.getListItemDataStreamExists();
const listDataStreamExists = await lists.getListDataStreamExists();
const listItemDataStreamExists = await lists.getListItemDataStreamExists();
// return early if no data stream or indices exist
if (
!listDataStreamExists &&
!listItemDataStreamExists &&
!listIndexExists &&
!listItemIndexExists
) {
// return early if no data stream or indices exist
if (
!listDataStreamExists &&
!listItemDataStreamExists &&
!listIndexExists &&
!listItemIndexExists
) {
return siemResponse.error({
body: `index and data stream: "${lists.getListName()}" and "${lists.getListItemName()}" does not exist`,
statusCode: 404,
});
}
// ensure data streams deleted if exist
await deleteDataStreams(lists, listDataStreamExists, listItemDataStreamExists);
// we need to call this section only if any of these indices exist
// to delete indices, ILM policies and legacy templates
// ILM policies and legacy templates do not exist on serverless,
// so by checking if any of index exists we ensure it is stateful
if (listIndexExists || listItemIndexExists) {
await deleteIndices(lists, listIndexExists, listItemIndexExists);
await lists.deleteLegacyListTemplateIfExists();
await lists.deleteLegacyListItemTemplateIfExists();
}
await deleteIndexTemplates(lists);
const [validated, errors] = validate({ acknowledged: true }, deleteListIndexResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `index and data stream: "${lists.getListName()}" and "${lists.getListItemName()}" does not exist`,
statusCode: 404,
body: error.message,
statusCode: error.statusCode,
});
}
// ensure data streams deleted if exist
await deleteDataStreams(lists, listDataStreamExists, listItemDataStreamExists);
// we need to call this section only if any of these indices exist
// to delete indices, ILM policies and legacy templates
// ILM policies and legacy templates do not exist on serverless,
// so by checking if any of index exists we ensure it is stateful
if (listIndexExists || listItemIndexExists) {
await deleteIndices(lists, listIndexExists, listItemIndexExists);
await lists.deleteLegacyListTemplateIfExists();
await lists.deleteLegacyListItemTemplateIfExists();
}
await deleteIndexTemplates(lists);
const [validated, errors] = validate({ acknowledged: true }, deleteListIndexResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};
/**

View file

@ -16,48 +16,55 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const exportListItemRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${LIST_ITEM_URL}/_export`,
validate: {
query: buildRouteValidation(exportListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(exportListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { list_id: listId } = request.query;
const lists = await getListClient(context);
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list_id: ${listId} does not exist`,
statusCode: 400,
});
} else {
// TODO: Allow the API to override the name of the file to export
const fileName = list.name;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { list_id: listId } = request.query;
const lists = await getListClient(context);
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list_id: ${listId} does not exist`,
statusCode: 400,
});
} else {
// TODO: Allow the API to override the name of the file to export
const fileName = list.name;
const stream = new Stream.PassThrough();
lists.exportListItemsToStream({ listId, stream, stringToAppend: '\n' });
return response.ok({
body: stream,
headers: {
'Content-Disposition': `attachment; filename="${fileName}"`,
'Content-Type': 'application/ndjson',
},
const stream = new Stream.PassThrough();
lists.exportListItemsToStream({ listId, stream, stringToAppend: '\n' });
return response.ok({
body: stream,
headers: {
'Content-Disposition': `attachment; filename="${fileName}"`,
'Content-Type': 'application/ndjson',
},
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,72 +15,79 @@ import { findListRequestQuery, findListResponse } from '../../../common/api';
import { buildRouteValidation, buildSiemResponse, getListClient } from '../utils';
export const findListRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${LIST_URL}/_find`,
validate: {
query: buildRouteValidation(findListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(findListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter],
} = decodeCursor({
cursor,
page,
perPage,
sortField,
});
if (!isValid) {
return siemResponse.error({
body: errorMessage,
statusCode: 400,
});
} else {
const exceptionList = await lists.findList({
currentIndexPosition,
filter,
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter],
} = decodeCursor({
cursor,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
const [validated, errors] = validate(exceptionList, findListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (!isValid) {
return siemResponse.error({
body: errorMessage,
statusCode: 400,
});
} else {
return response.ok({ body: validated ?? {} });
const exceptionList = await lists.findList({
currentIndexPosition,
filter,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
const [validated, errors] = validate(exceptionList, findListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,55 +15,60 @@ import { buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const readListIndexRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: LIST_INDEX,
validate: false,
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
})
.addVersion(
{
validate: false,
version: '2023-10-31',
},
async (context, _, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const listDataStreamExists = await lists.getListDataStreamExists();
const listItemDataStreamExists = await lists.getListItemDataStreamExists();
try {
const lists = await getListClient(context);
const listDataStreamExists = await lists.getListDataStreamExists();
const listItemDataStreamExists = await lists.getListItemDataStreamExists();
if (listDataStreamExists && listItemDataStreamExists) {
const [validated, errors] = validate(
{ list_index: listDataStreamExists, list_item_index: listItemDataStreamExists },
readListIndexResponse
);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (listDataStreamExists && listItemDataStreamExists) {
const [validated, errors] = validate(
{ list_index: listDataStreamExists, list_item_index: listItemDataStreamExists },
readListIndexResponse
);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else if (!listDataStreamExists && listItemDataStreamExists) {
return siemResponse.error({
body: `data stream ${lists.getListName()} does not exist`,
statusCode: 404,
});
} else if (!listItemDataStreamExists && listDataStreamExists) {
return siemResponse.error({
body: `data stream ${lists.getListItemName()} does not exist`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
return siemResponse.error({
body: `data stream ${lists.getListName()} and data stream ${lists.getListItemName()} does not exist`,
statusCode: 404,
});
}
} else if (!listDataStreamExists && listItemDataStreamExists) {
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: `data stream ${lists.getListName()} does not exist`,
statusCode: 404,
});
} else if (!listItemDataStreamExists && listDataStreamExists) {
return siemResponse.error({
body: `data stream ${lists.getListItemName()} does not exist`,
statusCode: 404,
});
} else {
return siemResponse.error({
body: `data stream ${lists.getListName()} and data stream ${lists.getListItemName()} does not exist`,
statusCode: 404,
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,67 +15,74 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const createListItemRoute = (router: ListsPluginRouter): void => {
router.post(
{
router.versioned
.post({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_ITEM_URL,
validate: {
body: buildRouteValidation(createListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(createListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value, meta } = request.body;
const lists = await getListClient(context);
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
if (id != null) {
const listItem = await lists.getListItem({ id });
if (listItem != null) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value, meta } = request.body;
const lists = await getListClient(context);
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
if (id != null) {
const listItem = await lists.getListItem({ id });
if (listItem != null) {
return siemResponse.error({
body: `list item id: "${id}" already exists`,
statusCode: 409,
});
}
}
const createdListItem = await lists.createListItem({
deserializer: list.deserializer,
id,
listId,
meta,
serializer: list.serializer,
type: list.type,
value,
});
if (createdListItem != null) {
const [validated, errors] = validate(createdListItem, createListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else {
return siemResponse.error({
body: `list item id: "${id}" already exists`,
statusCode: 409,
body: 'list item invalid',
statusCode: 400,
});
}
}
const createdListItem = await lists.createListItem({
deserializer: list.deserializer,
id,
listId,
meta,
serializer: list.serializer,
type: list.type,
value,
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
if (createdListItem != null) {
const [validated, errors] = validate(createdListItem, createListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
} else {
return siemResponse.error({
body: 'list item invalid',
statusCode: 400,
});
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,76 +19,83 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const deleteListItemRoute = (router: ListsPluginRouter): void => {
router.delete(
{
router.versioned
.delete({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_ITEM_URL,
validate: {
query: buildRouteValidation(deleteListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(deleteListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value } = request.query;
const lists = await getListClient(context);
if (id != null) {
const deleted = await lists.deleteListItem({ id });
if (deleted == null) {
return siemResponse.error({
body: `list item with id: "${id}" not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(deleted, deleteListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else if (listId != null && value != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list_id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
const deleted = await lists.deleteListItemByValue({
listId,
type: list.type,
value,
});
if (deleted == null || deleted.length === 0) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value } = request.query;
const lists = await getListClient(context);
if (id != null) {
const deleted = await lists.deleteListItem({ id });
if (deleted == null) {
return siemResponse.error({
body: `list_id: "${listId}" with ${value} was not found`,
body: `list item with id: "${id}" not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(deleted, deleteListItemArrayResponse);
const [validated, errors] = validate(deleted, deleteListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else if (listId != null && value != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list_id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
const deleted = await lists.deleteListItemByValue({
listId,
type: list.type,
value,
});
if (deleted == null || deleted.length === 0) {
return siemResponse.error({
body: `list_id: "${listId}" with ${value} was not found`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(deleted, deleteListItemArrayResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} else {
return siemResponse.error({
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
});
}
} else {
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,85 +19,92 @@ import {
import { buildRouteValidation, buildSiemResponse, getListClient } from '../utils';
export const findListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: `${LIST_ITEM_URL}/_find`,
validate: {
query: buildRouteValidation<
typeof findListItemRequestQuery,
FindListItemRequestQueryDecoded
>(findListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof findListItemRequestQuery,
FindListItemRequestQueryDecoded
>(findListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
list_id: listId,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const lists = await getListClient(context);
const {
cursor,
filter: filterOrUndefined,
list_id: listId,
page: pageOrUndefined,
per_page: perPageOrUndefined,
sort_field: sortField,
sort_order: sortOrder,
} = request.query;
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter = []],
} = decodeCursor({
cursor,
page,
perPage,
sortField,
});
if (!isValid) {
return siemResponse.error({
body: errorMessage,
statusCode: 400,
});
} else {
const exceptionList = await lists.findListItem({
currentIndexPosition,
filter,
listId,
const page = pageOrUndefined ?? 1;
const perPage = perPageOrUndefined ?? 20;
const filter = filterOrUndefined ?? '';
const {
isValid,
errorMessage,
cursor: [currentIndexPosition, searchAfter = []],
} = decodeCursor({
cursor,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
if (exceptionList == null) {
if (!isValid) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
body: errorMessage,
statusCode: 400,
});
} else {
const [validated, errors] = validate(exceptionList, findListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const exceptionList = await lists.findListItem({
currentIndexPosition,
filter,
listId,
page,
perPage,
runtimeMappings: undefined,
searchAfter,
sortField,
sortOrder,
});
if (exceptionList == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(exceptionList, findListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,57 +15,64 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const patchListItemRoute = (router: ListsPluginRouter): void => {
router.patch(
{
router.versioned
.patch({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_ITEM_URL,
validate: {
body: buildRouteValidation(patchListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(patchListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { value, id, meta, _version } = request.body;
const lists = await getListClient(context);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { value, id, meta, _version } = request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListItemDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListItemIndexExists();
if (indexExists) {
await lists.migrateListItemIndexToDataStream();
const dataStreamExists = await lists.getListItemDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListItemIndexExists();
if (indexExists) {
await lists.migrateListItemIndexToDataStream();
}
}
}
const listItem = await lists.patchListItem({
_version,
id,
meta,
value,
});
if (listItem == null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
const listItem = await lists.patchListItem({
_version,
id,
meta,
value,
});
} else {
const [validated, errors] = validate(listItem, patchListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (listItem == null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(listItem, patchListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -19,76 +19,83 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const readListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: LIST_ITEM_URL,
validate: {
query: buildRouteValidation(readListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation(readListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value } = request.query;
const lists = await getListClient(context);
if (id != null) {
const listItem = await lists.getListItem({ id });
if (listItem == null) {
return siemResponse.error({
body: `list item id: "${id}" does not exist`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(listItem, readListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else if (listId != null && value != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
const listItem = await lists.getListItemByValue({
listId,
type: list.type,
value,
});
if (listItem.length === 0) {
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, value } = request.query;
const lists = await getListClient(context);
if (id != null) {
const listItem = await lists.getListItem({ id });
if (listItem == null) {
return siemResponse.error({
body: `list_id: "${listId}" item of ${value} does not exist`,
body: `list item id: "${id}" does not exist`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(listItem, readListItemArrayResponse);
const [validated, errors] = validate(listItem, readListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else if (listId != null && value != null) {
const list = await lists.getList({ id: listId });
if (list == null) {
return siemResponse.error({
body: `list id: "${listId}" does not exist`,
statusCode: 404,
});
} else {
const listItem = await lists.getListItemByValue({
listId,
type: list.type,
value,
});
if (listItem.length === 0) {
return siemResponse.error({
body: `list_id: "${listId}" item of ${value} does not exist`,
statusCode: 404,
});
} else {
const [validated, errors] = validate(listItem, readListItemArrayResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} else {
return siemResponse.error({
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
});
}
} else {
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: 'Either "list_id" or "id" needs to be defined in the request',
statusCode: 400,
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -15,57 +15,64 @@ import { buildRouteValidation, buildSiemResponse } from '../utils';
import { getListClient } from '..';
export const updateListItemRoute = (router: ListsPluginRouter): void => {
router.put(
{
router.versioned
.put({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: LIST_ITEM_URL,
validate: {
body: buildRouteValidation(updateListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation(updateListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { value, id, meta, _version } = request.body;
const lists = await getListClient(context);
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { value, id, meta, _version } = request.body;
const lists = await getListClient(context);
const dataStreamExists = await lists.getListItemDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListItemIndexExists();
if (indexExists) {
await lists.migrateListItemIndexToDataStream();
const dataStreamExists = await lists.getListItemDataStreamExists();
// needs to be migrated to data stream if index exists
if (!dataStreamExists) {
const indexExists = await lists.getListItemIndexExists();
if (indexExists) {
await lists.migrateListItemIndexToDataStream();
}
}
}
const listItem = await lists.updateListItem({
_version,
id,
meta,
value,
});
if (listItem == null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
const listItem = await lists.updateListItem({
_version,
id,
meta,
value,
});
} else {
const [validated, errors] = validate(listItem, updateListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (listItem == null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(listItem, updateListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -13,38 +13,46 @@ import type { ListsPluginRouter } from '../../types';
import { buildSiemResponse, getListClient } from '../utils';
export const readPrivilegesRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: LIST_PRIVILEGES_URL,
validate: false,
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const lists = await getListClient(context);
const clusterPrivilegesLists = await readPrivileges(esClient, lists.getListName());
const clusterPrivilegesListItems = await readPrivileges(esClient, lists.getListItemName());
const privileges = merge(
{
listItems: clusterPrivilegesListItems,
lists: clusterPrivilegesLists,
},
{
is_authenticated: request.auth.isAuthenticated ?? false,
}
);
return response.ok({ body: privileges });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
})
.addVersion(
{
validate: false,
version: '2023-10-31',
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const lists = await getListClient(context);
const clusterPrivilegesLists = await readPrivileges(esClient, lists.getListName());
const clusterPrivilegesListItems = await readPrivileges(
esClient,
lists.getListItemName()
);
const privileges = merge(
{
listItems: clusterPrivilegesListItems,
lists: clusterPrivilegesLists,
},
{
is_authenticated: request.auth.isAuthenticated ?? false,
}
);
return response.ok({ body: privileges });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
}
);
);
};

View file

@ -24,52 +24,59 @@ import {
} from './utils';
export const readEndpointListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: ENDPOINT_LIST_ITEM_URL,
validate: {
query: buildRouteValidation<
typeof readEndpointListItemRequestQuery,
ReadEndpointListItemRequestQueryDecoded
>(readEndpointListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof readEndpointListItemRequestQuery,
ReadEndpointListItemRequestQueryDecoded
>(readEndpointListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, item_id: itemId } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || itemId != null) {
const exceptionListItem = await exceptionLists.getEndpointListItem({
id,
itemId,
});
if (exceptionListItem == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, item_id: itemId } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || itemId != null) {
const exceptionListItem = await exceptionLists.getEndpointListItem({
id,
itemId,
});
} else {
const [validated, errors] = validate(exceptionListItem, readEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (exceptionListItem == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(exceptionListItem, readEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else {
return siemResponse.error({ body: 'id or item_id required', statusCode: 400 });
}
} else {
return siemResponse.error({ body: 'id or item_id required', statusCode: 400 });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,53 +24,63 @@ import {
} from './utils';
export const readExceptionListItemRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: EXCEPTION_LIST_ITEM_URL,
validate: {
query: buildRouteValidation<
typeof readExceptionListItemRequestQuery,
ReadExceptionListItemRequestQueryDecoded
>(readExceptionListItemRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof readExceptionListItemRequestQuery,
ReadExceptionListItemRequestQueryDecoded
>(readExceptionListItemRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, item_id: itemId, namespace_type: namespaceType } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || itemId != null) {
const exceptionListItem = await exceptionLists.getExceptionListItem({
id,
itemId,
namespaceType,
});
if (exceptionListItem == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, item_id: itemId, namespace_type: namespaceType } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || itemId != null) {
const exceptionListItem = await exceptionLists.getExceptionListItem({
id,
itemId,
namespaceType,
});
} else {
const [validated, errors] = validate(exceptionListItem, readExceptionListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (exceptionListItem == null) {
return siemResponse.error({
body: getErrorMessageExceptionListItem({ id, itemId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(
exceptionListItem,
readExceptionListItemResponse
);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else {
return siemResponse.error({ body: 'id or item_id required', statusCode: 400 });
}
} else {
return siemResponse.error({ body: 'id or item_id required', statusCode: 400 });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,53 +24,60 @@ import {
} from './utils';
export const readExceptionListRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-read'],
},
path: EXCEPTION_LIST_URL,
validate: {
query: buildRouteValidation<
typeof readExceptionListRequestQuery,
ReadExceptionListRequestQueryDecoded
>(readExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof readExceptionListRequestQuery,
ReadExceptionListRequestQueryDecoded
>(readExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, namespace_type: namespaceType } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || listId != null) {
const exceptionList = await exceptionLists.getExceptionList({
id,
listId,
namespaceType,
});
if (exceptionList == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, namespace_type: namespaceType } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || listId != null) {
const exceptionList = await exceptionLists.getExceptionList({
id,
listId,
namespaceType,
});
} else {
const [validated, errors] = validate(exceptionList, readExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
if (exceptionList == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(exceptionList, readExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else {
return siemResponse.error({ body: 'id or list_id required', statusCode: 400 });
}
} else {
return siemResponse.error({ body: 'id or list_id required', statusCode: 400 });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,57 +24,64 @@ import {
} from './utils';
export const summaryExceptionListRoute = (router: ListsPluginRouter): void => {
router.get(
{
router.versioned
.get({
access: 'public',
options: {
tags: ['access:lists-summary'],
},
path: `${EXCEPTION_LIST_URL}/summary`,
validate: {
query: buildRouteValidation<
typeof summaryExceptionListRequestQuery,
SummaryExceptionListRequestQueryDecoded
>(summaryExceptionListRequestQuery),
})
.addVersion(
{
validate: {
request: {
query: buildRouteValidation<
typeof summaryExceptionListRequestQuery,
SummaryExceptionListRequestQueryDecoded
>(summaryExceptionListRequestQuery),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, namespace_type: namespaceType, filter } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || listId != null) {
const exceptionListSummary = await exceptionLists.getExceptionListSummary({
filter,
id,
listId,
namespaceType,
});
if (exceptionListSummary == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const { id, list_id: listId, namespace_type: namespaceType, filter } = request.query;
const exceptionLists = await getExceptionListClient(context);
if (id != null || listId != null) {
const exceptionListSummary = await exceptionLists.getExceptionListSummary({
filter,
id,
listId,
namespaceType,
});
} else {
const [validated, errors] = validate(
exceptionListSummary,
summaryExceptionListResponse
);
if (errors != null) {
return response.ok({ body: exceptionListSummary });
if (exceptionListSummary == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(
exceptionListSummary,
summaryExceptionListResponse
);
if (errors != null) {
return response.ok({ body: exceptionListSummary });
} else {
return response.ok({ body: validated ?? {} });
}
}
} else {
return siemResponse.error({ body: 'id or list_id required', statusCode: 400 });
}
} else {
return siemResponse.error({ body: 'id or list_id required', statusCode: 400 });
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -21,76 +21,83 @@ import { buildRouteValidation, buildSiemResponse } from './utils';
import { getExceptionListClient } from '.';
export const updateEndpointListItemRoute = (router: ListsPluginRouter): void => {
router.put(
{
router.versioned
.put({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: ENDPOINT_LIST_ITEM_URL,
validate: {
body: buildRouteValidation<
typeof updateEndpointListItemRequest,
UpdateEndpointListItemRequestDecoded
>(updateEndpointListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof updateEndpointListItemRequest,
UpdateEndpointListItemRequestDecoded
>(updateEndpointListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
description,
id,
name,
os_types: osTypes,
meta,
type,
_version,
comments,
entries,
item_id: itemId,
tags,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.updateEndpointListItem({
_version,
comments,
description,
entries,
id,
itemId,
meta,
name,
osTypes,
tags,
type,
});
if (exceptionListItem == null) {
if (id != null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
});
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
description,
id,
name,
os_types: osTypes,
meta,
type,
_version,
comments,
entries,
item_id: itemId,
tags,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.updateEndpointListItem({
_version,
comments,
description,
entries,
id,
itemId,
meta,
name,
osTypes,
tags,
type,
});
if (exceptionListItem == null) {
if (id != null) {
return siemResponse.error({
body: `list item id: "${id}" not found`,
statusCode: 404,
});
} else {
return siemResponse.error({
body: `list item item_id: "${itemId}" not found`,
statusCode: 404,
});
}
} else {
return siemResponse.error({
body: `list item item_id: "${itemId}" not found`,
statusCode: 404,
});
}
} else {
const [validated, errors] = validate(exceptionListItem, updateEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(exceptionListItem, updateEndpointListItemResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -22,95 +22,102 @@ import { buildRouteValidation, buildSiemResponse } from './utils';
import { getExceptionListClient } from '.';
export const updateExceptionListItemRoute = (router: ListsPluginRouter): void => {
router.put(
{
router.versioned
.put({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_ITEM_URL,
validate: {
body: buildRouteValidation<
typeof updateExceptionListItemRequest,
UpdateExceptionListItemRequestDecoded
>(updateExceptionListItemRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof updateExceptionListItemRequest,
UpdateExceptionListItemRequestDecoded
>(updateExceptionListItemRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
const validationErrors = updateExceptionListItemValidate(request.body);
if (validationErrors.length) {
return siemResponse.error({ body: validationErrors, statusCode: 400 });
}
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
const validationErrors = updateExceptionListItemValidate(request.body);
if (validationErrors.length) {
return siemResponse.error({ body: validationErrors, statusCode: 400 });
}
try {
const {
description,
id,
name,
meta,
type,
_version,
comments,
entries,
item_id: itemId,
namespace_type: namespaceType,
os_types: osTypes,
tags,
expire_time: expireTime,
} = request.body;
if (id == null && itemId == null) {
return siemResponse.error({
body: 'either id or item_id need to be defined',
statusCode: 404,
});
} else {
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.updateOverwriteExceptionListItem({
try {
const {
description,
id,
name,
meta,
type,
_version,
comments,
description,
entries,
expireTime,
id,
itemId,
meta,
name,
namespaceType,
osTypes,
item_id: itemId,
namespace_type: namespaceType,
os_types: osTypes,
tags,
type,
});
if (exceptionListItem == null) {
if (id != null) {
return siemResponse.error({
body: `exception list item id: "${id}" does not exist`,
statusCode: 404,
});
} else {
return siemResponse.error({
body: `exception list item item_id: "${itemId}" does not exist`,
statusCode: 404,
});
}
expire_time: expireTime,
} = request.body;
if (id == null && itemId == null) {
return siemResponse.error({
body: 'either id or item_id need to be defined',
statusCode: 404,
});
} else {
const [validated, errors] = validate(
exceptionListItem,
updateExceptionListItemResponse
);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const exceptionLists = await getExceptionListClient(context);
const exceptionListItem = await exceptionLists.updateOverwriteExceptionListItem({
_version,
comments,
description,
entries,
expireTime,
id,
itemId,
meta,
name,
namespaceType,
osTypes,
tags,
type,
});
if (exceptionListItem == null) {
if (id != null) {
return siemResponse.error({
body: `exception list item id: "${id}" does not exist`,
statusCode: 404,
});
} else {
return siemResponse.error({
body: `exception list item item_id: "${itemId}" does not exist`,
statusCode: 404,
});
}
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(
exceptionListItem,
updateExceptionListItemResponse
);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -24,76 +24,83 @@ import {
} from './utils';
export const updateExceptionListRoute = (router: ListsPluginRouter): void => {
router.put(
{
router.versioned
.put({
access: 'public',
options: {
tags: ['access:lists-all'],
},
path: EXCEPTION_LIST_URL,
validate: {
body: buildRouteValidation<
typeof updateExceptionListRequest,
UpdateExceptionListRequestDecoded
>(updateExceptionListRequest),
})
.addVersion(
{
validate: {
request: {
body: buildRouteValidation<
typeof updateExceptionListRequest,
UpdateExceptionListRequestDecoded
>(updateExceptionListRequest),
},
},
version: '2023-10-31',
},
},
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
_version,
tags,
name,
description,
id,
list_id: listId,
meta,
namespace_type: namespaceType,
os_types: osTypes,
type,
version,
} = request.body;
const exceptionLists = await getExceptionListClient(context);
if (id == null && listId == null) {
return siemResponse.error({
body: 'either id or list_id need to be defined',
statusCode: 404,
});
} else {
const list = await exceptionLists.updateExceptionList({
async (context, request, response) => {
const siemResponse = buildSiemResponse(response);
try {
const {
_version,
tags,
name,
description,
id,
listId,
list_id: listId,
meta,
name,
namespaceType,
osTypes,
tags,
namespace_type: namespaceType,
os_types: osTypes,
type,
version,
});
if (list == null) {
} = request.body;
const exceptionLists = await getExceptionListClient(context);
if (id == null && listId == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
body: 'either id or list_id need to be defined',
statusCode: 404,
});
} else {
const [validated, errors] = validate(list, updateExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
const list = await exceptionLists.updateExceptionList({
_version,
description,
id,
listId,
meta,
name,
namespaceType,
osTypes,
tags,
type,
version,
});
if (list == null) {
return siemResponse.error({
body: getErrorMessageExceptionList({ id, listId }),
statusCode: 404,
});
} else {
return response.ok({ body: validated ?? {} });
const [validated, errors] = validate(list, updateExceptionListResponse);
if (errors != null) {
return siemResponse.error({ body: errors, statusCode: 500 });
} else {
return response.ok({ body: validated ?? {} });
}
}
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
} catch (err) {
const error = transformError(err);
return siemResponse.error({
body: error.message,
statusCode: error.statusCode,
});
}
}
);
);
};

View file

@ -69,6 +69,7 @@ describe('Exceptions List Api Client', () => {
expect(fakeHttpServices.post).toHaveBeenCalledWith(
INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL,
{
version: '1',
body: JSON.stringify(getFakeListDefinition()),
}
);

View file

@ -62,6 +62,7 @@ export class ExceptionsListApiClient {
const asyncFunction = async () => {
try {
await this.http.post<ExceptionListSchema>(INTERNAL_EXCEPTIONS_LIST_ENSURE_CREATED_URL, {
version: '1',
body: JSON.stringify({ ...this.listDefinition, list_id: this.listId }),
});

View file

@ -37,6 +37,7 @@ export default ({ getService }: FtrProviderContext): void => {
const { body } = await supertest
.get(`${INTERNAL_FIND_LISTS_BY_SIZE}`)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.send()
.expect(200);
@ -65,6 +66,7 @@ export default ({ getService }: FtrProviderContext): void => {
const { body } = await supertest
.get(`${INTERNAL_FIND_LISTS_BY_SIZE}`)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.send()
.expect(200);

View file

@ -41,6 +41,7 @@ export default ({ getService }: FtrProviderContext): void => {
const { body } = await supertest
.post(`${INTERNAL_EXCEPTION_FILTER}`)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.send(getExceptionFilterFromExceptionItemsSchemaMock())
.expect(200);
@ -121,6 +122,7 @@ export default ({ getService }: FtrProviderContext): void => {
const { body } = await supertest
.post(`${INTERNAL_EXCEPTION_FILTER}`)
.set('kbn-xsrf', 'true')
.set('elastic-api-version', '1')
.send(getExceptionFilterFromExceptionIdsSchemaMock())
.expect(200);