Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Cases] Guardrails: Total number cases update #161076

Merged
merged 5 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion x-pack/plugins/cases/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
MAX_ASSIGNEES_FILTER_LENGTH,
MAX_REPORTERS_FILTER_LENGTH,
MAX_TAGS_FILTER_LENGTH,
MAX_CASES_TO_UPDATE,
MAX_BULK_GET_CASES,
MAX_CASES_PER_PAGE,
} from '../../constants';
Expand Down Expand Up @@ -461,7 +462,15 @@ export const CasePatchRequestRt = rt.intersection([
rt.strict({ id: rt.string, version: rt.string }),
]);

export const CasesPatchRequestRt = rt.strict({ cases: rt.array(CasePatchRequestRt) });
export const CasesPatchRequestRt = rt.strict({
cases: limitedArraySchema({
codec: CasePatchRequestRt,
min: 1,
max: MAX_CASES_TO_UPDATE,
fieldName: 'cases',
}),
});

export const CasesRt = rt.array(CaseRt);

export const CasePushRequestParamsRt = rt.strict({
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/cases/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export const MAX_COMMENT_LENGTH = 30000 as const;
export const MAX_LENGTH_PER_TAG = 256 as const;
export const MAX_TAGS_PER_CASE = 200 as const;
export const MAX_DELETE_IDS_LENGTH = 100 as const;
export const MAX_CASES_TO_UPDATE = 100 as const;

/**
* Cases features
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/docs/openapi/bundled.json
Original file line number Diff line number Diff line change
Expand Up @@ -5198,6 +5198,8 @@
"cases": {
"type": "array",
"description": "An array containing one or more case objects.",
"maxItems": 100,
"minItems": 1,
"items": {
"type": "object",
"required": [
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/docs/openapi/bundled.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3337,6 +3337,8 @@ components:
cases:
type: array
description: An array containing one or more case objects.
maxItems: 100
minItems: 1
items:
type: object
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ properties:
cases:
type: array
description: An array containing one or more case objects.
maxItems: 100
minItems: 1
items:
type: object
required:
Expand Down Expand Up @@ -35,7 +37,7 @@ properties:
settings:
$ref: 'settings.yaml'
severity:
$ref: 'severity_property.yaml'
$ref: 'severity_property.yaml'
status:
$ref: 'status.yaml'
tags:
Expand All @@ -55,4 +57,4 @@ properties:
maxLength: 160
version:
description: The current version of the case. To determine this value, use the get case or find cases APIs.
type: string
type: string
37 changes: 37 additions & 0 deletions x-pack/plugins/cases/server/client/cases/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
MAX_TAGS_PER_CASE,
MAX_LENGTH_PER_TAG,
MAX_TITLE_LENGTH,
MAX_CASES_TO_UPDATE,
} from '../../../common/constants';
import { mockCases } from '../../mocks';
import { createCasesClientMockArgs } from '../mocks';
Expand Down Expand Up @@ -701,4 +702,40 @@ describe('update', () => {
);
});
});

describe('Validation', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it(`throws an error when trying to update more than ${MAX_CASES_TO_UPDATE} cases`, async () => {
await expect(
update(
{
cases: Array(MAX_CASES_TO_UPDATE + 1).fill({
id: mockCases[0].id,
version: mockCases[0].version ?? '',
title: 'This is a test case!!',
}),
},
createCasesClientMockArgs()
)
).rejects.toThrow(
'Error: The length of the field cases is too long. Array must be of length <= 100.'
);
});

it('throws an error when trying to update zero cases', async () => {
await expect(
update(
{
cases: [],
},
createCasesClientMockArgs()
)
).rejects.toThrow(
'Error: The length of the field cases is too short. Array must be of length >= 1.'
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -561,109 +561,133 @@ export default ({ getService }: FtrProviderContext): void => {
});
});

it('400s if the title is too long', async () => {
const longTitle = 'a'.repeat(161);

const postedCase = await createCase(supertest, postCaseReq);
it('400s when trying to update too many cases', async () => {
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: longTitle,
},
],
cases: Array(101).fill({ id: 'foo', version: 'bar', title: 'coolTitle' }),
},
expectedHttpCode: 400,
});
});

it('400s if the title an empty string', async () => {
const postedCase = await createCase(supertest, postCaseReq);
it('400s when trying to update zero cases', async () => {
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: '',
},
],
cases: [],
},
expectedHttpCode: 400,
});
});

it('400s if the title is a string with empty characters', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: ' ',
},
],
},
expectedHttpCode: 400,
describe('title', async () => {
it('400s if the title is too long', async () => {
const longTitle = 'a'.repeat(161);

const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: longTitle,
},
],
},
expectedHttpCode: 400,
});
});
});

it('400s if the description is too long', async () => {
const longDescription = 'a'.repeat(30001);
it('400s if the title an empty string', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: '',
},
],
},
expectedHttpCode: 400,
});
});

const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: longDescription,
},
],
},
expectedHttpCode: 400,
it('400s if the title is a string with empty characters', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
title: ' ',
},
],
},
expectedHttpCode: 400,
});
});
});

it('400s if the description an empty string', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: '',
},
],
},
expectedHttpCode: 400,
describe('description', async () => {
it('400s if the description is too long', async () => {
const longDescription = 'a'.repeat(30001);

const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: longDescription,
},
],
},
expectedHttpCode: 400,
});
});
});

it('400s if the description is a string with empty characters', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: ' ',
},
],
},
expectedHttpCode: 400,
it('400s if the description an empty string', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: '',
},
],
},
expectedHttpCode: 400,
});
});

it('400s if the description is a string with empty characters', async () => {
const postedCase = await createCase(supertest, postCaseReq);
await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
description: ' ',
},
],
},
expectedHttpCode: 400,
});
});
});

Expand Down