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] [Cases] Introduce case observables (phase 0 & 1) #190237

Open
wants to merge 106 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
70c53c4
[Security Solutions][Cases] Add case observables
lgestc Aug 9, 2024
53525bf
add similar cases test for cases service
lgestc Oct 14, 2024
d47a4d9
Update x-pack/plugins/cases/common/types/api/observable/v1.ts
lgestc Oct 24, 2024
ad60ed7
Update x-pack/plugins/cases/server/routes/api/cases/similar.ts
lgestc Oct 24, 2024
ddcb50b
Update x-pack/plugins/cases/common/constants/index.ts
lgestc Oct 24, 2024
43f03b3
code review changes
lgestc Oct 24, 2024
770d04b
Merge branch 'main' into cases_observables
lgestc Oct 28, 2024
4e0cf95
specific ids for observable types
lgestc Oct 28, 2024
d27825b
move internal route constant
lgestc Oct 28, 2024
6dcb7a2
move external to iternal routes
lgestc Oct 28, 2024
0a325a8
update mappings
lgestc Oct 28, 2024
a5fdd5c
[CI] Auto-commit changed files from 'node scripts/jest_integration -u…
kibanamachine Oct 28, 2024
b6143cc
update similar cases url
lgestc Oct 29, 2024
ea191b7
Merge branch 'main' into cases_observables
lgestc Oct 30, 2024
719337f
apply pr review suggestion about skipping observables from the reques…
lgestc Oct 30, 2024
11e9e6e
apply response type suggestion
lgestc Oct 30, 2024
b515879
remove unused files
lgestc Oct 31, 2024
e939ada
Update x-pack/plugins/cases/common/constants/index.ts
lgestc Oct 31, 2024
07c6861
update constant name
lgestc Oct 31, 2024
682c32a
remove uri
lgestc Oct 31, 2024
dcc8593
increase max observables per case
lgestc Oct 31, 2024
5e41027
update route access options
lgestc Oct 31, 2024
8a90fbe
remove fixme
lgestc Oct 31, 2024
f69aee2
remove tags
lgestc Oct 31, 2024
770aa43
rename type
lgestc Oct 31, 2024
8f899d7
remove unused file
lgestc Oct 31, 2024
1329aa7
Revert "remove unused files"
lgestc Nov 4, 2024
c65cf67
restore broken type
lgestc Nov 4, 2024
17f2948
fix types in similar tesT
lgestc Nov 4, 2024
a3053ee
update translations
lgestc Nov 4, 2024
e17c6d5
remove find similar cases from cases service
lgestc Nov 4, 2024
533dd56
add mocks
lgestc Nov 4, 2024
22f71fb
split observable apis
lgestc Nov 4, 2024
7fa8cc1
use react query
lgestc Nov 4, 2024
f49725f
update response parsing
lgestc Nov 5, 2024
8a89936
fix type errors
lgestc Nov 5, 2024
5d6e774
add observables
lgestc Nov 5, 2024
e4c1a62
fix unit tests
lgestc Nov 6, 2024
8518919
fix server side tests
lgestc Nov 6, 2024
38461ee
fix common tests
lgestc Nov 6, 2024
077c0de
Merge branch 'main' into cases_observables
lgestc Nov 6, 2024
83f2f99
add standalone table for similar cases
lgestc Nov 6, 2024
416b67a
update test subj
lgestc Nov 7, 2024
43754cc
add empty observables test case to tranform
lgestc Nov 7, 2024
46b6b82
add test case for non empty observable
lgestc Nov 7, 2024
d9c9a88
update test
lgestc Nov 7, 2024
b5b12de
simplify types, add tests
lgestc Nov 7, 2024
9b31409
one more bugfix
lgestc Nov 7, 2024
83f95df
update unit tests
lgestc Nov 7, 2024
e86d0f1
similar cases - case id as url parameter
lgestc Nov 7, 2024
e919cba
update similar cases test
lgestc Nov 7, 2024
d6f102a
remove error
lgestc Nov 7, 2024
7eb5313
update ftr test
lgestc Nov 7, 2024
fa7fa81
a bit more fixes
lgestc Nov 7, 2024
8419248
fix failing test
lgestc Nov 7, 2024
4259c46
Merge branch 'main' into cases_observables
lgestc Nov 10, 2024
f56c02c
escape quotes in the value
lgestc Nov 10, 2024
9d4b496
prevent duplicates in observable types
lgestc Nov 10, 2024
bfb34ee
update enum
lgestc Nov 12, 2024
1185706
prevent duplicates for builtin types
lgestc Nov 12, 2024
cea43d0
add delete observable integration test
lgestc Nov 12, 2024
b2739ec
add license check on observable creation
lgestc Nov 12, 2024
e4dadc1
restore max observables per case
lgestc Nov 12, 2024
09b0e31
register platinum feature for observables
lgestc Nov 12, 2024
7851dbb
add experimental badge to obserable types section
lgestc Nov 12, 2024
ea505b4
add similar cases count next to the badge
lgestc Nov 12, 2024
27f5be1
update test subj in similar cases table
lgestc Nov 12, 2024
8df844b
prevent duplicate observables
lgestc Nov 12, 2024
bcdc7a5
hide and exclude removed types
lgestc Nov 12, 2024
2ad0ea1
show similarity reason
lgestc Nov 12, 2024
1a6bbe0
add space inbetween similar observable values
lgestc Nov 12, 2024
2d20d59
fix types
lgestc Nov 12, 2024
9826195
remove ios and hasbeensighted
lgestc Nov 12, 2024
5b63c04
prevent duplicate observables when updating
lgestc Nov 12, 2024
37cf8d2
move types
lgestc Nov 13, 2024
8184b4f
update test
lgestc Nov 13, 2024
079c30e
fix api tests
lgestc Nov 13, 2024
cf1c940
fix view activity test
lgestc Nov 13, 2024
6b775b6
Merge branch 'main' into cases_observables
lgestc Nov 13, 2024
b289291
fix features tests
lgestc Nov 13, 2024
3c32cff
update case view tabs test
lgestc Nov 13, 2024
1c986ab
update similar client test
lgestc Nov 13, 2024
bd75d23
fix integration test for observables
lgestc Nov 13, 2024
a3375b8
add simple validation for emails
lgestc Nov 13, 2024
f30cb5e
some validation tests
lgestc Nov 13, 2024
7abbcd8
add utils test
lgestc Nov 14, 2024
a469cf0
adjust test id
lgestc Nov 14, 2024
8262afd
add cleanup
lgestc Nov 14, 2024
ef9682b
fix typo
lgestc Nov 14, 2024
32eccfe
remove unnecessary count check
lgestc Nov 14, 2024
a8a2a80
update similar cases url
lgestc Nov 14, 2024
4dea787
update licensing feature
lgestc Nov 14, 2024
da14d26
remove unused type
lgestc Nov 14, 2024
7e6b648
remove types
lgestc Nov 14, 2024
723866f
remove unnecessary test
lgestc Nov 14, 2024
d25bca9
update rt types
lgestc Nov 14, 2024
90e290d
fix broken type paths
lgestc Nov 14, 2024
37a3666
add update observable rt test
lgestc Nov 14, 2024
e181cf4
add observable mock
lgestc Nov 14, 2024
618b029
move patch request test
lgestc Nov 14, 2024
ee2cab6
added mock observable types
lgestc Nov 14, 2024
ea3521a
new type for configure validation
lgestc Nov 14, 2024
6188b55
add some tests
lgestc Nov 14, 2024
b087859
add observables field to similarities
lgestc Nov 14, 2024
b1e61b3
use null instead of undefined
lgestc Nov 14, 2024
ec93a28
change flag name
lgestc Nov 14, 2024
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
3 changes: 3 additions & 0 deletions packages/kbn-check-mappings-update-cli/current_fields.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@
"external_service.pushed_by.full_name",
"external_service.pushed_by.profile_uid",
"external_service.pushed_by.username",
"observables",
"observables.typeKey",
"observables.value",
"owner",
"settings",
"settings.syncAlerts",
Expand Down
11 changes: 11 additions & 0 deletions packages/kbn-check-mappings-update-cli/current_mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,17 @@
}
}
},
"observables": {
"properties": {
"typeKey": {
"type": "keyword"
},
"value": {
"type": "keyword"
}
},
"type": "nested"
},
"owner": {
"type": "keyword"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"canvas-element": "cdedc2123eb8a1506b87a56b0bcce60f4ec08bc8",
"canvas-workpad": "9d82aafb19586b119e5c9382f938abe28c26ca5c",
"canvas-workpad-template": "c077b0087346776bb3542b51e1385d172cb24179",
"cases": "5433a9f1277f8f17bbc4fd20d33b1fc6d997931e",
"cases": "68e529e445a34d0bb61942f73bd4d87134d02ab3",
"cases-comments": "5cb0a421588831c2a950e50f486048b8aabbae25",
"cases-configure": "44ed7b8e0f44df39516b8870589b89e32224d2bf",
"cases-connector-mappings": "f9d1ac57e484e69506c36a8051e4d61f4a8cfd25",
Expand Down
13 changes: 13 additions & 0 deletions x-pack/plugins/cases/common/api/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
INTERNAL_DELETE_FILE_ATTACHMENTS_URL,
CASE_FIND_ATTACHMENTS_URL,
INTERNAL_PUT_CUSTOM_FIELDS_URL,
INTERNAL_CASE_OBSERVABLES_URL,
INTERNAL_CASE_OBSERVABLES_PATCH_URL,
} from '../constants';

