diff --git a/x-pack/plugins/cases/common/types/api/case/v1.test.ts b/x-pack/plugins/cases/common/types/api/case/v1.test.ts
index 40c980d633545..91676b7ca6510 100644
--- a/x-pack/plugins/cases/common/types/api/case/v1.test.ts
+++ b/x-pack/plugins/cases/common/types/api/case/v1.test.ts
@@ -5,6 +5,18 @@
* 2.0.
*/
+import {
+ MAX_CATEGORY_FILTER_LENGTH,
+ MAX_TAGS_FILTER_LENGTH,
+ MAX_ASSIGNEES_FILTER_LENGTH,
+ MAX_REPORTERS_FILTER_LENGTH,
+ MAX_ASSIGNEES_PER_CASE,
+ MAX_DESCRIPTION_LENGTH,
+ MAX_TAGS_PER_CASE,
+ MAX_LENGTH_PER_TAG,
+ MAX_TITLE_LENGTH,
+ MAX_CATEGORY_LENGTH,
+} from '../../../constants';
import { PathReporter } from 'io-ts/lib/PathReporter';
import { AttachmentType } from '../../domain/attachment/v1';
import { CaseSeverity, CaseStatuses } from '../../domain/case/v1';
@@ -187,6 +199,66 @@ describe('Status', () => {
right: defaultRequest,
});
});
+
+ it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
+ const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });
+
+ expect(
+ PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, assignees }))
+ ).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
+ });
+
+ it('does not throw an error with empty assignees', async () => {
+ expect(
+ PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, assignees: [] }))
+ ).toContain('No errors!');
+ });
+
+ it('does not throw an error with undefined assignees', async () => {
+ const { assignees, ...rest } = defaultRequest;
+
+ expect(PathReporter.report(CasePostRequestRt.decode(rest))).toContain('No errors!');
+ });
+
+ it(`throws an error when the description contains more than ${MAX_DESCRIPTION_LENGTH} characters`, async () => {
+ const description = 'a'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+
+ expect(
+ PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, description }))
+ ).toContain('The length of the description is too long. The maximum length is 30000.');
+ });
+
+ it(`throws an error when there are more than ${MAX_TAGS_PER_CASE} tags`, async () => {
+ const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foobar');
+
+ expect(PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, tags }))).toContain(
+ 'The length of the field tags is too long. Array must be of length <= 200.'
+ );
+ });
+
+ it(`throws an error when the a tag is more than ${MAX_LENGTH_PER_TAG} characters`, async () => {
+ const tag = 'a'.repeat(MAX_LENGTH_PER_TAG + 1);
+
+ expect(
+ PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, tags: [tag] }))
+ ).toContain('The length of the tag is too long. The maximum length is 256.');
+ });
+
+ it(`throws an error when the title contains more than ${MAX_TITLE_LENGTH} characters`, async () => {
+ const title = 'a'.repeat(MAX_TITLE_LENGTH + 1);
+
+ expect(PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, title }))).toContain(
+ 'The length of the title is too long. The maximum length is 160.'
+ );
+ });
+
+ it(`throws an error when the category contains more than ${MAX_CATEGORY_LENGTH} characters`, async () => {
+ const category = 'a'.repeat(MAX_CATEGORY_LENGTH + 1);
+
+ expect(
+ PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, category }))
+ ).toContain('The length of the category is too long. The maximum length is 50.');
+ });
});
describe('CasesFindRequestRt', () => {
@@ -276,6 +348,38 @@ describe('Status', () => {
'No errors!'
);
});
+
+ it(`throws an error when the category array has ${MAX_CATEGORY_FILTER_LENGTH} items`, async () => {
+ const category = Array(MAX_CATEGORY_FILTER_LENGTH + 1).fill('foobar');
+
+ expect(PathReporter.report(CasesFindRequestRt.decode({ category }))).toContain(
+ 'The length of the field category is too long. Array must be of length <= 100.'
+ );
+ });
+
+ it(`throws an error when the tags array has ${MAX_TAGS_FILTER_LENGTH} items`, async () => {
+ const tags = Array(MAX_TAGS_FILTER_LENGTH + 1).fill('foobar');
+
+ expect(PathReporter.report(CasesFindRequestRt.decode({ tags }))).toContain(
+ 'The length of the field tags is too long. Array must be of length <= 100.'
+ );
+ });
+
+ it(`throws an error when the assignees array has ${MAX_ASSIGNEES_FILTER_LENGTH} items`, async () => {
+ const assignees = Array(MAX_ASSIGNEES_FILTER_LENGTH + 1).fill('foobar');
+
+ expect(PathReporter.report(CasesFindRequestRt.decode({ assignees }))).toContain(
+ 'The length of the field assignees is too long. Array must be of length <= 100.'
+ );
+ });
+
+ it(`throws an error when the reporters array has ${MAX_REPORTERS_FILTER_LENGTH} items`, async () => {
+ const reporters = Array(MAX_REPORTERS_FILTER_LENGTH + 1).fill('foobar');
+
+ expect(PathReporter.report(CasesFindRequestRt.decode({ reporters }))).toContain(
+ 'The length of the field reporters is too long. Array must be of length <= 100.'
+ );
+ });
});
});
});
@@ -393,6 +497,64 @@ describe('CasePatchRequestRt', () => {
right: defaultRequest,
});
});
+
+ it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
+ const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });
+
+ expect(
+ PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, assignees }))
+ ).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
+ });
+
+ it('does not throw an error with empty assignees', async () => {
+ expect(
+ PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, assignees: [] }))
+ ).toContain('No errors!');
+ });
+
+ it('does not throw an error with undefined assignees', async () => {
+ expect(PathReporter.report(CasePatchRequestRt.decode(defaultRequest))).toContain('No errors!');
+ });
+
+ it(`throws an error when the description contains more than ${MAX_DESCRIPTION_LENGTH} characters`, async () => {
+ const description = 'a'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+
+ expect(
+ PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, description }))
+ ).toContain('The length of the description is too long. The maximum length is 30000.');
+ });
+
+ it(`throws an error when there are more than ${MAX_TAGS_PER_CASE} tags`, async () => {
+ const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foobar');
+
+ expect(PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, tags }))).toContain(
+ 'The length of the field tags is too long. Array must be of length <= 200.'
+ );
+ });
+
+ it(`throws an error when the a tag is more than ${MAX_LENGTH_PER_TAG} characters`, async () => {
+ const tag = 'a'.repeat(MAX_LENGTH_PER_TAG + 1);
+
+ expect(
+ PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, tags: [tag] }))
+ ).toContain('The length of the tag is too long. The maximum length is 256.');
+ });
+
+ it(`throws an error when the title contains more than ${MAX_TITLE_LENGTH} characters`, async () => {
+ const title = 'a'.repeat(MAX_TITLE_LENGTH + 1);
+
+ expect(PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, title }))).toContain(
+ 'The length of the title is too long. The maximum length is 160.'
+ );
+ });
+
+ it(`throws an error when the category contains more than ${MAX_CATEGORY_LENGTH} characters`, async () => {
+ const category = 'a'.repeat(MAX_CATEGORY_LENGTH + 1);
+
+ expect(
+ PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, category }))
+ ).toContain('The length of the category is too long. The maximum length is 50.');
+ });
});
describe('CasesPatchRequestRt', () => {
@@ -423,6 +585,24 @@ describe('CasesPatchRequestRt', () => {
right: defaultRequest,
});
});
+
+ it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
+ const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });
+
+ expect(
+ PathReporter.report(
+ CasesPatchRequestRt.decode({
+ cases: [
+ {
+ id: 'basic-case-id',
+ version: 'WzQ3LDFd',
+ assignees,
+ },
+ ],
+ })
+ )
+ ).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
+ });
});
describe('CasePushRequestParamsRt', () => {
diff --git a/x-pack/plugins/cases/common/types/api/case/v1.ts b/x-pack/plugins/cases/common/types/api/case/v1.ts
index 97981f90fb8dc..7a92c1f32ca12 100644
--- a/x-pack/plugins/cases/common/types/api/case/v1.ts
+++ b/x-pack/plugins/cases/common/types/api/case/v1.ts
@@ -19,6 +19,8 @@ import {
MAX_TAGS_FILTER_LENGTH,
MAX_CASES_TO_UPDATE,
MAX_BULK_GET_CASES,
+ MAX_CATEGORY_FILTER_LENGTH,
+ MAX_ASSIGNEES_PER_CASE,
} from '../../../constants';
import {
limitedStringSchema,
@@ -35,7 +37,7 @@ import {
RelatedCaseRt,
} from '../../domain/case/v1';
import { CaseConnectorRt } from '../../domain/connector/v1';
-import { CaseAssigneesRt, UserRt } from '../../domain/user/v1';
+import { CaseUserProfileRt, UserRt } from '../../domain/user/v1';
import { CasesStatusResponseRt } from '../stats/v1';
/**
@@ -84,7 +86,12 @@ export const CasePostRequestRt = rt.intersection([
/**
* The users assigned to the case
*/
- assignees: CaseAssigneesRt,
+ assignees: limitedArraySchema({
+ codec: CaseUserProfileRt,
+ fieldName: 'assignees',
+ min: 0,
+ max: MAX_ASSIGNEES_PER_CASE,
+ }),
/**
* The severity of the case. The severity is
* default it to "low" if not provided.
@@ -214,7 +221,15 @@ export const CasesFindRequestRt = rt.intersection([
/**
* The category of the case.
*/
- category: rt.union([rt.array(rt.string), rt.string]),
+ category: rt.union([
+ limitedArraySchema({
+ codec: rt.string,
+ fieldName: 'category',
+ min: 0,
+ max: MAX_CATEGORY_FILTER_LENGTH,
+ }),
+ rt.string,
+ ]),
})
),
paginationSchema({ maxPerPage: MAX_CASES_PER_PAGE }),
@@ -330,7 +345,12 @@ export const CasePatchRequestRt = rt.intersection([
/**
* The users assigned to this case
*/
- assignees: CaseAssigneesRt,
+ assignees: limitedArraySchema({
+ codec: CaseUserProfileRt,
+ fieldName: 'assignees',
+ min: 0,
+ max: MAX_ASSIGNEES_PER_CASE,
+ }),
/**
* The category of the case.
*/
diff --git a/x-pack/plugins/cases/server/client/cases/create.test.ts b/x-pack/plugins/cases/server/client/cases/create.test.ts
index 9918cc302c033..61672e70d8e3d 100644
--- a/x-pack/plugins/cases/server/client/cases/create.test.ts
+++ b/x-pack/plugins/cases/server/client/cases/create.test.ts
@@ -10,6 +10,7 @@ import {
MAX_TAGS_PER_CASE,
MAX_LENGTH_PER_TAG,
MAX_TITLE_LENGTH,
+ MAX_ASSIGNEES_PER_CASE,
} from '../../../common/constants';
import { SECURITY_SOLUTION_OWNER } from '../../../common';
import { mockCases } from '../../mocks';
@@ -82,6 +83,14 @@ describe('create', () => {
theCase: caseSO,
});
});
+
+ it('should throw an error if the assignees array length is too long', async () => {
+ const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foo' });
+
+ await expect(create({ ...theCase, assignees }, clientArgs)).rejects.toThrow(
+ `Failed to create case: Error: The length of the field assignees is too long. Array must be of length <= ${MAX_ASSIGNEES_PER_CASE}.`
+ );
+ });
});
describe('Attributes', () => {
diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts
index 9467fe9ecc887..30524c1db6595 100644
--- a/x-pack/plugins/cases/server/client/cases/create.ts
+++ b/x-pack/plugins/cases/server/client/cases/create.ts
@@ -12,8 +12,6 @@ import { SavedObjectsUtils } from '@kbn/core/server';
import type { Case } from '../../../common/types/domain';
import { CaseSeverity, UserActionTypes, CaseRt } from '../../../common/types/domain';
import { decodeWithExcessOrThrow } from '../../../common/api';
-import { MAX_ASSIGNEES_PER_CASE } from '../../../common/constants';
-import { areTotalAssigneesInvalid } from '../../../common/utils/validators';
import { Operations } from '../../authorization';
import { createCaseError } from '../../common/error';
@@ -63,12 +61,6 @@ export const create = async (data: CasePostRequest, clientArgs: CasesClientArgs)
licensingService.notifyUsage(LICENSING_CASE_ASSIGNMENT_FEATURE);
}
- if (areTotalAssigneesInvalid(query.assignees)) {
- throw Boom.badRequest(
- `You cannot assign more than ${MAX_ASSIGNEES_PER_CASE} assignees to a case.`
- );
- }
-
const newCase = await caseService.postNewCase({
attributes: transformNewCase({
user,
diff --git a/x-pack/plugins/cases/server/client/cases/find.test.ts b/x-pack/plugins/cases/server/client/cases/find.test.ts
index 0a09fa8b5b7b2..8acb91b40c383 100644
--- a/x-pack/plugins/cases/server/client/cases/find.test.ts
+++ b/x-pack/plugins/cases/server/client/cases/find.test.ts
@@ -128,7 +128,7 @@ describe('find', () => {
const findRequest = createCasesClientMockFindRequest({ category });
await expect(find(findRequest, clientArgs)).rejects.toThrow(
- `Error: Too many categories provided. The maximum allowed is ${MAX_CATEGORY_FILTER_LENGTH}`
+ `Error: The length of the field category is too long. Array must be of length <= ${MAX_CATEGORY_FILTER_LENGTH}`
);
});
diff --git a/x-pack/plugins/cases/server/client/cases/find.ts b/x-pack/plugins/cases/server/client/cases/find.ts
index 9c8699ce9db84..c61c4ba71ffde 100644
--- a/x-pack/plugins/cases/server/client/cases/find.ts
+++ b/x-pack/plugins/cases/server/client/cases/find.ts
@@ -10,7 +10,6 @@ import Boom from '@hapi/boom';
import type { CasesFindRequest, CasesFindResponse } from '../../../common/types/api';
import { CasesFindRequestRt, CasesFindResponseRt } from '../../../common/types/api';
-import { MAX_CATEGORY_FILTER_LENGTH } from '../../../common/constants';
import { decodeWithExcessOrThrow } from '../../../common/api';
import { createCaseError } from '../../common/error';
@@ -22,16 +21,6 @@ import { LICENSING_CASE_ASSIGNMENT_FEATURE } from '../../common/constants';
import type { CasesFindQueryParams } from '../types';
import { decodeOrThrow } from '../../../common/api/runtime_types';
-/**
- * Throws an error if the user tries to filter by more than MAX_CATEGORY_FILTER_LENGTH categories.
- */
-function throwIfCategoryParamTooLong(category?: string[] | string) {
- if (Array.isArray(category) && category.length > MAX_CATEGORY_FILTER_LENGTH)
- throw Boom.badRequest(
- `Too many categories provided. The maximum allowed is ${MAX_CATEGORY_FILTER_LENGTH}`
- );
-}
-
/**
* Retrieves a case and optionally its comments.
*
@@ -52,8 +41,6 @@ export const find = async (
try {
const queryParams = decodeWithExcessOrThrow(CasesFindRequestRt)(params);
- throwIfCategoryParamTooLong(queryParams.category);
-
/**
* Assign users to a case is only available to Platinum+
*/
diff --git a/x-pack/plugins/cases/server/client/cases/update.test.ts b/x-pack/plugins/cases/server/client/cases/update.test.ts
index 039f4e8582804..17b51658b1233 100644
--- a/x-pack/plugins/cases/server/client/cases/update.test.ts
+++ b/x-pack/plugins/cases/server/client/cases/update.test.ts
@@ -13,6 +13,7 @@ import {
MAX_TITLE_LENGTH,
MAX_CASES_TO_UPDATE,
MAX_USER_ACTIONS_PER_CASE,
+ MAX_ASSIGNEES_PER_CASE,
} from '../../../common/constants';
import { mockCases } from '../../mocks';
import { createCasesClientMockArgs } from '../mocks';
@@ -279,6 +280,27 @@ describe('update', () => {
`"Failed to update case, ids: [{\\"id\\":\\"mock-id-1\\",\\"version\\":\\"WzAsMV0=\\"}]: Error: invalid keys \\"foo\\""`
);
});
+
+ it('should throw an error if the assignees array length is too long', async () => {
+ const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foo' });
+
+ await expect(
+ update(
+ {
+ cases: [
+ {
+ id: mockCases[0].id,
+ version: mockCases[0].version ?? '',
+ assignees,
+ },
+ ],
+ },
+ clientArgs
+ )
+ ).rejects.toThrow(
+ 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the field assignees is too long. Array must be of length <= 10.'
+ );
+ });
});
describe('Category', () => {
diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts
index 1844b12b3ab07..121fd2a8e3aa5 100644
--- a/x-pack/plugins/cases/server/client/cases/update.ts
+++ b/x-pack/plugins/cases/server/client/cases/update.ts
@@ -25,11 +25,9 @@ import type { PatchCasesArgs } from '../../services/cases/types';
import type { UserActionEvent, UserActionsDict } from '../../services/user_actions/types';
import type { CasePatchRequest, CasesPatchRequest } from '../../../common/types/api';
-import { areTotalAssigneesInvalid } from '../../../common/utils/validators';
import {
CASE_COMMENT_SAVED_OBJECT,
CASE_SAVED_OBJECT,
- MAX_ASSIGNEES_PER_CASE,
MAX_USER_ACTIONS_PER_CASE,
} from '../../../common/constants';
import { Operations } from '../../authorization';
@@ -138,27 +136,6 @@ function notifyPlatinumUsage(
}
}
-/**
- * Throws an error if any of the requests attempt to add more than
- * MAX_ASSIGNEES_PER_CASE to a case
- */
-function throwIfTotalAssigneesAreInvalid(requests: UpdateRequestWithOriginalCase[]) {
- const requestsUpdatingAssignees = requests.filter(
- ({ updateReq }) => updateReq.assignees !== undefined
- );
-
- if (
- requestsUpdatingAssignees.some(({ updateReq }) => areTotalAssigneesInvalid(updateReq.assignees))
- ) {
- const ids = requestsUpdatingAssignees.map(({ updateReq }) => updateReq.id);
- throw Boom.badRequest(
- `You cannot assign more than ${MAX_ASSIGNEES_PER_CASE} assignees to a case, ids: [${ids.join(
- ', '
- )}]`
- );
- }
-}
-
/**
* Get the id from a reference in a comment for a specific type.
*/
@@ -394,7 +371,6 @@ export const update = async (
throwIfUpdateOwner(casesToUpdate);
throwIfUpdateAssigneesWithoutValidLicense(casesToUpdate, hasPlatinumLicense);
- throwIfTotalAssigneesAreInvalid(casesToUpdate);
const patchCasesPayload = createPatchCasesPayload({ user, casesToUpdate });
const userActionsDict = userActionService.creator.buildUserActions({
diff --git a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
index 7d0ba2af2a061..05e4662f27292 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
@@ -7,8 +7,7 @@
import { schema } from '@kbn/config-schema';
-import { FindAttachmentsQueryParamsRt } from '../../../../common/types/api';
-import { decodeWithExcessOrThrow } from '../../../../common/api';
+import type { attachmentApiV1 } from '../../../../common/types/api';
import { CASE_FIND_ATTACHMENTS_URL } from '../../../../common/constants';
import { createCasesRoute } from '../create_cases_route';
import { createCaseError } from '../../../common/error';
@@ -23,15 +22,17 @@ export const findCommentsRoute = createCasesRoute({
},
handler: async ({ context, request, response }) => {
try {
- const query = decodeWithExcessOrThrow(FindAttachmentsQueryParamsRt)(request.query);
-
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
+ const query = request.query as attachmentApiV1.FindAttachmentsQueryParams;
+
+ const res: attachmentApiV1.AttachmentsFindResponse = await client.attachments.find({
+ caseID: request.params.case_id,
+ findQueryParams: query,
+ });
+
return response.ok({
- body: await client.attachments.find({
- caseID: request.params.case_id,
- findQueryParams: query,
- }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
index 72b8f7e6ac98b..fffb23921e2d3 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
@@ -10,6 +10,7 @@ import { schema } from '@kbn/config-schema';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
+import type { attachmentDomainV1 } from '../../../../common/types/domain';
/**
* @deprecated since version 8.1.0
@@ -27,11 +28,12 @@ export const getAllCommentsRoute = createCasesRoute({
try {
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
+ const res: attachmentDomainV1.Attachments = await client.attachments.getAll({
+ caseID: request.params.case_id,
+ });
return response.ok({
- body: await client.attachments.getAll({
- caseID: request.params.case_id,
- }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
index 2a863f3ee4df4..6267895f587ce 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
@@ -10,6 +10,7 @@ import { schema } from '@kbn/config-schema';
import { CASE_COMMENT_DETAILS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
+import type { attachmentDomainV1 } from '../../../../common/types/domain';
export const getCommentRoute = createCasesRoute({
method: 'get',
@@ -24,12 +25,13 @@ export const getCommentRoute = createCasesRoute({
try {
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
+ const res: attachmentDomainV1.Attachment = await client.attachments.get({
+ attachmentID: request.params.comment_id,
+ caseID: request.params.case_id,
+ });
return response.ok({
- body: await client.attachments.get({
- attachmentID: request.params.comment_id,
- caseID: request.params.case_id,
- }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
index 8130808e51dcb..99a697cb0bc9a 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
@@ -11,6 +11,7 @@ import { decodeWithExcessOrThrow } from '../../../../common/api';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
+import type { caseDomainV1 } from '../../../../common/types/domain';
export const patchCommentRoute = createCasesRoute({
method: 'patch',
@@ -26,12 +27,13 @@ export const patchCommentRoute = createCasesRoute({
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
+ const res: caseDomainV1.Case = await client.attachments.update({
+ caseID: request.params.case_id,
+ updateRequest: query,
+ });
return response.ok({
- body: await client.attachments.update({
- caseID: request.params.case_id,
- updateRequest: query,
- }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
index 80ce9386252d7..d4d8f4f4f8db2 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
@@ -7,9 +7,10 @@
import { schema } from '@kbn/config-schema';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
-import type { AttachmentRequest } from '../../../../common/types/api';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
+import type { caseDomainV1 } from '../../../../common/types/domain';
+import type { attachmentApiV1 } from '../../../../common/types/api';
export const postCommentRoute = createCasesRoute({
method: 'post',
@@ -24,10 +25,11 @@ export const postCommentRoute = createCasesRoute({
const caseContext = await context.cases;
const casesClient = await caseContext.getCasesClient();
const caseId = request.params.case_id;
- const comment = request.body as AttachmentRequest;
+ const comment = request.body as attachmentApiV1.AttachmentRequest;
+ const res: caseDomainV1.Case = await casesClient.attachments.add({ caseId, comment });
return response.ok({
- body: await casesClient.attachments.add({ caseId, comment }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts b/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts
index e0abb28a58efe..8074d8e626ea7 100644
--- a/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts
+++ b/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts
@@ -6,11 +6,12 @@
*/
import { schema } from '@kbn/config-schema';
-import type { BulkCreateAttachmentsRequest } from '../../../../common/types/api';
import { INTERNAL_BULK_CREATE_ATTACHMENTS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
import { createCasesRoute } from '../create_cases_route';
import { escapeHatch } from '../utils';
+import type { attachmentApiV1 } from '../../../../common/types/api';
+import type { caseDomainV1 } from '../../../../common/types/domain';
export const bulkCreateAttachmentsRoute = createCasesRoute({
method: 'post',
@@ -26,10 +27,14 @@ export const bulkCreateAttachmentsRoute = createCasesRoute({
const casesContext = await context.cases;
const casesClient = await casesContext.getCasesClient();
const caseId = request.params.case_id;
- const attachments = request.body as BulkCreateAttachmentsRequest;
+ const attachments = request.body as attachmentApiV1.BulkCreateAttachmentsRequest;
+ const res: caseDomainV1.Case = await casesClient.attachments.bulkCreate({
+ caseId,
+ attachments,
+ });
return response.ok({
- body: await casesClient.attachments.bulkCreate({ caseId, attachments }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cases/server/routes/api/internal/bulk_delete_file_attachments.ts b/x-pack/plugins/cases/server/routes/api/internal/bulk_delete_file_attachments.ts
index c719d16e7ab16..d1e62f13c633b 100644
--- a/x-pack/plugins/cases/server/routes/api/internal/bulk_delete_file_attachments.ts
+++ b/x-pack/plugins/cases/server/routes/api/internal/bulk_delete_file_attachments.ts
@@ -7,12 +7,13 @@
import { schema } from '@kbn/config-schema';
-import { BulkDeleteFileAttachmentsRequestRt } from '../../../../common/types/api';
+import { decodeWithExcessOrThrow } from '../../../../common/api';
import { INTERNAL_DELETE_FILE_ATTACHMENTS_URL } from '../../../../common/constants';
import { createCasesRoute } from '../create_cases_route';
import { createCaseError } from '../../../common/error';
import { escapeHatch } from '../utils';
-import { decodeWithExcessOrThrow } from '../../../../common/api';
+import type { attachmentApiV1 } from '../../../../common/types/api';
+import { BulkDeleteFileAttachmentsRequestRt } from '../../../../common/types/api/attachment/v1';
export const bulkDeleteFileAttachments = createCasesRoute({
method: 'post',
@@ -27,8 +28,9 @@ export const bulkDeleteFileAttachments = createCasesRoute({
try {
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
-
- const requestBody = decodeWithExcessOrThrow(BulkDeleteFileAttachmentsRequestRt)(request.body);
+ const requestBody: attachmentApiV1.BulkDeleteFileAttachmentsRequest = decodeWithExcessOrThrow(
+ BulkDeleteFileAttachmentsRequestRt
+ )(request.body);
await client.attachments.bulkDeleteFileAttachments({
caseId: request.params.case_id,
diff --git a/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts b/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts
index 1e59df016e8c6..a5cb22f536caa 100644
--- a/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts
+++ b/x-pack/plugins/cases/server/routes/api/internal/bulk_get_attachments.ts
@@ -6,8 +6,9 @@
*/
import { schema } from '@kbn/config-schema';
-import { BulkGetAttachmentsRequestRt } from '../../../../common/types/api';
+import { BulkGetAttachmentsRequestRt } from '../../../../common/types/api/attachment/v1';
import { decodeWithExcessOrThrow } from '../../../../common/api';
+import type { attachmentApiV1 } from '../../../../common/types/api';
import { INTERNAL_BULK_GET_ATTACHMENTS_URL } from '../../../../common/constants';
import { createCaseError } from '../../../common/error';
@@ -28,13 +29,17 @@ export const bulkGetAttachmentsRoute = createCasesRoute({
const caseContext = await context.cases;
const client = await caseContext.getCasesClient();
- const requestBody = decodeWithExcessOrThrow(BulkGetAttachmentsRequestRt)(request.body);
+ const requestBody: attachmentApiV1.BulkGetAttachmentsRequest = decodeWithExcessOrThrow(
+ BulkGetAttachmentsRequestRt
+ )(request.body);
+
+ const res: attachmentApiV1.BulkGetAttachmentsResponse = await client.attachments.bulkGet({
+ caseID: request.params.case_id,
+ attachmentIDs: requestBody.ids,
+ });
return response.ok({
- body: await client.attachments.bulkGet({
- caseID: request.params.case_id,
- attachmentIDs: requestBody.ids,
- }),
+ body: res,
});
} catch (error) {
throw createCaseError({
diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts
index 5e5bcdcbd1ef3..91d42c2544191 100644
--- a/x-pack/plugins/cloud_security_posture/common/constants.ts
+++ b/x-pack/plugins/cloud_security_posture/common/constants.ts
@@ -84,6 +84,8 @@ export const CLOUDBEAT_AZURE = 'cloudbeat/cis_azure';
export const CLOUDBEAT_VULN_MGMT_AWS = 'cloudbeat/vuln_mgmt_aws';
export const CLOUDBEAT_VULN_MGMT_GCP = 'cloudbeat/vuln_mgmt_gcp';
export const CLOUDBEAT_VULN_MGMT_AZURE = 'cloudbeat/vuln_mgmt_azure';
+export const CIS_AWS = 'cis_aws';
+export const CIS_GCP = 'cis_gcp';
export const KSPM_POLICY_TEMPLATE = 'kspm';
export const CSPM_POLICY_TEMPLATE = 'cspm';
export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt';
diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx
new file mode 100644
index 0000000000000..2709163c22a84
--- /dev/null
+++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx
@@ -0,0 +1,64 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { CIS_AWS, CIS_GCP } from '../../common/constants';
+import { Cluster } from '../../common/types';
+import { CISBenchmarkIcon } from './cis_benchmark_icon';
+import { CompactFormattedNumber } from './compact_formatted_number';
+
+export const AccountsEvaluatedWidget = ({
+ clusters,
+ benchmarkAbbreviateAbove = 999,
+}: {
+ clusters: Cluster[];
+ /** numbers higher than the value of this field will be abbreviated using compact notation and have a tooltip displaying the full value */
+ benchmarkAbbreviateAbove?: number;
+}) => {
+ const filterClustersById = (benchmarkId: string) => {
+ return clusters?.filter((obj) => obj?.meta.benchmark.id === benchmarkId) || [];
+ };
+
+ const cisAwsClusterAmount = filterClustersById(CIS_AWS).length;
+ const cisGcpClusterAmount = filterClustersById(CIS_GCP).length;
+
+ const cisAwsBenchmarkName = filterClustersById(CIS_AWS)[0]?.meta.benchmark.name || '';
+ const cisGcpBenchmarkName = filterClustersById(CIS_GCP)[0]?.meta.benchmark.name || '';
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx
index 4751335c3debd..6ca379ffb24a4 100644
--- a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx
@@ -23,6 +23,8 @@ const getBenchmarkIdIconType = (props: Props): string => {
return cisEksIcon;
case 'cis_aws':
return 'logoAWS';
+ case 'cis_gcp':
+ return 'logoGCP';
case 'cis_k8s':
default:
return 'logoKubernetes';
diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
index bd6e46f018063..9837c531021f2 100644
--- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
+++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/summary_section.tsx
@@ -31,6 +31,7 @@ import {
KSPM_POLICY_TEMPLATE,
RULE_FAILED,
} from '../../../../common/constants';
+import { AccountsEvaluatedWidget } from '../../../components/accounts_evaluated_widget';
export const dashboardColumnsGrow: Record = {
first: 3,
@@ -86,7 +87,12 @@ export const SummarySection = ({
'xpack.csp.dashboard.summarySection.counterCard.accountsEvaluatedDescription',
{ defaultMessage: 'Accounts Evaluated' }
),
- title: ,
+ title:
+ dashboardType === KSPM_POLICY_TEMPLATE ? (
+
+ ) : (
+
+ ),
},
{
id: DASHBOARD_COUNTER_CARDS.RESOURCES_EVALUATED,
@@ -116,7 +122,7 @@ export const SummarySection = ({
},
],
[
- complianceData.clusters.length,
+ complianceData.clusters,
complianceData.stats.resourcesEvaluated,
complianceData.stats.totalFailed,
dashboardType,