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

[Security Solution][Case] Fix subcases bugs on detections and case view #91836

Merged
merged 39 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
538f2d9
Fix bug with case connector UI
cnasikas Feb 18, 2021
3e6fadd
Disable closed cases on detections
cnasikas Feb 22, 2021
8dd955e
Fix bug with alert query
cnasikas Feb 22, 2021
348772a
Fix order of attaching alert to a case
cnasikas Feb 22, 2021
3936948
Fix icon
cnasikas Feb 22, 2021
e90abd9
Select only subcases on modal
cnasikas Feb 22, 2021
41fd862
Better error messages
cnasikas Feb 23, 2021
9738128
Remove CollectionWithSubCaseResponse
cnasikas Feb 23, 2021
2f841fb
Fix post/patch comment in single case
cnasikas Feb 23, 2021
a5a865f
Fixes
cnasikas Feb 23, 2021
ded103e
Working integration tests
jonathan-buttner Feb 23, 2021
6fdebd5
Merge pull request #1 from jonathan-buttner/subcase-fix-int-tests
cnasikas Feb 24, 2021
00d75a1
Fix encoding
cnasikas Feb 24, 2021
69e55c5
Fix bug with case connector UI
cnasikas Feb 18, 2021
0fa13ff
Disable closed cases on detections
cnasikas Feb 22, 2021
7f50906
Fix bug with alert query
cnasikas Feb 22, 2021
1538e3c
Fix order of attaching alert to a case
cnasikas Feb 22, 2021
1b5b9b6
Fix icon
cnasikas Feb 22, 2021
4f24c74
Select only subcases on modal
cnasikas Feb 22, 2021
ff6b6d3
Better error messages
cnasikas Feb 23, 2021
9c09b76
Remove CollectionWithSubCaseResponse
cnasikas Feb 23, 2021
a330b03
Fix post/patch comment in single case
cnasikas Feb 23, 2021
ebdb719
Fixes
cnasikas Feb 23, 2021
add23de
Working integration tests
jonathan-buttner Feb 23, 2021
f93f33e
Fix encoding
cnasikas Feb 24, 2021
8146664
Fixing sub case comment integration tests
jonathan-buttner Feb 24, 2021
b17470b
Merge branch 'fix_subcases_bugs' of github.com:cnasikas/kibana into f…
jonathan-buttner Feb 24, 2021
ceea3f4
Merge branch 'master' into fix_subcases_bugs
cnasikas Feb 24, 2021
5449edf
Fixing patch sub cases integration tests
jonathan-buttner Feb 25, 2021
7d8a09f
Hide status from collections
cnasikas Feb 25, 2021
735ae92
Merge branch 'fix_subcases_bugs' of github.com:cnasikas/kibana into f…
cnasikas Feb 25, 2021
3622df1
Fix types
cnasikas Feb 25, 2021
f0c4886
Merge branch 'master' into fix_subcases_bugs
cnasikas Feb 25, 2021
6131afa
Fixes
cnasikas Feb 25, 2021
cea5dbb
Fix comment id
cnasikas Feb 26, 2021
8f8a7b5
Merge branch 'master' into fix_subcases_bugs
cnasikas Feb 26, 2021
361336f
Fix tests
cnasikas Feb 26, 2021
50d851d
Show toaster when attaching to an existing case
cnasikas Feb 26, 2021
936df23
Fix tests
cnasikas Feb 26, 2021
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
35 changes: 0 additions & 35 deletions x-pack/plugins/case/common/api/cases/commentable_case.ts

This file was deleted.

1 change: 0 additions & 1 deletion x-pack/plugins/case/common/api/cases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ export * from './comment';
export * from './status';
export * from './user_actions';
export * from './sub_case';
export * from './commentable_case';
8 changes: 4 additions & 4 deletions x-pack/plugins/case/common/api/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const getSubCasesUrl = (caseID: string): string => {
return SUB_CASES_URL.replace('{case_id}', caseID);
};