export const getCaseDetailsUrl = (id: string): string => {
Expand Down Expand Up @@ -90,3 +92,14 @@ export const getCustomFieldReplaceUrl = (caseId: string, customFieldId: string):
customFieldId
);
};

export const getCaseCreateObservableUrl = (id: string): string => {
return INTERNAL_CASE_OBSERVABLES_URL.replace('{case_id}', id);
};

export const getCaseUpdateObservableUrl = (id: string, observableId: string): string => {
return INTERNAL_CASE_OBSERVABLES_PATCH_URL.replace('{case_id}', id).replace(
'{observable_id}',
observableId
);
};
44 changes: 44 additions & 0 deletions x-pack/plugins/cases/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ export const INTERNAL_DELETE_FILE_ATTACHMENTS_URL =
export const INTERNAL_GET_CASE_CATEGORIES_URL = `${CASES_INTERNAL_URL}/categories` as const;
export const INTERNAL_CASE_METRICS_URL = `${CASES_INTERNAL_URL}/metrics` as const;
export const INTERNAL_CASE_METRICS_DETAILS_URL = `${CASES_INTERNAL_URL}/metrics/{case_id}` as const;
export const INTERNAL_CASE_SIMILAR_CASES_URL = `${CASES_INTERNAL_URL}/_similar/{case_id}` as const;
lgestc marked this conversation as resolved.
Show resolved Hide resolved
export const INTERNAL_PUT_CUSTOM_FIELDS_URL = `${CASES_INTERNAL_URL}/{case_id}/custom_fields/{custom_field_id}`;
export const INTERNAL_CASE_OBSERVABLES_URL = `${CASES_INTERNAL_URL}/{case_id}/observables` as const;
export const INTERNAL_CASE_OBSERVABLES_PATCH_URL =
`${INTERNAL_CASE_OBSERVABLES_URL}/{observable_id}` as const;

