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

[Case] Detection rules for case #88726

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
1346445
Adding type field to client
jonathan-buttner Jan 13, 2021
9f82a3d
Removing context and adding association type
jonathan-buttner Jan 14, 2021
edf97b3
Handle alerts from multiple indices
jonathan-buttner Jan 14, 2021
e10e620
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 15, 2021
ada3eb9
Adding flow for adding a sub case
jonathan-buttner Jan 15, 2021
695df14
Making progress on creating alerts from rules
jonathan-buttner Jan 19, 2021
a01a70f
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 20, 2021
7c012c7
Refactored add comment to handle case and sub case
jonathan-buttner Jan 20, 2021
914a263
Starting sub case API and refactoring of case client
jonathan-buttner Jan 21, 2021
cc57dd9
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 25, 2021
26a02fc
Fleshing out find cases
jonathan-buttner Jan 25, 2021
2d1b4e9
Finished the find cases api
jonathan-buttner Jan 26, 2021
c5f47b3
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 26, 2021
c58ddb0
Filtering comments by association type
jonathan-buttner Jan 26, 2021
fcc40c0
Fixing tests and types
jonathan-buttner Jan 28, 2021
bc62907
Updating snapshots
jonathan-buttner Jan 28, 2021
2507213
Cleaning up comment references
jonathan-buttner Jan 28, 2021
d486483
Working unit tests
jonathan-buttner Jan 28, 2021
2ee61b0
Fixing integration tests and got ES to work
jonathan-buttner Jan 28, 2021
34fb160
Unit tests and api integration test working
jonathan-buttner Jan 29, 2021
435b741
Refactoring find and get_status
jonathan-buttner Jan 29, 2021
1b45ca5
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 29, 2021
a4f4114
Starting patch, and update
jonathan-buttner Jan 30, 2021
5f4bbb3
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Jan 30, 2021
060db5b
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 1, 2021
a96c765
script for sub cases
jonathan-buttner Feb 1, 2021
bc3e861
Removing converted_by and fixing type errors
jonathan-buttner Feb 1, 2021
f13d1d4
Adding docs for script
jonathan-buttner Feb 1, 2021
1db10e5
Removing converted_by and fixing integration test
jonathan-buttner Feb 2, 2021
dd1744e
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 2, 2021
f7b136b
Adding sub case id to comment routes
jonathan-buttner Feb 2, 2021
51504e0
Removing stringify comparison
jonathan-buttner Feb 2, 2021
1d81bdf
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 2, 2021
db09ea7
Adding delete api and tests
jonathan-buttner Feb 3, 2021
5279958
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 3, 2021
541daa8
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 4, 2021
9633296
Updating license
jonathan-buttner Feb 4, 2021
2333330
missed license files
jonathan-buttner Feb 4, 2021
80c2161
Integration tests passing
jonathan-buttner Feb 4, 2021
eed7f56
Adding more tests for sub cases
jonathan-buttner Feb 4, 2021
877a82c
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 5, 2021
acf20f4
Find int tests, scoped client, patch sub user actions
jonathan-buttner Feb 5, 2021
96e38f8
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 5, 2021
ee0fea6
fixing types and call cluster
jonathan-buttner Feb 6, 2021
24919af
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 6, 2021
a4458d1
fixing get sub case param issue
jonathan-buttner Feb 6, 2021
e2a020a
Adding user actions for sub cases
jonathan-buttner Feb 6, 2021
dfccb2e
Preventing alerts on collections and refactoring user
jonathan-buttner Feb 7, 2021
9903a51
Allowing type to be updated for ind cases
jonathan-buttner Feb 7, 2021
019a63e
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 7, 2021
1441a76
Refactoring and writing tests
jonathan-buttner Feb 8, 2021
6449229
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 8, 2021
728c2de
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 8, 2021
8cde2b5
Fixing sub case status filtering
jonathan-buttner Feb 8, 2021
c35a735
Adding more tests not allowing gen alerts patch
jonathan-buttner Feb 9, 2021
ff0297f
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 9, 2021
338cd16
Working unit tests
jonathan-buttner Feb 9, 2021
315a46d
Push to connector gets all sub case comments
jonathan-buttner Feb 9, 2021
886badd
Writing more tests and cleaning up
jonathan-buttner Feb 9, 2021
f69b0dd
Updating push functionality for generated alerts and sub cases
jonathan-buttner Feb 9, 2021
0cce723
Adding comment about updating collection sync
jonathan-buttner Feb 9, 2021
5c282a5
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 10, 2021
8a553c2
Merge branch 'master' into detection-rules-for-case
kibanamachine Feb 10, 2021
f78ba23
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 10, 2021
b6c296e
Refactoring update alert status for sub cases and removing request an…
jonathan-buttner Feb 11, 2021
57b8414
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 11, 2021
f58b5ff
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 11, 2021
575d849
Addressing alert service feedback
jonathan-buttner Feb 11, 2021
fe3d043
Fixing sub case sync bug and cleaning up comment types
jonathan-buttner Feb 11, 2021
0c98e1c
Merge branch 'master' of github.com:elastic/kibana into detection-rul…
jonathan-buttner Feb 11, 2021
99d37ae
Addressing more feedback
jonathan-buttner Feb 11, 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
54 changes: 42 additions & 12 deletions x-pack/plugins/case/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,21 @@ import * as rt from 'io-ts';
import { NumberFromString } from '../saved_object';
import { UserRT } from '../user';
import { CommentResponseRt } from './comment';
import { CasesStatusResponseRt } from './status';
import { CasesStatusResponseRt, CaseStatusRt } from './status';
import { CaseConnectorRt, ESCaseConnector } from '../connectors';
import { SubCaseResponseRt } from './sub_case';