export const getSubCaseDetailsUrl = (caseID: string, subCaseID: string): string => {
return SUB_CASE_DETAILS_URL.replace('{case_id}', caseID).replace('{sub_case_id}', subCaseID);
export const getSubCaseDetailsUrl = (caseID: string, subCaseId: string): string => {
return SUB_CASE_DETAILS_URL.replace('{case_id}', caseID).replace('{sub_case_id}', subCaseId);
};

export const getCaseCommentsUrl = (id: string): string => {
Expand All @@ -40,8 +40,8 @@ export const getCaseUserActionUrl = (id: string): string => {
return CASE_USER_ACTIONS_URL.replace('{case_id}', id);
};

export const getSubCaseUserActionUrl = (caseID: string, subCaseID: string): string => {
return SUB_CASE_USER_ACTIONS_URL.replace('{case_id}', caseID).replace('{sub_case_id}', subCaseID);
export const getSubCaseUserActionUrl = (caseID: string, subCaseId: string): string => {
return SUB_CASE_USER_ACTIONS_URL.replace('{case_id}', caseID).replace('{sub_case_id}', subCaseId);
};

export const getCasePushUrl = (caseId: string, connectorId: string): string => {
Expand Down
27 changes: 25 additions & 2 deletions x-pack/plugins/case/common/api/runtime_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,37 @@ import { either, fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
import * as rt from 'io-ts';
import { failure } from 'io-ts/lib/PathReporter';
import { isObject } from 'lodash/fp';

type ErrorFactory = (message: string) => Error;

export const formatErrors = (errors: rt.Errors): string[] => {
const err = errors.map((error) => {
if (error.message != null) {
return error.message;
} else {
const keyContext = error.context
.filter(
(entry) => entry.key != null && !Number.isInteger(+entry.key) && entry.key.trim() !== ''
)
.map((entry) => entry.key)
.join(',');

const nameContext = error.context.find((entry) => entry.type?.name?.length > 0);
const suppliedValue =
keyContext !== '' ? keyContext : nameContext != null ? nameContext.type.name : '';
const value = isObject(error.value) ? JSON.stringify(error.value) : error.value;
return `Invalid value "${value}" supplied to "${suppliedValue}"`;
}
});

return [...new Set(err)];
};

export const createPlainError = (message: string) => new Error(message);

export const throwErrors = (createError: ErrorFactory) => (errors: rt.Errors) => {
throw createError(failure(errors).join('\n'));
throw createError(formatErrors(errors).join());
};

export const decodeOrThrow = <A, O, I>(
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/case/server/client/cases/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export interface TransformFieldsArgs<P, S> {

export interface ExternalServiceComment {
comment: string;
commentId?: string;
commentId: string;
}

export interface MapIncident {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/case/server/client/cases/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ describe('utils', () => {
},
{
comment: 'Elastic Security Alerts attached to the case: 3',
commentId: 'mock-id-1-total-alerts',
},
]);
});
Expand Down Expand Up @@ -569,6 +570,7 @@ describe('utils', () => {
},
{
comment: 'Elastic Security Alerts attached to the case: 4',
commentId: 'mock-id-1-total-alerts',
},
]);
});
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/case/server/client/cases/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const createIncident = async ({
if (totalAlerts > 0) {
comments.push({
comment: `Elastic Security Alerts attached to the case: ${totalAlerts}`,
commentId: `${theCase.id}-total-alerts`,
});
}

Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/case/server/client/comments/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
CaseType,
SubCaseAttributes,
CommentRequest,
CollectionWithSubCaseResponse,
CaseResponse,
User,
CommentRequestAlertType,
AlertCommentRequestRt,
Expand Down Expand Up @@ -113,7 +113,7 @@ const addGeneratedAlerts = async ({
caseClient,
caseId,
comment,
}: AddCommentFromRuleArgs): Promise<CollectionWithSubCaseResponse> => {
}: AddCommentFromRuleArgs): Promise<CaseResponse> => {
const query = pipe(
AlertCommentRequestRt.decode(comment),
fold(throwErrors(Boom.badRequest), identity)
Expand Down Expand Up @@ -260,7 +260,7 @@ export const addComment = async ({
caseId,
comment,
user,
}: AddCommentArgs): Promise<CollectionWithSubCaseResponse> => {
}: AddCommentArgs): Promise<CaseResponse> => {
const query = pipe(
CommentRequestRt.decode(comment),
fold(throwErrors(Boom.badRequest), identity)
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/case/server/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
CasesPatchRequest,
CasesResponse,
CaseStatuses,
CollectionWithSubCaseResponse,
CommentRequest,
ConnectorMappingsAttributes,
GetFieldsResponse,
Expand Down Expand Up @@ -89,7 +88,7 @@ export interface ConfigureFields {
* This represents the interface that other plugins can access.
*/
export interface CaseClient {
addComment(args: CaseClientAddComment): Promise<CollectionWithSubCaseResponse>;
addComment(args: CaseClientAddComment): Promise<CaseResponse>;
create(theCase: CasePostRequest): Promise<CaseResponse>;
get(args: CaseClientGet): Promise<CaseResponse>;
getAlerts(args: CaseClientGetAlerts): Promise<CaseClientGetAlertsResponse>;
Expand Down
63 changes: 41 additions & 22 deletions x-pack/plugins/case/server/common/models/commentable_case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
CaseSettings,
CaseStatuses,
CaseType,
CollectionWithSubCaseResponse,
CollectWithSubCaseResponseRt,
CaseResponse,
CaseResponseRt,
CommentAttributes,
CommentPatchRequest,
CommentRequest,
Expand Down Expand Up @@ -254,7 +254,7 @@ export class CommentableCase {
};
}

public async encode(): Promise<CollectionWithSubCaseResponse> {
public async encode(): Promise<CaseResponse> {
const collectionCommentStats = await this.service.getAllCaseComments({
client: this.soClient,
id: this.collection.id,
Expand All @@ -265,22 +265,6 @@ export class CommentableCase {
},
});

if (this.subCase) {
const subCaseComments = await this.service.getAllSubCaseComments({
client: this.soClient,
id: this.subCase.id,
});

return CollectWithSubCaseResponseRt.encode({
subCase: flattenSubCaseSavedObject({
savedObject: this.subCase,
comments: subCaseComments.saved_objects,
totalAlerts: countAlertsForID({ comments: subCaseComments, id: this.subCase.id }),
}),
...this.formatCollectionForEncoding(collectionCommentStats.total),
});
}

const collectionComments = await this.service.getAllCaseComments({
client: this.soClient,
id: this.collection.id,
Expand All @@ -291,10 +275,45 @@ export class CommentableCase {
},
});

return CollectWithSubCaseResponseRt.encode({
const collectionTotalAlerts =
countAlertsForID({ comments: collectionComments, id: this.collection.id }) ?? 0;

const caseResponse = {
jonathan-buttner marked this conversation as resolved.
Show resolved Hide resolved
comments: flattenCommentSavedObjects(collectionComments.saved_objects),
totalAlerts: countAlertsForID({ comments: collectionComments, id: this.collection.id }),
totalAlerts: collectionTotalAlerts,
...this.formatCollectionForEncoding(collectionCommentStats.total),
});
};

if (this.subCase) {
const subCaseComments = await this.service.getAllSubCaseComments({
client: this.soClient,
id: this.subCase.id,
});
const totalAlerts = countAlertsForID({ comments: subCaseComments, id: this.subCase.id }) ?? 0;

return CaseResponseRt.encode({
...caseResponse,
/**
* For now we need the sub case comments and totals to be exposed on the top level of the response so that the UI
* functionality can stay the same. Ideally in the future we can refactor this so that the UI will look for the
* comments either in the top level for a case or a collection or in the subCases field if it is a sub case.
*
* If we ever need to return both the collection's comments and the sub case comments we'll need to refactor it then
* as well.
*/
comments: flattenCommentSavedObjects(subCaseComments.saved_objects),
jonathan-buttner marked this conversation as resolved.
Show resolved Hide resolved
totalComment: subCaseComments.saved_objects.length,
totalAlerts,
subCases: [
flattenSubCaseSavedObject({
savedObject: this.subCase,
totalComment: subCaseComments.saved_objects.length,
totalAlerts,
}),
],
});
}

return CaseResponseRt.encode(caseResponse);
}
}
4 changes: 2 additions & 2 deletions x-pack/plugins/case/server/connectors/case/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
AssociationType,
CaseResponse,
CasesResponse,
CollectionWithSubCaseResponse,
} from '../../../common/api';
import {
connectorMappingsServiceMock,
Expand Down Expand Up @@ -1018,9 +1017,10 @@ describe('case connector', () => {

describe('addComment', () => {
it('executes correctly', async () => {
const commentReturn: CollectionWithSubCaseResponse = {
const commentReturn: CaseResponse = {
id: 'mock-it',
totalComment: 0,
totalAlerts: 0,
version: 'WzksMV0=',

closed_at: null,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/case/server/connectors/case/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
ConnectorSchema,
CommentSchema,
} from './schema';
import { CaseResponse, CasesResponse, CollectionWithSubCaseResponse } from '../../../common/api';
import { CaseResponse, CasesResponse } from '../../../common/api';

export type CaseConfiguration = TypeOf<typeof CaseConfigurationSchema>;
export type Connector = TypeOf<typeof ConnectorSchema>;
Expand All @@ -29,7 +29,7 @@ export type ExecutorSubActionAddCommentParams = TypeOf<
>;

export type CaseExecutorParams = TypeOf<typeof CaseExecutorParamsSchema>;
export type CaseExecutorResponse = CaseResponse | CasesResponse | CollectionWithSubCaseResponse;
export type CaseExecutorResponse = CaseResponse | CasesResponse;

export type CaseActionType = ActionType<
CaseConfiguration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function initDeleteAllCommentsApi({ caseService, router, userActionServic
}),
query: schema.maybe(
schema.object({
subCaseID: schema.maybe(schema.string()),
subCaseId: schema.maybe(schema.string()),
})
),
},
Expand All @@ -35,11 +35,11 @@ export function initDeleteAllCommentsApi({ caseService, router, userActionServic
const { username, full_name, email } = await caseService.getUser({ request });
const deleteDate = new Date().toISOString();

const id = request.query?.subCaseID ?? request.params.case_id;
const id = request.query?.subCaseId ?? request.params.case_id;
const comments = await caseService.getCommentsByAssociation({
client,
id,
associationType: request.query?.subCaseID
associationType: request.query?.subCaseId
? AssociationType.subCase
: AssociationType.case,
});
Expand All @@ -61,7 +61,7 @@ export function initDeleteAllCommentsApi({ caseService, router, userActionServic
actionAt: deleteDate,
actionBy: { username, full_name, email },
caseId: request.params.case_id,
subCaseId: request.query?.subCaseID,
subCaseId: request.query?.subCaseId,
commentId: comment.id,
fields: ['comment'],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function initDeleteCommentApi({ caseService, router, userActionService }:
}),
query: schema.maybe(
schema.object({
subCaseID: schema.maybe(schema.string()),
subCaseId: schema.maybe(schema.string()),
})
),
},
Expand All @@ -46,8 +46,8 @@ export function initDeleteCommentApi({ caseService, router, userActionService }:
throw Boom.notFound(`This comment ${request.params.comment_id} does not exist anymore.`);
}

const type = request.query?.subCaseID ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT;
const id = request.query?.subCaseID ?? request.params.case_id;
const type = request.query?.subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT;
const id = request.query?.subCaseId ?? request.params.case_id;

const caseRef = myComment.references.find((c) => c.type === type);
if (caseRef == null || (caseRef != null && caseRef.id !== id)) {
Expand All @@ -69,7 +69,7 @@ export function initDeleteCommentApi({ caseService, router, userActionService }:
actionAt: deleteDate,
actionBy: { username, full_name, email },
caseId: id,
subCaseId: request.query?.subCaseID,
subCaseId: request.query?.subCaseId,
commentId: request.params.comment_id,
fields: ['comment'],
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { defaultPage, defaultPerPage } from '../..';

const FindQueryParamsRt = rt.partial({
...SavedObjectFindOptionsRt.props,
subCaseID: rt.string,
subCaseId: rt.string,
});

export function initFindCaseCommentsApi({ caseService, router }: RouteDeps) {
Expand All @@ -49,8 +49,8 @@ export function initFindCaseCommentsApi({ caseService, router }: RouteDeps) {
fold(throwErrors(Boom.badRequest), identity)
);

const id = query.subCaseID ?? request.params.case_id;
const associationType = query.subCaseID ? AssociationType.subCase : AssociationType.case;
const id = query.subCaseId ?? request.params.case_id;
const associationType = query.subCaseId ? AssociationType.subCase : AssociationType.case;
const args = query
? {
caseService,
Expand Down
Loading