/**
* Action routes
*/
Expand Down Expand Up @@ -199,6 +204,7 @@ export const DEFAULT_USER_SIZE = 10;
export const MAX_ASSIGNEES_PER_CASE = 10;
export const NO_ASSIGNEES_FILTERING_KEYWORD = 'none';
export const KIBANA_SYSTEM_USERNAME = 'elastic/kibana';
export const MAX_OBSERVABLES_PER_CASE = 50;

/**
* Delays
Expand Down Expand Up @@ -257,3 +263,41 @@ export const CASES_CONNECTOR_TIME_WINDOW_REGEX = '^[1-9][0-9]*[d,w]$';
* operation continues, otherwise we throw a 403.
*/
export const OWNER_FIELD = 'owner';

/**
* Exporting an array of built-in observable types for use in the application
*/
export const OBSERVABLE_TYPES_BUILTIN = [
lgestc marked this conversation as resolved.
Show resolved Hide resolved
{
label: 'IPv4',
key: 'observable-type-ipv4',
},
{
label: 'IPv6',
key: 'observable-type-ipv6',
},
{
label: 'URL',
key: 'observable-type-url',
},
{
label: 'Domain',
key: 'observable-type-domain',
},
{
label: 'Hostname',
key: 'observable-type-hostname',
},
{
label: 'File hash',
key: 'observable-type-file-hash',
},
{
label: 'File path',
key: 'observable-type-file-path',
},
{
label: 'Email',
key: 'observable-type-email',
},
];
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ export enum CASE_VIEW_PAGE_TABS {
ALERTS = 'alerts',
ACTIVITY = 'activity',
FILES = 'files',
OBSERVABLES = 'observables',
SIMILAR_CASES = 'similar_cases',
}
8 changes: 8 additions & 0 deletions x-pack/plugins/cases/common/types/api/case/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const basicCase: Case = {
value: 3,
},
],
observables: [],
lgestc marked this conversation as resolved.
Show resolved Hide resolved
};