export enum CaseStatuses {
open = 'open',
'in-progress' = 'in-progress',
closed = 'closed',
export enum CaseType {
collection = 'collection',
individual = 'individual',
}

const CaseStatusRt = rt.union([
rt.literal(CaseStatuses.open),
rt.literal(CaseStatuses['in-progress']),
rt.literal(CaseStatuses.closed),
]);
/**
* Exposing the field used to define the case type so that it can be used for filtering in saved object find queries.
*/
export const caseTypeField = 'type';

export const caseStatuses = Object.values(CaseStatuses);
const CaseTypeRt = rt.union([rt.literal(CaseType.collection), rt.literal(CaseType.individual)]);

const SettingsRt = rt.type({
syncAlerts: rt.boolean,
Expand All @@ -36,6 +35,7 @@ const CaseBasicRt = rt.type({
status: CaseStatusRt,
tags: rt.array(rt.string),
title: rt.string,
[caseTypeField]: CaseTypeRt,
connector: CaseConnectorRt,
settings: SettingsRt,
});
Expand Down Expand Up @@ -72,15 +72,35 @@ export const CaseAttributesRt = rt.intersection([
}),
]);

export const CasePostRequestRt = rt.type({
const CasePostRequestNoTypeRt = rt.type({
description: rt.string,
tags: rt.array(rt.string),
title: rt.string,
connector: CaseConnectorRt,
settings: SettingsRt,
});

/**
* This type is used for validating a create case request. It requires that the type field be defined.
*/
export const CaseClientPostRequestRt = rt.type({
...CasePostRequestNoTypeRt.props,
[caseTypeField]: CaseTypeRt,
});

/**
* This type is not used for validation when decoding a request because intersection does not have props defined which
* required for the excess function. Instead we use this as the type used by the UI. This allows the type field to be
* optional and the server will handle setting it to a default value before validating that the request
* has all the necessary fields. CaseClientPostRequestRt is used for validation.
*/
export const CasePostRequestRt = rt.intersection([
rt.partial({ type: CaseTypeRt }),
CasePostRequestNoTypeRt,
]);

export const CasesFindRequestRt = rt.partial({
type: CaseTypeRt,
tags: rt.union([rt.array(rt.string), rt.string]),
status: CaseStatusRt,
reporters: rt.union([rt.array(rt.string), rt.string]),
Expand All @@ -99,9 +119,11 @@ export const CaseResponseRt = rt.intersection([
rt.type({
id: rt.string,
totalComment: rt.number,
totalAlerts: rt.number,
version: rt.string,
}),
rt.partial({
subCases: rt.array(SubCaseResponseRt),
comments: rt.array(CommentResponseRt),
}),
]);
Expand Down Expand Up @@ -150,13 +172,21 @@ export const ExternalServiceResponseRt = rt.intersection([
]);

export type CaseAttributes = rt.TypeOf<typeof CaseAttributesRt>;
/**
* This field differs from the CasePostRequest in that the post request's type field can be optional. This type requires
* that the type field be defined. The CasePostRequest should be used in most places (the UI etc). This type is really
* only necessary for validation.
*/
export type CaseClientPostRequest = rt.TypeOf<typeof CaseClientPostRequestRt>;
export type CasePostRequest = rt.TypeOf<typeof CasePostRequestRt>;
export type CaseResponse = rt.TypeOf<typeof CaseResponseRt>;
export type CasesResponse = rt.TypeOf<typeof CasesResponseRt>;
export type CasesFindRequest = rt.TypeOf<typeof CasesFindRequestRt>;
export type CasesFindResponse = rt.TypeOf<typeof CasesFindResponseRt>;
export type CasePatchRequest = rt.TypeOf<typeof CasePatchRequestRt>;
export type CasesPatchRequest = rt.TypeOf<typeof CasesPatchRequestRt>;
export type CaseFullExternalService = rt.TypeOf<typeof CaseFullExternalServiceRt>;
export type CaseSettings = rt.TypeOf<typeof SettingsRt>;
export type ExternalServiceResponse = rt.TypeOf<typeof ExternalServiceResponseRt>;

export type ESCaseAttributes = Omit<CaseAttributes, 'connector'> & { connector: ESCaseConnector };
Expand Down
51 changes: 35 additions & 16 deletions x-pack/plugins/case/common/api/cases/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,22 @@ import * as rt from 'io-ts';

import { UserRT } from '../user';

/**
* this is used to differentiate between an alert attached to a top-level case and a group of alerts that should only
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the comments 👍🏾

* be attached to a sub case. The reason we need this is because an alert group comment will have references to both a case and
* sub case when it is created. For us to be able to filter out alert groups in a top-level case we need a field to
* use as a filter.
*/
export enum AssociationType {
case = 'case',
subCase = 'sub_case',
}

export const CommentAttributesBasicRt = rt.type({
associationType: rt.union([
rt.literal(AssociationType.case),
rt.literal(AssociationType.subCase),
]),
created_at: rt.string,
created_by: UserRT,
pushed_at: rt.union([rt.string, rt.null]),
Expand All @@ -18,24 +33,33 @@ export const CommentAttributesBasicRt = rt.type({
updated_by: rt.union([UserRT, rt.null]),
});

export enum CommentType {
user = 'user',
alert = 'alert',
generatedAlert = 'generated_alert',
}

export const ContextTypeUserRt = rt.type({
comment: rt.string,
type: rt.literal('user'),
type: rt.literal(CommentType.user),
});

export const ContextTypeAlertRt = rt.type({
type: rt.literal('alert'),
alertId: rt.string,
/**
* This defines the structure of how alerts (generated or user attached) are stored in saved objects documents. It also
* represents of an alert after it has been transformed. A generated alert will be transformed by the connector so that
* it matches this structure. User attached alerts do not need to be transformed.
*/
export const AlertCommentRequestRt = rt.type({
type: rt.union([rt.literal(CommentType.generatedAlert), rt.literal(CommentType.alert)]),
alertId: rt.union([rt.array(rt.string), rt.string]),
index: rt.string,
});

const AttributesTypeUserRt = rt.intersection([ContextTypeUserRt, CommentAttributesBasicRt]);
const AttributesTypeAlertsRt = rt.intersection([ContextTypeAlertRt, CommentAttributesBasicRt]);
const AttributesTypeAlertsRt = rt.intersection([AlertCommentRequestRt, CommentAttributesBasicRt]);
const CommentAttributesRt = rt.union([AttributesTypeUserRt, AttributesTypeAlertsRt]);

const ContextBasicRt = rt.union([ContextTypeUserRt, ContextTypeAlertRt]);

export const CommentRequestRt = ContextBasicRt;
export const CommentRequestRt = rt.union([ContextTypeUserRt, AlertCommentRequestRt]);

export const CommentResponseRt = rt.intersection([
CommentAttributesRt,
Expand All @@ -60,7 +84,7 @@ export const CommentPatchRequestRt = rt.intersection([
* Partial updates are not allowed.
* We want to prevent the user for changing the type without removing invalid fields.
*/
ContextBasicRt,
CommentRequestRt,
rt.type({ id: rt.string, version: rt.string }),
]);

Expand All @@ -71,7 +95,7 @@ export const CommentPatchRequestRt = rt.intersection([
* We ensure that partial updates of CommentContext is not going to happen inside the patch comment route.
*/
export const CommentPatchAttributesRt = rt.intersection([
rt.union([rt.partial(CommentAttributesBasicRt.props), rt.partial(ContextTypeAlertRt.props)]),
rt.union([rt.partial(CommentAttributesBasicRt.props), rt.partial(AlertCommentRequestRt.props)]),
rt.partial(CommentAttributesBasicRt.props),
]);

Expand All @@ -82,11 +106,6 @@ export const CommentsResponseRt = rt.type({
total: rt.number,
});

export enum CommentType {
user = 'user',
alert = 'alert',
}

export const AllCommentsResponseRt = rt.array(CommentResponseRt);

export type CommentAttributes = rt.TypeOf<typeof CommentAttributesRt>;
Expand All @@ -98,4 +117,4 @@ export type CommentsResponse = rt.TypeOf<typeof CommentsResponseRt>;
export type CommentPatchRequest = rt.TypeOf<typeof CommentPatchRequestRt>;
export type CommentPatchAttributes = rt.TypeOf<typeof CommentPatchAttributesRt>;
export type CommentRequestUserType = rt.TypeOf<typeof ContextTypeUserRt>;
export type CommentRequestAlertType = rt.TypeOf<typeof ContextTypeAlertRt>;
export type CommentRequestAlertType = rt.TypeOf<typeof AlertCommentRequestRt>;
35 changes: 35 additions & 0 deletions x-pack/plugins/case/common/api/cases/commentable_case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 * as rt from 'io-ts';
import { CaseAttributesRt } from './case';
import { CommentResponseRt } from './comment';
import { SubCaseAttributesRt, SubCaseResponseRt } from './sub_case';

export const CollectionSubCaseAttributesRt = rt.intersection([
rt.partial({ subCase: SubCaseAttributesRt }),
rt.type({
case: CaseAttributesRt,
}),
]);

export const CollectWithSubCaseResponseRt = rt.intersection([
CaseAttributesRt,
rt.type({
id: rt.string,
totalComment: rt.number,
version: rt.string,
}),
rt.partial({
subCase: SubCaseResponseRt,
totalAlerts: rt.number,
comments: rt.array(CommentResponseRt),
}),
]);

export type CollectionWithSubCaseResponse = rt.TypeOf<typeof CollectWithSubCaseResponseRt>;
export type CollectionWithSubCaseAttributes = rt.TypeOf<typeof CollectionSubCaseAttributesRt>;
2 changes: 2 additions & 0 deletions x-pack/plugins/case/common/api/cases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export * from './configure';
export * from './comment';
export * from './status';
export * from './user_actions';
export * from './sub_case';
export * from './commentable_case';
14 changes: 14 additions & 0 deletions x-pack/plugins/case/common/api/cases/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

import * as rt from 'io-ts';

export enum CaseStatuses {
open = 'open',
'in-progress' = 'in-progress',
closed = 'closed',
}

export const CaseStatusRt = rt.union([
rt.literal(CaseStatuses.open),
rt.literal(CaseStatuses['in-progress']),
rt.literal(CaseStatuses.closed),
]);

export const caseStatuses = Object.values(CaseStatuses);

export const CasesStatusResponseRt = rt.type({
count_open_cases: rt.number,
count_in_progress_cases: rt.number,
Expand Down
80 changes: 80 additions & 0 deletions x-pack/plugins/case/common/api/cases/sub_case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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 * as rt from 'io-ts';

import { NumberFromString } from '../saved_object';
import { UserRT } from '../user';
import { CommentResponseRt } from './comment';
import { CasesStatusResponseRt } from './status';
import { CaseStatusRt } from './status';

const SubCaseBasicRt = rt.type({
status: CaseStatusRt,
});

export const SubCaseAttributesRt = rt.intersection([
SubCaseBasicRt,
rt.type({
closed_at: rt.union([rt.string, rt.null]),
closed_by: rt.union([UserRT, rt.null]),
created_at: rt.string,
created_by: rt.union([UserRT, rt.null]),
updated_at: rt.union([rt.string, rt.null]),
updated_by: rt.union([UserRT, rt.null]),
}),
]);

export const SubCasesFindRequestRt = rt.partial({
status: CaseStatusRt,
defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]),
fields: rt.array(rt.string),
page: NumberFromString,
perPage: NumberFromString,
search: rt.string,
searchFields: rt.array(rt.string),
sortField: rt.string,
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
});

export const SubCaseResponseRt = rt.intersection([
SubCaseAttributesRt,
rt.type({
id: rt.string,
totalComment: rt.number,
totalAlerts: rt.number,
version: rt.string,
}),
rt.partial({
comments: rt.array(CommentResponseRt),
}),
]);

export const SubCasesFindResponseRt = rt.intersection([
rt.type({
subCases: rt.array(SubCaseResponseRt),
page: rt.number,
per_page: rt.number,
total: rt.number,
}),
CasesStatusResponseRt,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This status is the parent case status?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misread it! Ignore :)

]);

export const SubCasePatchRequestRt = rt.intersection([
rt.partial(SubCaseBasicRt.props),
rt.type({ id: rt.string, version: rt.string }),
]);

export const SubCasesPatchRequestRt = rt.type({ subCases: rt.array(SubCasePatchRequestRt) });
export const SubCasesResponseRt = rt.array(SubCaseResponseRt);

export type SubCaseAttributes = rt.TypeOf<typeof SubCaseAttributesRt>;
export type SubCaseResponse = rt.TypeOf<typeof SubCaseResponseRt>;
export type SubCasesResponse = rt.TypeOf<typeof SubCasesResponseRt>;
export type SubCasesFindResponse = rt.TypeOf<typeof SubCasesFindResponseRt>;
export type SubCasePatchRequest = rt.TypeOf<typeof SubCasePatchRequestRt>;
export type SubCasesPatchRequest = rt.TypeOf<typeof SubCasesPatchRequestRt>;
25 changes: 13 additions & 12 deletions x-pack/plugins/case/common/api/cases/user_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ import { UserRT } from '../user';
/* To the next developer, if you add/removed fields here
* make sure to check this file (x-pack/plugins/case/server/services/user_actions/helpers.ts) too
*/
const UserActionFieldRt = rt.array(
rt.union([
rt.literal('comment'),
rt.literal('connector'),
rt.literal('description'),
rt.literal('pushed'),
rt.literal('tags'),
rt.literal('title'),
rt.literal('status'),
rt.literal('settings'),
])
);
const UserActionFieldTypeRt = rt.union([
rt.literal('comment'),
rt.literal('connector'),
rt.literal('description'),
rt.literal('pushed'),
rt.literal('tags'),
rt.literal('title'),
rt.literal('status'),
rt.literal('settings'),
rt.literal('sub_case'),
]);
const UserActionFieldRt = rt.array(UserActionFieldTypeRt);
const UserActionRt = rt.union([
rt.literal('add'),
rt.literal('create'),
Expand Down Expand Up @@ -60,3 +60,4 @@ export type CaseUserActionsResponse = rt.TypeOf<typeof CaseUserActionsResponseRt

export type UserAction = rt.TypeOf<typeof UserActionRt>;
export type UserActionField = rt.TypeOf<typeof UserActionFieldRt>;
export type UserActionFieldType = rt.TypeOf<typeof UserActionFieldTypeRt>;
Loading