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

[DE] - Investigation fields followup #164133

Merged
merged 70 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
eb33083
trying to recreate changes for new custom highlighted fields
yctercero Aug 3, 2023
0b98a19
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 3, 2023
110eb99
got most of the code ported over
yctercero Aug 6, 2023
5a249b9
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 6, 2023
ccf1c24
adding back in tests and cleanup
yctercero Aug 6, 2023
cbbbbf9
continued cleanup
yctercero Aug 7, 2023
6f5ac58
cleanup and making by default additive
yctercero Aug 9, 2023
f416282
cleaning up types
yctercero Aug 10, 2023
a42910e
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 10, 2023
888c042
updating so now everywhere pulls latest custom highlighted fields
yctercero Aug 11, 2023
b8c3923
dont need to map it in alert since always accessing latest
yctercero Aug 11, 2023
b272000
continued cleanup
yctercero Aug 11, 2023
7b97501
continued cleanup
yctercero Aug 12, 2023
eda33ef
continued cleanup
yctercero Aug 12, 2023
5920bba
trying to fix tests
yctercero Aug 12, 2023
317873d
trying to fix tests
yctercero Aug 13, 2023
0363fb8
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 13, 2023
e0b6a90
trying to fix tests
yctercero Aug 13, 2023
bfda622
trying to fix tests
yctercero Aug 14, 2023
44aa7a5
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 14, 2023
013c437
cleanup, cleanup, everybody cleanup
yctercero Aug 14, 2023
7376b1b
addressing PR feedback
yctercero Aug 14, 2023
07d0e41
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 14, 2023
2fd6f51
updated jest test and linting issue
yctercero Aug 14, 2023
a7379e5
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Aug 14, 2023
df9d604
working on cypress tests and renaming field to investigation_fields
yctercero Aug 15, 2023
3f67bc5
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 15, 2023
ba11dba
Merge branch 'custom_highlighted_fields' of github.com:yctercero/kiba…
yctercero Aug 15, 2023
97d5adf
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Aug 15, 2023
2188d0d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Aug 15, 2023
e0f8fc9
adding to older event details flyout after refactor
yctercero Aug 15, 2023
a416d1a
Merge branch 'custom_highlighted_fields' of github.com:yctercero/kiba…
yctercero Aug 15, 2023
5a225bf
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 15, 2023
49bdfc0
skipping flakey tests, linked to issue as required follow up for release
yctercero Aug 15, 2023
2d01586
some cleanup
yctercero Aug 15, 2023
294387d
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 15, 2023
9d6c1e3
Merge branch 'main' into custom_highlighted_fields
kibanamachine Aug 15, 2023
f62deca
Merge branch 'main' of github.com:elastic/kibana into custom_highligh…
yctercero Aug 16, 2023
e3b8078
trying to get ci to pass
yctercero Aug 16, 2023
983053e
Merge branch 'custom_highlighted_fields' of github.com:yctercero/kiba…
yctercero Aug 16, 2023
3ed8d16
updating field name
yctercero Aug 16, 2023
e2284b6
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 16, 2023
570ca32
updating field typing
yctercero Aug 17, 2023
4351887
remove unused type
yctercero Aug 17, 2023
8532c68
fixing up tests
yctercero Aug 20, 2023
569bacc
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 20, 2023
d3dcb90
adjusting field name per feedback, adding tests
yctercero Aug 21, 2023
0d981f2
updating tests
yctercero Aug 22, 2023
90b6322
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 22, 2023
8c3d391
test cleanup
yctercero Aug 22, 2023
aabb918
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 22, 2023
465c335
update patch test
yctercero Aug 23, 2023
c330ba0
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 23, 2023
9f17e15
updating per PR feedback - naming and tests
yctercero Aug 24, 2023
dbc5dca
cleanup
yctercero Aug 24, 2023
7bd0c87
finish updating field name
yctercero Aug 24, 2023
703ca67
Merge branch 'main' into investigation_fields_followup
kibanamachine Aug 24, 2023
53f4796
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 25, 2023
c4ed6a3
Merge branch 'main' into investigation_fields_followup
kibanamachine Aug 25, 2023
49aaec2
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 28, 2023
e1703de
Merge branch 'investigation_fields_followup' of github.com:yctercero/…
yctercero Aug 28, 2023
99061d5
trying to fix flakey burn test
yctercero Aug 28, 2023
62f7dc7
trying to figure out flaking burn test
yctercero Aug 28, 2023
d391c63
Merge branch 'main' of github.com:elastic/kibana into investigation_f…
yctercero Aug 28, 2023
362cd46
cypress cleanup
yctercero Aug 28, 2023
4e6eff5
Merge branch 'main' into investigation_fields_followup
kibanamachine Aug 29, 2023
15efe3a
Merge branch 'main' into investigation_fields_followup
kibanamachine Aug 29, 2023
f6dbac5
cypress cleanup
yctercero Aug 29, 2023
b21defb
Merge branch 'investigation_fields_followup' of github.com:yctercero/…
yctercero Aug 29, 2023
10f6317
Merge branch 'main' into investigation_fields_followup
kibanamachine Aug 29, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE) => ({
enabled: true,
false_positives: ['false positive 1', 'false positive 2'],
from: 'now-6m',
investigation_fields: ['custom.field1', 'custom.field2'],
immutable: false,
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import * as t from 'io-ts';
import { listArray } from '@kbn/securitysolution-io-ts-list-types';
import { NonEmptyString, version, UUID } from '@kbn/securitysolution-io-ts-types';
import { NonEmptyString, version, UUID, NonEmptyArray } from '@kbn/securitysolution-io-ts-types';
import { max_signals, threat } from '@kbn/securitysolution-io-ts-alerting-types';

export type RuleObjectId = t.TypeOf<typeof RuleObjectId>;
Expand Down Expand Up @@ -55,14 +55,6 @@ export const RuleAuthorArray = t.array(t.string); // should be non-empty strings
export type RuleFalsePositiveArray = t.TypeOf<typeof RuleFalsePositiveArray>;
export const RuleFalsePositiveArray = t.array(t.string); // should be non-empty strings?

/**
* User defined fields to display in areas such as alert details and exceptions auto-populate
* Field added in PR - https://github.com/elastic/kibana/pull/163235
* @example const investigationFields: RuleCustomHighlightedFieldArray = ['host.os.name']
*/
export type RuleCustomHighlightedFieldArray = t.TypeOf<typeof RuleCustomHighlightedFieldArray>;
export const RuleCustomHighlightedFieldArray = t.array(NonEmptyString);

export type RuleReferenceArray = t.TypeOf<typeof RuleReferenceArray>;
export const RuleReferenceArray = t.array(t.string); // should be non-empty strings?

Expand Down Expand Up @@ -265,3 +257,32 @@ export const RelatedIntegration = t.exact(
*/
export type RelatedIntegrationArray = t.TypeOf<typeof RelatedIntegrationArray>;
export const RelatedIntegrationArray = t.array(RelatedIntegration);

/**
* Schema for fields relating to investigation fields, these are user defined fields we use to highlight
* in various features in the UI such as alert details flyout and exceptions auto-population from alert.
* Added in PR #163235
* Right now we only have a single field but anticipate adding more related fields to store various
* configuration states such as `override` - where a user might say if they want only these fields to
* display, or if they want these fields + the fields we select. When expanding this field, it may look
* something like:
* export const investigationFields = t.intersection([
* t.exact(
* t.type({
* field_names: NonEmptyArray(NonEmptyString),
* })
* ),
* t.exact(
* t.partial({
* overide: t.boolean,
* })
* ),
* ]);
*
*/
export type InvestigationFields = t.TypeOf<typeof InvestigationFields>;
export const InvestigationFields = t.exact(
t.type({
field_names: NonEmptyArray(NonEmptyString),
})
);
Original file line number Diff line number Diff line change
Expand Up @@ -1290,10 +1290,38 @@ describe('rules schema', () => {
expect(getPaths(left(message.errors))).toEqual(['invalid keys "data_view_id"']);
});

test('You can optionally send in an array of investigation_fields', () => {
test('You can omit investigation_fields', () => {
// getCreateRulesSchemaMock doesn't include investigation_fields
const payload: RuleCreateProps = getCreateRulesSchemaMock();

const decoded = RuleCreateProps.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('You cannot pass empty object for investigation_fields', () => {
const payload: Omit<RuleCreateProps, 'investigation_fields'> & {
investigation_fields: unknown;
} = {
...getCreateRulesSchemaMock(),
investigation_fields: {},
};

const decoded = RuleCreateProps.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "undefined" supplied to "investigation_fields,field_names"',
]);
expect(message.schema).toEqual({});
});

test('You can send in investigation_fields', () => {
const payload: RuleCreateProps = {
...getCreateRulesSchemaMock(),
investigation_fields: ['field1', 'field2'],
investigation_fields: { field_names: ['field1', 'field2'] },
};

const decoded = RuleCreateProps.decode(payload);
Expand All @@ -1303,19 +1331,49 @@ describe('rules schema', () => {
expect(message.schema).toEqual(payload);
});

test('You cannot send in an array of investigation_fields that are numbers', () => {
test('You cannot send in an empty array of investigation_fields.field_names', () => {
const payload = {
...getCreateRulesSchemaMock(),
investigation_fields: { field_names: [] },
};

const decoded = RuleCreateProps.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "[]" supplied to "investigation_fields,field_names"',
]);
expect(message.schema).toEqual({});
});

test('You cannot send in an array of investigation_fields.field_names that are numbers', () => {
const payload = {
...getCreateRulesSchemaMock(),
investigation_fields: { field_names: [0, 1, 2] },
};

const decoded = RuleCreateProps.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "0" supplied to "investigation_fields,field_names"',
'Invalid value "1" supplied to "investigation_fields,field_names"',
'Invalid value "2" supplied to "investigation_fields,field_names"',
]);
expect(message.schema).toEqual({});
});

test('You cannot send in investigation_fields without specifying fields', () => {
const payload = {
...getCreateRulesSchemaMock(),
investigation_fields: [0, 1, 2],
investigation_fields: { foo: true },
};

const decoded = RuleCreateProps.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
expect(getPaths(left(message.errors))).toEqual([
'Invalid value "0" supplied to "investigation_fields"',
'Invalid value "1" supplied to "investigation_fields"',
'Invalid value "2" supplied to "investigation_fields"',
'Invalid value "undefined" supplied to "investigation_fields,field_names"',
]);
expect(message.schema).toEqual({});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,47 +234,73 @@ describe('Rule response schema', () => {
});

describe('investigation_fields', () => {
test('it should validate rule with empty array for "investigation_fields"', () => {
test('it should validate rule with "investigation_fields"', () => {
const payload = getRulesSchemaMock();
payload.investigation_fields = [];
payload.investigation_fields = { field_names: ['foo', 'bar'] };

const decoded = RuleResponse.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
const expected = { ...getRulesSchemaMock(), investigation_fields: [] };
const expected = {
...getRulesSchemaMock(),
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expected);
});

test('it should validate rule with "investigation_fields"', () => {
const payload = getRulesSchemaMock();
payload.investigation_fields = ['foo', 'bar'];
test('it should validate undefined for "investigation_fields"', () => {
const payload: RuleResponse = {
...getRulesSchemaMock(),
investigation_fields: undefined,
};

const decoded = RuleResponse.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
const expected = { ...getRulesSchemaMock(), investigation_fields: ['foo', 'bar'] };
const expected = { ...getRulesSchemaMock(), investigation_fields: undefined };

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expected);
});

test('it should validate undefined for "investigation_fields"', () => {
test('it should validate "investigation_fields" not in schema', () => {
const payload: RuleResponse = {
...getRulesSchemaMock(),
investigation_fields: undefined,
};

delete payload.investigation_fields;

const decoded = RuleResponse.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
const expected = { ...getRulesSchemaMock(), investigation_fields: undefined };
const expected = getRulesSchemaMock();

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expected);
});

test('it should NOT validate an empty array for "investigation_fields.field_names"', () => {
const payload: RuleResponse = {
...getRulesSchemaMock(),
investigation_fields: {
field_names: [],
},
};

const decoded = RuleResponse.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([
'Invalid value "[]" supplied to "investigation_fields,field_names"',
'Invalid value "{"field_names":[]}" supplied to "investigation_fields"',
]);
expect(message.schema).toEqual({});
});

test('it should NOT validate a string for "investigation_fields"', () => {
const payload: Omit<RuleResponse, 'investigation_fields'> & {
investigation_fields: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
RelatedIntegrationArray,
RequiredFieldArray,
RuleAuthorArray,
RuleCustomHighlightedFieldArray,
InvestigationFields,
RuleDescription,
RuleFalsePositiveArray,
RuleFilterArray,
Expand Down Expand Up @@ -117,7 +117,7 @@ export const baseSchema = buildRuleSchemas({
output_index: AlertsIndex,
namespace: AlertsIndexNamespace,
meta: RuleMetadata,
investigation_fields: RuleCustomHighlightedFieldArray,
investigation_fields: InvestigationFields,
// Throttle
throttle: RuleActionThrottle,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ const EventDetailsComponent: React.FC<Props> = ({
isReadOnly,
}}
goToTable={goToTableTab}
investigationFields={maybeRule?.investigation_fields ?? []}
investigationFields={maybeRule?.investigation_fields?.field_names ?? []}
/>
<EuiSpacer size="xl" />
<Insights
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
Expand Down Expand Up @@ -636,7 +636,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
Expand All @@ -661,7 +661,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
Expand Down Expand Up @@ -705,7 +705,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
Expand Down Expand Up @@ -758,7 +758,7 @@ describe('helpers', () => {
],
},
],
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
Expand Down Expand Up @@ -787,13 +787,13 @@ describe('helpers', () => {
threat: getThreatMock(),
timestamp_override: 'event.ingest',
timestamp_override_fallback_disabled: true,
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
};

expect(result).toEqual(expected);
});

test('returns formatted object if investigation_fields is empty array', () => {
test('returns formatted object if investigationFields is empty array', () => {
const mockStepData: AboutStepRule = {
...mockData,
investigationFields: [],
Expand All @@ -817,7 +817,7 @@ describe('helpers', () => {
timestamp_override: undefined,
timestamp_override_fallback_disabled: undefined,
threat: getThreatMock(),
investigation_fields: [],
investigation_fields: undefined,
};

expect(result).toEqual(expected);
Expand All @@ -843,7 +843,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: ['foo', 'bar'],
investigation_fields: { field_names: ['foo', 'bar'] },
threat_indicator_path: undefined,
timestamp_override: undefined,
timestamp_override_fallback_disabled: undefined,
Expand All @@ -855,7 +855,7 @@ describe('helpers', () => {
test('returns formatted object if investigation_fields includes empty string', () => {
const mockStepData: AboutStepRule = {
...mockData,
investigationFields: [' '],
investigationFields: [' '],
};
const result = formatAboutStepData(mockStepData);
const expected: AboutStepRuleJson = {
Expand All @@ -872,7 +872,7 @@ describe('helpers', () => {
severity_mapping: [],
tags: ['tag1', 'tag2'],
threat: getThreatMock(),
investigation_fields: [],
investigation_fields: undefined,
threat_indicator_path: undefined,
timestamp_override: undefined,
timestamp_override_fallback_disabled: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ export const formatAboutStepData = (

const detectionExceptionLists =
exceptionsList != null ? exceptionsList.filter((list) => list.type !== 'endpoint') : [];
const isinvestigationFieldsEmpty = investigationFields.every((item) => isEmpty(item.trim()));

const resp = {
author: author.filter((item) => !isEmpty(item)),
Expand All @@ -525,7 +526,9 @@ export const formatAboutStepData = (
: {}),
false_positives: falsePositives.filter((item) => !isEmpty(item)),
references: references.filter((item) => !isEmpty(item)),
investigation_fields: investigationFields.filter((item) => !isEmpty(item.trim())),
investigation_fields: isinvestigationFieldsEmpty
? undefined
: { field_names: investigationFields },
risk_score: riskScore.value,
risk_score_mapping: riskScore.isMappingChecked
? riskScore.mapping.filter((m) => m.field != null && m.field !== '')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ describe('When the add exception modal is opened', () => {
rules={[
{
...getRulesSchemaMock(),
investigation_fields: ['foo.bar'],
investigation_fields: { field_names: ['foo.bar'] },
exceptions_list: [],
} as Rule,
]}
Expand Down
Loading