describe('CasePostRequestRt', () => {
Expand Down Expand Up @@ -834,6 +835,13 @@ describe('CasePatchRequestRt', () => {
`The length of the value is too long. The maximum length is ${MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH}.`
);
});

it(`does not throw an error when observables are empty`, () => {
CasePatchRequestRt.decode({
lgestc marked this conversation as resolved.
Show resolved Hide resolved
...defaultRequest,
observables: [],
});
});
});

describe('CasesPatchRequestRt', () => {
Expand Down
28 changes: 27 additions & 1 deletion x-pack/plugins/cases/common/types/api/case/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ export const CasesFindResponseRt = rt.intersection([
CasesStatusResponseRt,
]);

export const SimilarityRt = rt.strict({
typeKey: rt.string,
value: rt.string,
});

export const SimilarCaseRt = rt.intersection([
CaseRt,
rt.strict({ similarities: rt.array(SimilarityRt) }),
]);

export const CasesSimilarResponseRt = rt.strict({
cases: rt.array(SimilarCaseRt),
page: rt.number,
per_page: rt.number,
total: rt.number,
});

/**
* Delete cases
*/
Expand Down Expand Up @@ -452,7 +469,10 @@ export const CasePatchRequestRt = rt.intersection([
/**
* The saved object ID and version
*/
rt.strict({ id: rt.string, version: rt.string }),
rt.strict({
id: rt.string,
version: rt.string,
}),
]);

export const CasesPatchRequestRt = rt.strict({
Expand Down Expand Up @@ -519,6 +539,8 @@ export const CasesByAlertIDRequestRt = rt.exact(

export const GetRelatedCasesByAlertResponseRt = rt.array(RelatedCaseRt);

export const SimilarCasesSearchRequestRt = paginationSchema({ maxPerPage: MAX_CASES_PER_PAGE });

export type CasePostRequest = rt.TypeOf<typeof CasePostRequestRt>;
export type CaseResolveResponse = rt.TypeOf<typeof CaseResolveResponseRt>;
export type CasesDeleteRequest = rt.TypeOf<typeof CasesDeleteRequestRt>;
Expand All @@ -542,3 +564,7 @@ export type CaseRequestCustomFields = rt.TypeOf<typeof CaseRequestCustomFieldsRt
export type CaseRequestCustomField = rt.TypeOf<typeof CustomFieldRt>;
export type BulkCreateCasesRequest = rt.TypeOf<typeof BulkCreateCasesRequestRt>;
export type BulkCreateCasesResponse = rt.TypeOf<typeof BulkCreateCasesResponseRt>;
export type SimilarCasesSearchRequest = rt.TypeOf<typeof SimilarCasesSearchRequestRt>;
export type CasesSimilarResponse = rt.TypeOf<typeof CasesSimilarResponseRt>;
export type SimilarCase = rt.TypeOf<typeof SimilarCaseRt>;
export type SimilarCases = SimilarCase[];
24 changes: 24 additions & 0 deletions x-pack/plugins/cases/common/types/api/configure/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,24 @@ describe('configure', () => {
});
});

it('has expected attributes in request with observableTypes', () => {
const request = {
...defaultRequest,
observableTypes: [
{
key: '371357ae-77ce-44bd-88b7-fbba9c80501f',
label: 'Example Label',
},
],
};
const query = ConfigurationRequestRt.decode(request);

expect(query).toStrictEqual({
_tag: 'Right',
right: request,
});
});

it(`limits customFields to ${MAX_CUSTOM_FIELDS_PER_CASE}`, () => {
const customFields = new Array(MAX_CUSTOM_FIELDS_PER_CASE + 1).fill({
key: 'text_custom_field',
Expand Down Expand Up @@ -171,6 +189,12 @@ describe('configure', () => {
connector: serviceNow,
closure_type: 'close-by-user',
version: 'WzQ3LDFd',
observableTypes: [
lgestc marked this conversation as resolved.
Show resolved Hide resolved
{
label: 'Example Label',
key: '1e4650b3-b66b-4067-bc5d-6867be6ee73b',
},
],
};

it('has expected attributes in request', () => {
Expand Down
8 changes: 7 additions & 1 deletion x-pack/plugins/cases/common/types/api/configure/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import {
CustomFieldNumberTypeRt,
} from '../../domain';
import type { Configurations, Configuration } from '../../domain/configure/v1';
import { ConfigurationBasicWithoutOwnerRt, ClosureTypeRt } from '../../domain/configure/v1';
import {
ConfigurationBasicWithoutOwnerRt,
ClosureTypeRt,
ObservableTypesConfigurationRt,
} from '../../domain/configure/v1';
import { CaseConnectorRt } from '../../domain/connector/v1';
import { CaseBaseOptionalFieldsRequestRt } from '../case/v1';
import {
Expand Down Expand Up @@ -167,6 +171,7 @@ export const ConfigurationRequestRt = rt.intersection([
rt.partial({
customFields: CustomFieldsConfigurationRt,
templates: TemplatesConfigurationRt,
observableTypes: ObservableTypesConfigurationRt,
lgestc marked this conversation as resolved.
Show resolved Hide resolved
})
),
]);
Expand All @@ -192,6 +197,7 @@ export const ConfigurationPatchRequestRt = rt.intersection([
connector: ConfigurationBasicWithoutOwnerRt.type.props.connector,
customFields: CustomFieldsConfigurationRt,
templates: TemplatesConfigurationRt,
observableTypes: ObservableTypesConfigurationRt,
})
),
rt.strict({ version: rt.string }),
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/common/types/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './connector/latest';
export * from './attachment/latest';
export * from './metrics/latest';
export * from './custom_field/latest';
export * from './observable/latest';

// V1
export * as configureApiV1 from './configure/v1';
Expand All @@ -30,3 +31,4 @@ export * as connectorApiV1 from './connector/v1';
export * as attachmentApiV1 from './attachment/v1';
export * as metricsApiV1 from './metrics/v1';
export * as customFieldsApiV1 from './custom_field/v1';
export * as observableApiV1 from './observable/v1';
8 changes: 8 additions & 0 deletions x-pack/plugins/cases/common/types/api/observable/latest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export * from './v1';
27 changes: 27 additions & 0 deletions x-pack/plugins/cases/common/types/api/observable/v1.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 { AddObservableRequestRt } from './v1';

describe('AddObservableRequestRT', () => {
lgestc marked this conversation as resolved.
Show resolved Hide resolved
it('has expected attributes in request', () => {
const defaultRequest = {
observable: {
description: undefined,
typeKey: 'ef528526-2af9-4345-9b78-046512c5bbd6',
value: '[email protected]',
},
};

const query = AddObservableRequestRt.decode(defaultRequest);

expect(query).toStrictEqual({
_tag: 'Right',
right: defaultRequest,
});
});
});
38 changes: 38 additions & 0 deletions x-pack/plugins/cases/common/types/api/observable/v1.ts
lgestc marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { CaseObservableRt, ObservablePatch } from '../../domain';
lgestc marked this conversation as resolved.
Show resolved Hide resolved

/**
* Observables
*/

export const AddObservableRequestRt = rt.strict({
observable: ObservablePatch,
});

export const UpdateObservableRequestRt = rt.strict({
observable: ObservablePatch,
Copy link
Member

Choose a reason for hiding this comment

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

I think the update request should be different from the add request. The reason is you should not be able to update the type of the observable. Only the value and the description are valid fields to be updated.

});

export const BulkGetObservablesResponseRt = rt.strict({
lgestc marked this conversation as resolved.
Show resolved Hide resolved
observables: CaseObservableRt,
errors: rt.array(
rt.strict({
error: rt.string,
message: rt.string,
status: rt.union([rt.undefined, rt.number]),
attachmentId: rt.string,
})
),
});

export type AddObservableRequest = rt.TypeOf<typeof AddObservableRequestRt>;
export type UpdateObservableRequest = rt.TypeOf<typeof UpdateObservableRequestRt>;

export type ObservableRequest = AddObservableRequest;
lgestc marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/common/types/domain/case/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const basicCase = {
value: 0,
},
],
observables: [],
};

describe('RelatedCaseRt', () => {
Expand Down Expand Up @@ -204,6 +205,7 @@ describe('CaseAttributesRt', () => {
value: 0,
},
],
observables: [],
};

it('has expected attributes in request', () => {
Expand Down
Loading