diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.test.tsx
new file mode 100644
index 0000000000000..86c36187d73fa
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.test.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { mountWithIntl } from 'test_utils/enzyme_helpers';
+
+import { TestProviders } from '../../../../mock';
+
+import { ExitCodeDraggable } from './exit_code_draggable';
+
+describe('ExitCodeDraggable', () => {
+ test('it renders the expected text and exit code, when both text and an endgameExitCode are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('with exit code0');
+ });
+
+ test('it returns an empty string when text is provided, but endgameExitCode is undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it returns an empty string when text is provided, but endgameExitCode is null', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it returns an empty string when text is provided, but endgameExitCode is an empty string', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it renders just the exit code when text is undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('1');
+ });
+
+ test('it renders just the exit code when text is null', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('1');
+ });
+
+ test('it renders just the exit code when text is an empty string', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('1');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx
new file mode 100644
index 0000000000000..68acc58972370
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.test.tsx
@@ -0,0 +1,110 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { mountWithIntl } from 'test_utils/enzyme_helpers';
+
+import { TestProviders } from '../../../../mock';
+
+import { FileDraggable } from './file_draggable';
+
+describe('FileDraggable', () => {
+ test('it prefers fileName and filePath over endgameFileName and endgameFilePath when all of them are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[fileName]in[filePath]');
+ });
+
+ test('it returns an empty string when none of the files or paths are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it renders just the endgameFileName if only endgameFileName is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[endgameFileName]');
+ });
+
+ test('it renders "in endgameFilePath" if only endgameFilePath is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('in[endgameFilePath]');
+ });
+
+ test('it renders just the filename if only fileName is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[fileName]');
+ });
+
+ test('it renders "in filePath" if only filePath is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('in[filePath]');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx
index af8876927d235..a1c43f3ecb163 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx
@@ -31,8 +31,8 @@ export const FileDraggable = React.memo(
return null;
}
- const fileNameIsKnown =
- !isNillEmptyOrNotFinite(fileName) || !isNillEmptyOrNotFinite(endgameFileName);
+ const filePathIsKnown =
+ !isNillEmptyOrNotFinite(filePath) || !isNillEmptyOrNotFinite(endgameFilePath);
return (
<>
@@ -58,7 +58,7 @@ export const FileDraggable = React.memo(
) : null}
- {fileNameIsKnown && (
+ {filePathIsKnown && (
{i18n.IN}
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx
index 2cd75344deaf6..98a99cb6e4089 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx
@@ -8,7 +8,15 @@ import { cloneDeep } from 'lodash/fp';
import { TimelineNonEcsData } from '../../../../graphql/types';
import { mockTimelineData } from '../../../../mock';
-import { deleteItemIdx, findItem, getValues, isNillEmptyOrNotFinite, showVia } from './helpers';
+import {
+ deleteItemIdx,
+ findItem,
+ getValues,
+ isFileEvent,
+ isNillEmptyOrNotFinite,
+ isProcessStoppedOrTerminationEvent,
+ showVia,
+} from './helpers';
describe('helpers', () => {
describe('#deleteItemIdx', () => {
@@ -126,66 +134,150 @@ describe('helpers', () => {
describe('#isNillEmptyOrNotFinite', () => {
test('undefined returns true', () => {
- expect(isNillEmptyOrNotFinite(undefined)).toEqual(true);
+ expect(isNillEmptyOrNotFinite(undefined)).toBe(true);
});
test('null returns true', () => {
- expect(isNillEmptyOrNotFinite(null)).toEqual(true);
+ expect(isNillEmptyOrNotFinite(null)).toBe(true);
});
test('empty string returns true', () => {
- expect(isNillEmptyOrNotFinite('')).toEqual(true);
+ expect(isNillEmptyOrNotFinite('')).toBe(true);
});
test('empty array returns true', () => {
- expect(isNillEmptyOrNotFinite([])).toEqual(true);
+ expect(isNillEmptyOrNotFinite([])).toBe(true);
});
test('NaN returns true', () => {
- expect(isNillEmptyOrNotFinite(NaN)).toEqual(true);
+ expect(isNillEmptyOrNotFinite(NaN)).toBe(true);
});
test('Infinity returns true', () => {
- expect(isNillEmptyOrNotFinite(Infinity)).toEqual(true);
+ expect(isNillEmptyOrNotFinite(Infinity)).toBe(true);
});
test('a single space string returns false', () => {
- expect(isNillEmptyOrNotFinite(' ')).toEqual(false);
+ expect(isNillEmptyOrNotFinite(' ')).toBe(false);
});
test('a simple string returns false', () => {
- expect(isNillEmptyOrNotFinite('a simple string')).toEqual(false);
+ expect(isNillEmptyOrNotFinite('a simple string')).toBe(false);
});
test('the number 0 returns false', () => {
- expect(isNillEmptyOrNotFinite(0)).toEqual(false);
+ expect(isNillEmptyOrNotFinite(0)).toBe(false);
});
test('a non-empty array return false', () => {
- expect(isNillEmptyOrNotFinite(['non empty array'])).toEqual(false);
+ expect(isNillEmptyOrNotFinite(['non empty array'])).toBe(false);
});
});
describe('#showVia', () => {
test('undefined returns false', () => {
- expect(showVia(undefined)).toEqual(false);
+ expect(showVia(undefined)).toBe(false);
});
test('null returns false', () => {
- expect(showVia(undefined)).toEqual(false);
+ expect(showVia(null)).toBe(false);
});
- test('empty string false', () => {
- expect(showVia('')).toEqual(false);
+ test('empty string returns false', () => {
+ expect(showVia('')).toBe(false);
});
test('a random string returns false', () => {
- expect(showVia('a random string')).toEqual(false);
+ expect(showVia('a random string')).toBe(false);
});
- ['file_create_event', 'created', 'file_delete_event', 'deleted'].forEach(eventAction => {
- test(`${eventAction} returns true`, () => {
- expect(showVia(eventAction)).toEqual(true);
+ describe('valid values', () => {
+ const validValues = ['file_create_event', 'created', 'file_delete_event', 'deleted'];
+
+ validValues.forEach(eventAction => {
+ test(`${eventAction} returns true`, () => {
+ expect(showVia(eventAction)).toBe(true);
+ });
+ });
+
+ validValues.forEach(value => {
+ const upperCaseValue = value.toUpperCase();
+
+ test(`${upperCaseValue} (upper case) returns true`, () => {
+ expect(showVia(upperCaseValue)).toBe(true);
+ });
+ });
+ });
+ });
+
+ describe('#isFileEvent', () => {
+ test('returns true when both eventCategory and eventDataset are file', () => {
+ expect(isFileEvent({ eventCategory: 'file', eventDataset: 'file' })).toBe(true);
+ });
+
+ test('returns false when eventCategory and eventDataset are undefined', () => {
+ expect(isFileEvent({ eventCategory: undefined, eventDataset: undefined })).toBe(false);
+ });
+
+ test('returns false when eventCategory and eventDataset are null', () => {
+ expect(isFileEvent({ eventCategory: null, eventDataset: null })).toBe(false);
+ });
+
+ test('returns false when eventCategory and eventDataset are random values', () => {
+ expect(
+ isFileEvent({ eventCategory: 'random category', eventDataset: 'random dataset' })
+ ).toBe(false);
+ });
+
+ test('returns true when just eventCategory is file', () => {
+ expect(isFileEvent({ eventCategory: 'file', eventDataset: undefined })).toBe(true);
+ });
+
+ test('returns true when just eventDataset is file', () => {
+ expect(isFileEvent({ eventCategory: null, eventDataset: 'file' })).toBe(true);
+ });
+
+ test('returns true when just eventCategory is File with a capitol F', () => {
+ expect(isFileEvent({ eventCategory: 'File', eventDataset: '' })).toBe(true);
+ });
+
+ test('returns true when just eventDataset is File with a capitol F', () => {
+ expect(isFileEvent({ eventCategory: 'random', eventDataset: 'File' })).toBe(true);
+ });
+ });
+
+ describe('#isProcessStoppedOrTerminationEvent', () => {
+ test('returns false when eventAction is undefined', () => {
+ expect(isProcessStoppedOrTerminationEvent(undefined)).toBe(false);
+ });
+
+ test('returns false when eventAction is null', () => {
+ expect(isProcessStoppedOrTerminationEvent(null)).toBe(false);
+ });
+
+ test('returns false when eventAction is an empty string', () => {
+ expect(isProcessStoppedOrTerminationEvent('')).toBe(false);
+ });
+
+ test('returns false when eventAction is a random value', () => {
+ expect(isProcessStoppedOrTerminationEvent('a random value')).toBe(false);
+ });
+
+ describe('valid values', () => {
+ const validValues = ['process_stopped', 'termination_event'];
+
+ validValues.forEach(value => {
+ test(`returns true when eventAction is ${value}`, () => {
+ expect(isProcessStoppedOrTerminationEvent(value)).toBe(true);
+ });
+ });
+
+ validValues.forEach(value => {
+ const upperCaseValue = value.toUpperCase();
+
+ test(`returns true when eventAction is (upper case) ${upperCaseValue}`, () => {
+ expect(isProcessStoppedOrTerminationEvent(upperCaseValue)).toBe(true);
+ });
});
});
});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx
index b8e55fd29930e..26aa5cea51ce7 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx
@@ -43,7 +43,7 @@ export function isNillEmptyOrNotFinite(value: string | number | T[] | null |
return isNumber(value) ? !isFinite(value) : isEmpty(value);
}
-export const isFimEvent = ({
+export const isFileEvent = ({
eventCategory,
eventDataset,
}: {
@@ -53,8 +53,11 @@ export const isFimEvent = ({
(eventCategory != null && eventCategory.toLowerCase() === 'file') ||
(eventDataset != null && eventDataset.toLowerCase() === 'file');
-export const isProcessStoppedOrTerminationEvent = (eventAction: string | null | undefined) =>
- eventAction === 'process_stopped' || eventAction === 'termination_event';
+export const isProcessStoppedOrTerminationEvent = (
+ eventAction: string | null | undefined
+): boolean => ['process_stopped', 'termination_event'].includes(`${eventAction}`.toLowerCase());
-export const showVia = (eventAction: string | null | undefined) =>
- ['file_create_event', 'created', 'file_delete_event', 'deleted'].includes(`${eventAction}`);
+export const showVia = (eventAction: string | null | undefined): boolean =>
+ ['file_create_event', 'created', 'file_delete_event', 'deleted'].includes(
+ `${eventAction}`.toLowerCase()
+ );
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.test.tsx
new file mode 100644
index 0000000000000..9f6a8676694e4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.test.tsx
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { mountWithIntl } from 'test_utils/enzyme_helpers';
+
+import { TestProviders } from '../../../../mock';
+
+import { ParentProcessDraggable } from './parent_process_draggable';
+
+describe('ParentProcessDraggable', () => {
+ test('displays the text, endgameParentProcessName, and processPpid when they are all provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('via parent process[endgameParentProcessName](456)');
+ });
+
+ test('displays nothing when the text is provided, but endgameParentProcessName and processPpid are both undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('displays the text and processPpid when endgameParentProcessName is undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('via parent process(456)');
+ });
+
+ test('displays the processPpid when both endgameParentProcessName and text are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('(456)');
+ });
+
+ test('displays the text and endgameParentProcessName when processPpid is undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('via parent process[endgameParentProcessName]');
+ });
+
+ test('displays the endgameParentProcessName when both processPpid and text are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[endgameParentProcessName]');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx
index 1423ccad7211e..7cb6c3704a238 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx
@@ -8,27 +8,19 @@ import * as React from 'react';
import { DraggableBadge } from '../../../draggables';
-import {
- isNillEmptyOrNotFinite,
- isProcessStoppedOrTerminationEvent,
- TokensFlexItem,
-} from './helpers';
+import { isNillEmptyOrNotFinite, TokensFlexItem } from './helpers';
interface Props {
contextId: string;
endgameParentProcessName: string | null | undefined;
- eventAction: string | null | undefined;
eventId: string;
processPpid: number | undefined | null;
text: string | null | undefined;
}
export const ParentProcessDraggable = React.memo(
- ({ contextId, endgameParentProcessName, eventAction, eventId, processPpid, text }) => {
- if (
- (isNillEmptyOrNotFinite(endgameParentProcessName) && isNillEmptyOrNotFinite(processPpid)) ||
- isProcessStoppedOrTerminationEvent(eventAction)
- ) {
+ ({ contextId, endgameParentProcessName, eventId, processPpid, text }) => {
+ if (isNillEmptyOrNotFinite(endgameParentProcessName) && isNillEmptyOrNotFinite(processPpid)) {
return null;
}
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx
index 826af00c39011..27b41fa6d5d76 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx
@@ -10,7 +10,7 @@ import * as React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { TestProviders } from '../../../../mock';
-import { ProcessDraggable } from './process_draggable';
+import { ProcessDraggable, ProcessDraggableWithNonExistentProcess } from './process_draggable';
describe('ProcessDraggable', () => {
describe('rendering', () => {
@@ -317,5 +317,211 @@ describe('ProcessDraggable', () => {
);
expect(wrapper.text()).toEqual('[process-executable](123)');
});
+
+ test('it prefers process.name when process.executable and endgame.process_name are also provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[process-name]');
+ });
+
+ test('it falls back to rendering process.executable when process.name is NOT provided, but process.executable and endgame.process_name are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[process-executable]');
+ });
+
+ test('it falls back to rendering endgame.process_name when process.name and process.executable are NOT provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[endgame-process-name]');
+ });
+
+ test('it prefers process.pid when endgame.pid is also provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('(123)');
+ });
+
+ test('it falls back to rendering endgame.pid when process.pid is NOT provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('(999)');
+ });
+ });
+});
+
+describe('ProcessDraggableWithNonExistentProcess', () => {
+ test('it renders the expected text when all fields are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('an unknown process');
+ });
+
+ test('it renders the expected text when just endgamePid is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('(999)');
+ });
+
+ test('it renders the expected text when just endgameProcessName is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[endgameProcessName]');
+ });
+
+ test('it renders the expected text when just processExecutable is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processExecutable]');
+ });
+
+ test('it renders the expected text when just processName is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processName]');
+ });
+
+ test('it renders the expected text when just processPid is provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('(123)');
+ });
+
+ test('it renders the expected text when all values are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processName](123)');
});
});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_hash.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_hash.test.tsx
new file mode 100644
index 0000000000000..35ce52b576d8e
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_hash.test.tsx
@@ -0,0 +1,89 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { mountWithIntl } from 'test_utils/enzyme_helpers';
+
+import { TestProviders } from '../../../../mock';
+
+import { ProcessHash } from './process_hash';
+
+describe('ProcessHash', () => {
+ test('displays the processHashMd5, processHashSha1, and processHashSha256 when they are all provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processHashSha256][processHashSha1][processHashMd5]');
+ });
+
+ test('displays nothing when processHashMd5, processHashSha1, and processHashSha256 are all undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('displays just processHashMd5 when the other hashes are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processHashMd5]');
+ });
+
+ test('displays just processHashSha1 when the other hashes are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processHashSha1]');
+ });
+
+ test('displays just processHashSha256 when the other hashes are undefined', () => {
+ const wrapper = mountWithIntl(
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[processHashSha256]');
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_hash.tsx
similarity index 100%
rename from x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx
rename to x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_hash.tsx
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx
index 91a848c8e02ed..308f80429d57e 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx
@@ -1079,5 +1079,522 @@ describe('SystemGenericFileDetails', () => {
'[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][text-123][processName-123](123)[arg-1][arg-2][arg-3][process-title-123]with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]'
);
});
+
+ test('it renders a FileDraggable when endgameFileName and endgameFilePath are provided, but fileName and filePath are NOT provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+ expect(wrapper.text()).toEqual('[endgameFileName]in[endgameFilePath]an unknown process');
+ });
+
+ test('it prefers to render fileName and filePath over endgameFileName and endgameFilePath respectfully when all of those fields are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('[fileName]in[filePath]an unknown process');
+ });
+
+ ['file_create_event', 'created', 'file_delete_event', 'deleted'].forEach(eventAction => {
+ test(`it renders the text "via" when eventAction is ${eventAction}`, () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text().includes('via')).toBe(true);
+ });
+ });
+
+ test('it does NOT render the text "via" when eventAction is not a whitelisted action', () => {
+ const eventAction = 'a_non_whitelisted_event_action';
+
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text().includes('via')).toBe(false);
+ });
+
+ test('it renders a ParentProcessDraggable when eventAction is NOT "process_stopped" and NOT "termination_event"', () => {
+ const eventAction = 'something_else';
+
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'an unknown processvia parent process[endgameParentProcessName](456)'
+ );
+ });
+
+ test('it does NOT render a ParentProcessDraggable when eventAction is "process_stopped"', () => {
+ const eventAction = 'process_stopped';
+
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('an unknown process');
+ });
+
+ test('it does NOT render a ParentProcessDraggable when eventAction is "termination_event"', () => {
+ const eventAction = 'termination_event';
+
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('an unknown process');
+ });
+
+ test('it returns renders the message when showMessage is true', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('an unknown process[message]');
+ });
+
+ test('it does NOT render the message when showMessage is false', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('an unknown process');
+ });
+
+ test('it renders a ProcessDraggableWithNonExistentProcess when endgamePid and endgameProcessName are provided, but processPid and processName are NOT provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('[endgameProcessName](789)');
+ });
+
+ test('it prefers to render processName and processPid over endgameProcessName and endgamePid respectfully when all of those fields are provided', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('[processName](123)');
+ });
});
});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
index 4d87543dd64f2..0e75e8de6f9b2 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
@@ -17,7 +17,7 @@ import { OverflowField } from '../../../../tables/helpers';
import * as i18n from './translations';
import { NetflowRenderer } from '../netflow';
import { UserHostWorkingDir } from '../user_host_working_dir';
-import { Details, showVia, TokensFlexItem } from '../helpers';
+import { Details, isProcessStoppedOrTerminationEvent, showVia, TokensFlexItem } from '../helpers';
import { ProcessDraggableWithNonExistentProcess } from '../process_draggable';
import { Args } from '../args';
import { AuthSsh } from './auth_ssh';
@@ -26,7 +26,7 @@ import { FileDraggable } from '../file_draggable';
import { Package } from './package';
import { Badge } from '../../../../page';
import { ParentProcessDraggable } from '../parent_process_draggable';
-import { ProcessHash } from '../process.hash';
+import { ProcessHash } from '../process_hash';
interface Props {
args: string[] | null | undefined;
@@ -144,14 +144,15 @@ export const SystemGenericFileLine = pure(
eventId={id}
text={i18n.WITH_EXIT_CODE}
/>
-
+ {!isProcessStoppedOrTerminationEvent(eventAction) && (
+
+ )}
{outcome != null && (
{i18n.WITH_RESULT}
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
index 595e8f0327db8..3f8a726ed44f4 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
@@ -12,12 +12,43 @@ import * as React from 'react';
import { BrowserFields } from '../../../../../containers/source';
import { mockBrowserFields } from '../../../../../containers/source/mock';
import { Ecs } from '../../../../../graphql/types';
-import { mockTimelineData, TestProviders } from '../../../../../mock';
+import {
+ mockDnsEvent,
+ mockFimFileCreatedEvent,
+ mockFimFileDeletedEvent,
+ mockSocketClosedEvent,
+ mockSocketOpenedEvent,
+ mockTimelineData,
+ TestProviders,
+} from '../../../../../mock';
+import {
+ mockEndgameAdminLogon,
+ mockEndgameCreationEvent,
+ mockEndgameDnsRequest,
+ mockEndgameExplicitUserLogon,
+ mockEndgameFileCreateEvent,
+ mockEndgameFileDeleteEvent,
+ mockEndgameIpv4ConnectionAcceptEvent,
+ mockEndgameIpv6ConnectionAcceptEvent,
+ mockEndgameIpv4DisconnectReceivedEvent,
+ mockEndgameIpv6DisconnectReceivedEvent,
+ mockEndgameTerminationEvent,
+ mockEndgameUserLogoff,
+ mockEndgameUserLogon,
+} from '../../../../../mock/mock_endgame_ecs_data';
import { RowRenderer } from '../row_renderer';
import {
+ createDnsRowRenderer,
+ createEndgameProcessRowRenderer,
+ createFimRowRenderer,
createGenericSystemRowRenderer,
createGenericFileRowRenderer,
+ createSecurityEventRowRenderer,
+ createSocketRowRenderer,
} from './generic_row_renderer';
+import * as i18n from './translations';
+
+jest.mock('../../../../../lib/settings/use_kibana_ui_setting');
describe('GenericRowRenderer', () => {
describe('#createGenericSystemRowRenderer', () => {
@@ -144,4 +175,794 @@ describe('GenericRowRenderer', () => {
);
});
});
+
+ describe('#createEndgameProcessRowRenderer', () => {
+ test('it renders an endgame process creation_event', () => {
+ const actionName = 'creation_event';
+ const text = i18n.PROCESS_STARTED;
+ const endgameCreationEvent = {
+ ...mockEndgameCreationEvent,
+ };
+
+ const endgameProcessCreationEventRowRenderer = createEndgameProcessRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameProcessCreationEventRowRenderer.isInstance(endgameCreationEvent) &&
+ endgameProcessCreationEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameCreationEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54started processMicrosoft.Photos.exe(441684)C:\\Program Files\\WindowsApps\\Microsoft.Windows.Photos_2018.18091.17210.0_x64__8wekyb3d8bbwe\\Microsoft.Photos.exe-ServerName:App.AppXzst44mncqdg84v7sv6p7yznqwssy6f7f.mcavia parent processsvchost.exe(8)d4c97ed46046893141652e2ec0056a698f6445109949d7fcabbce331146889ee12563599116157778a22600d2a163d8112aed84562d06d7235b37895b68de56687895743'
+ );
+ });
+
+ test('it renders an endgame process termination_event', () => {
+ const actionName = 'termination_event';
+ const text = i18n.TERMINATED_PROCESS;
+ const endgameTerminationEvent = {
+ ...mockEndgameTerminationEvent,
+ };
+
+ const endgameProcessTerminationEventRowRenderer = createEndgameProcessRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameProcessTerminationEventRowRenderer.isInstance(endgameTerminationEvent) &&
+ endgameProcessTerminationEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameTerminationEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54terminated processRuntimeBroker.exe(442384)with exit code087976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776797255e72d5ed5c058d4785950eba7abaa057653bd4401441a21bf1abce6404f4231db4d'
+ );
+ });
+
+ test('it does NOT render the event if the action name does not match', () => {
+ const actionName = 'does_not_match';
+ const text = i18n.PROCESS_STARTED;
+ const endgameCreationEvent = {
+ ...mockEndgameCreationEvent,
+ };
+
+ const endgameProcessCreationEventRowRenderer = createEndgameProcessRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameProcessCreationEventRowRenderer.isInstance(endgameCreationEvent) &&
+ endgameProcessCreationEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameCreationEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it does NOT render the event when the event category is NOT process', () => {
+ const actionName = 'creation_event';
+ const text = i18n.PROCESS_STARTED;
+ const endgameCreationEvent = {
+ ...mockEndgameCreationEvent,
+ event: {
+ ...mockEndgameCreationEvent.event,
+ category: ['something_else'],
+ },
+ };
+
+ const endgameProcessCreationEventRowRenderer = createEndgameProcessRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameProcessCreationEventRowRenderer.isInstance(endgameCreationEvent) &&
+ endgameProcessCreationEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameCreationEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it does NOT render the event when both the action name and event category do NOT match', () => {
+ const actionName = 'does_not_match';
+ const text = i18n.PROCESS_STARTED;
+ const endgameCreationEvent = {
+ ...mockEndgameCreationEvent,
+ event: {
+ ...mockEndgameCreationEvent.event,
+ category: ['something_else'],
+ },
+ };
+
+ const endgameProcessCreationEventRowRenderer = createEndgameProcessRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameProcessCreationEventRowRenderer.isInstance(endgameCreationEvent) &&
+ endgameProcessCreationEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameCreationEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+ });
+
+ describe('#createFimRowRenderer', () => {
+ test('it renders an endgame file_create_event', () => {
+ const actionName = 'file_create_event';
+ const text = i18n.CREATED_FILE;
+ const endgameFileCreateEvent = {
+ ...mockEndgameFileCreateEvent,
+ };
+
+ const endgameFileCreateEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameFileCreateEventRowRenderer.isInstance(endgameFileCreateEvent) &&
+ endgameFileCreateEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameFileCreateEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54created a fileinC:\\Users\\Arun\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\63d78c21-e593-4484-b7a9-db33cd522ddc.tmpviachrome.exe(11620)'
+ );
+ });
+
+ test('it renders an endgame file_delete_event', () => {
+ const actionName = 'file_delete_event';
+ const text = i18n.DELETED_FILE;
+ const endgameFileDeleteEvent = {
+ ...mockEndgameFileDeleteEvent,
+ };
+
+ const endgameFileDeleteEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameFileDeleteEventRowRenderer.isInstance(endgameFileDeleteEvent) &&
+ endgameFileDeleteEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameFileDeleteEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-v1s-d2118419deleted a filetmp000002f6inC:\\Windows\\TEMP\\tmp00000404\\tmp000002f6viaAmSvc.exe(1084)'
+ );
+ });
+
+ test('it renders a FIM (non-endgame) file created event', () => {
+ const actionName = 'created';
+ const text = i18n.CREATED_FILE;
+ const fimFileCreatedEvent = {
+ ...mockFimFileCreatedEvent,
+ };
+
+ const fileCreatedEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {fileCreatedEventRowRenderer.isInstance(fimFileCreatedEvent) &&
+ fileCreatedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: fimFileCreatedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children foohostcreated a filein/etc/subgidviaan unknown process'
+ );
+ });
+
+ test('it renders a FIM (non-endgame) file deleted event', () => {
+ const actionName = 'deleted';
+ const text = i18n.DELETED_FILE;
+ const fimFileDeletedEvent = {
+ ...mockFimFileDeletedEvent,
+ };
+
+ const fileDeletedEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {fileDeletedEventRowRenderer.isInstance(fimFileDeletedEvent) &&
+ fileDeletedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: fimFileDeletedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children foohostdeleted a filein/etc/gshadow.lockviaan unknown process'
+ );
+ });
+
+ test('it does NOT render an event if the action name does not match', () => {
+ const actionName = 'does_not_match';
+ const text = i18n.CREATED_FILE;
+ const endgameFileCreateEvent = {
+ ...mockEndgameFileCreateEvent,
+ };
+
+ const endgameFileCreateEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameFileCreateEventRowRenderer.isInstance(endgameFileCreateEvent) &&
+ endgameFileCreateEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameFileCreateEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it does NOT render an Endgame file_create_event when category is NOT file', () => {
+ const actionName = 'file_create_event';
+ const text = i18n.CREATED_FILE;
+ const endgameFileCreateEvent = {
+ ...mockEndgameFileCreateEvent,
+ event: {
+ ...mockEndgameFileCreateEvent.event,
+ category: ['something_else'],
+ },
+ };
+
+ const endgameFileCreateEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameFileCreateEventRowRenderer.isInstance(endgameFileCreateEvent) &&
+ endgameFileCreateEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: endgameFileCreateEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it does NOT render a FIM (non-Endgame) file created event when the event dataset is NOT file', () => {
+ const actionName = 'created';
+ const text = i18n.CREATED_FILE;
+ const fimFileCreatedEvent = {
+ ...mockFimFileCreatedEvent,
+ event: {
+ ...mockEndgameFileCreateEvent.event,
+ dataset: ['something_else'],
+ },
+ };
+
+ const fileCreatedEventRowRenderer = createFimRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {fileCreatedEventRowRenderer.isInstance(fimFileCreatedEvent) &&
+ fileCreatedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: fimFileCreatedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+ });
+
+ describe('#createSocketRowRenderer', () => {
+ test('it renders an Endgame ipv4_connection_accept_event', () => {
+ const actionName = 'ipv4_connection_accept_event';
+ const text = i18n.ACCEPTED_A_CONNECTION_VIA;
+ const ipv4ConnectionAcceptEvent = {
+ ...mockEndgameIpv4ConnectionAcceptEvent,
+ };
+
+ const endgameIpv4ConnectionAcceptEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameIpv4ConnectionAcceptEventRowRenderer.isInstance(ipv4ConnectionAcceptEvent) &&
+ endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: ipv4ConnectionAcceptEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-gqf-0af7b4feaccepted a connection viaAmSvc.exe(1084)tcp1:network-community_idSource127.0.0.1:49306Destination127.0.0.1:49305'
+ );
+ });
+
+ test('it renders an Endgame ipv6_connection_accept_event', () => {
+ const actionName = 'ipv6_connection_accept_event';
+ const text = i18n.ACCEPTED_A_CONNECTION_VIA;
+ const ipv6ConnectionAcceptEvent = {
+ ...mockEndgameIpv6ConnectionAcceptEvent,
+ };
+
+ const endgameIpv6ConnectionAcceptEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameIpv6ConnectionAcceptEventRowRenderer.isInstance(ipv6ConnectionAcceptEvent) &&
+ endgameIpv6ConnectionAcceptEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: ipv6ConnectionAcceptEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66accepted a connection via(4)tcp1:network-community_idSource::1:51324Destination::1:5357'
+ );
+ });
+
+ test('it renders an Endgame ipv4_disconnect_received_event', () => {
+ const actionName = 'ipv4_disconnect_received_event';
+ const text = i18n.DISCONNECTED_VIA;
+ const ipv4DisconnectReceivedEvent = {
+ ...mockEndgameIpv4DisconnectReceivedEvent,
+ };
+
+ const endgameIpv4DisconnectReceivedEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameIpv4DisconnectReceivedEventRowRenderer.isInstance(ipv4DisconnectReceivedEvent) &&
+ endgameIpv4DisconnectReceivedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: ipv4DisconnectReceivedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54disconnected viachrome.exe(11620)8.1KBtcp1:LxYHJJv98b2O0fNccXu6HheXmwk=Source192.168.0.6:59356(25.78%)2.1KB(74.22%)6KBDestination10.156.162.53:443'
+ );
+ });
+
+ test('it renders an Endgame ipv6_disconnect_received_event', () => {
+ const actionName = 'ipv6_disconnect_received_event';
+ const text = i18n.DISCONNECTED_VIA;
+ const ipv6DisconnectReceivedEvent = {
+ ...mockEndgameIpv6DisconnectReceivedEvent,
+ };
+
+ const endgameIpv6DisconnectReceivedEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameIpv6DisconnectReceivedEventRowRenderer.isInstance(ipv6DisconnectReceivedEvent) &&
+ endgameIpv6DisconnectReceivedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: ipv6DisconnectReceivedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66disconnected via(4)7.9KBtcp1:ZylzQhsB1dcptA2t4DY8S6l9o8E=Source::1:51338(96.92%)7.7KB(3.08%)249BDestination::1:2869'
+ );
+ });
+
+ test('it renders a (non-Endgame) socket_opened event', () => {
+ const actionName = 'socket_opened';
+ const text = i18n.SOCKET_OPENED;
+ const socketOpenedEvent = {
+ ...mockSocketOpenedEvent,
+ };
+
+ const socketOpenedEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {socketOpenedEventRowRenderer.isInstance(socketOpenedEvent) &&
+ socketOpenedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: socketOpenedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children root@foohostopened a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59554 -> 10.1.2.3:80) Ooutboundtcp1:network-community_idSource10.4.20.1:59554Destination10.1.2.3:80'
+ );
+ });
+
+ test('it renders a (non-Endgame) socket_closed event', () => {
+ const actionName = 'socket_closed';
+ const text = i18n.SOCKET_CLOSED;
+ const socketClosedEvent = {
+ ...mockSocketClosedEvent,
+ };
+
+ const socketClosedEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {socketClosedEventRowRenderer.isInstance(socketClosedEvent) &&
+ socketClosedEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: socketClosedEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children root@foohostclosed a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59508 -> 10.1.2.3:80) Coutboundtcp1:network-community_idSource10.4.20.1:59508Destination10.1.2.3:80'
+ );
+ });
+
+ test('it does NOT render an event if the action name does not match', () => {
+ const actionName = 'does_not_match';
+ const text = i18n.ACCEPTED_A_CONNECTION_VIA;
+ const ipv4ConnectionAcceptEvent = {
+ ...mockEndgameIpv4ConnectionAcceptEvent,
+ };
+
+ const endgameIpv4ConnectionAcceptEventRowRenderer = createSocketRowRenderer({
+ actionName,
+ text,
+ });
+
+ const wrapper = mount(
+
+ {endgameIpv4ConnectionAcceptEventRowRenderer.isInstance(ipv4ConnectionAcceptEvent) &&
+ endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: ipv4ConnectionAcceptEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+ });
+
+ describe('#createSecurityEventRowRenderer', () => {
+ test('it renders an Endgame user_logon event', () => {
+ const actionName = 'user_logon';
+ const userLogonEvent = {
+ ...mockEndgameUserLogon,
+ };
+
+ const userLogonEventRowRenderer = createSecurityEventRowRenderer({ actionName });
+
+ const wrapper = mount(
+
+ {userLogonEventRowRenderer.isInstance(userLogonEvent) &&
+ userLogonEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: userLogonEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-v1s-d2118419successfully logged inusing logon type5 - Service(target logon ID0x3e7)viaC:\\Windows\\System32\\services.exe(432)as requested by subjectWIN-Q3DOP1UKA81$(subject logon ID0x3e7)4624'
+ );
+ });
+
+ test('it renders an Endgame admin_logon event', () => {
+ const actionName = 'admin_logon';
+ const adminLogonEvent = {
+ ...mockEndgameAdminLogon,
+ };
+
+ const adminLogonEventRowRenderer = createSecurityEventRowRenderer({ actionName });
+
+ const wrapper = mount(
+
+ {adminLogonEventRowRenderer.isInstance(adminLogonEvent) &&
+ adminLogonEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: adminLogonEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children With special privileges,SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54successfully logged inviaC:\\Windows\\System32\\lsass.exe(964)as requested by subjectSYSTEM\\NT AUTHORITY4672'
+ );
+ });
+
+ test('it renders an Endgame explicit_user_logon event', () => {
+ const actionName = 'explicit_user_logon';
+ const explicitUserLogonEvent = {
+ ...mockEndgameExplicitUserLogon,
+ };
+
+ const explicitUserLogonEventRowRenderer = createSecurityEventRowRenderer({ actionName });
+
+ const wrapper = mount(
+
+ {explicitUserLogonEventRowRenderer.isInstance(explicitUserLogonEvent) &&
+ explicitUserLogonEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: explicitUserLogonEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children A login was attempted using explicit credentialsArun\\Anvi-AcertoHD-55b-3ec87f66viaC:\\Windows\\System32\\svchost.exe(1736)as requested by subjectANVI-ACER$\\WORKGROUP(subject logon ID0x3e7)4648'
+ );
+ });
+
+ test('it renders an Endgame user_logoff event', () => {
+ const actionName = 'user_logoff';
+ const userLogoffEvent = {
+ ...mockEndgameUserLogoff,
+ };
+
+ const userLogoffEventRowRenderer = createSecurityEventRowRenderer({ actionName });
+
+ const wrapper = mount(
+
+ {userLogoffEventRowRenderer.isInstance(userLogoffEvent) &&
+ userLogoffEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: userLogoffEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children Arun\\Anvi-Acer@HD-55b-3ec87f66logged offusing logon type2 - Interactive(target logon ID0x16db41e)viaC:\\Windows\\System32\\lsass.exe(964)4634'
+ );
+ });
+
+ test('it does NOT render an event if the action name does not match', () => {
+ const actionName = 'does_not_match';
+ const userLogonEvent = {
+ ...mockEndgameUserLogon,
+ };
+
+ const userLogonEventRowRenderer = createSecurityEventRowRenderer({ actionName });
+
+ const wrapper = mount(
+
+ {userLogonEventRowRenderer.isInstance(userLogonEvent) &&
+ userLogonEventRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: userLogonEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+ });
+
+ describe('#createDnsRowRenderer', () => {
+ test('it renders an Endgame DNS request_event', () => {
+ const requestEvent = {
+ ...mockEndgameDnsRequest,
+ };
+
+ const dnsRowRenderer = createDnsRowRenderer();
+
+ const wrapper = mount(
+
+ {dnsRowRenderer.isInstance(requestEvent) &&
+ dnsRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: requestEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54asked forupdate.googleapis.comwith question typeA, which resolved to10.100.197.67viaGoogleUpdate.exe(443192)3008dns'
+ );
+ });
+
+ test('it renders a non-Endgame DNS event', () => {
+ const dnsEvent = {
+ ...mockDnsEvent,
+ };
+
+ const dnsRowRenderer = createDnsRowRenderer();
+
+ const wrapper = mount(
+
+ {dnsRowRenderer.isInstance(dnsEvent) &&
+ dnsRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: dnsEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual(
+ 'some children iot.example.comasked forlookup.example.comwith question typeA, which resolved to10.1.2.3(response code:NOERROR)viaan unknown process6.937500msOct 8, 2019 @ 10:05:23.241Oct 8, 2019 @ 10:05:23.248outbounddns177Budp1:network-community_idSource10.9.9.9:58732(22.60%)40B(77.40%)137BDestination10.1.1.1:53OceaniaAustralia🇦🇺AU'
+ );
+ });
+
+ test('it does NOT render an event if dns.question.type is not provided', () => {
+ const requestEvent = {
+ ...mockEndgameDnsRequest,
+ dns: {
+ ...mockDnsEvent.dns,
+ question: {
+ name: ['lookup.example.com'],
+ },
+ },
+ };
+
+ const dnsRowRenderer = createDnsRowRenderer();
+
+ const wrapper = mount(
+
+ {dnsRowRenderer.isInstance(requestEvent) &&
+ dnsRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: requestEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+
+ test('it does NOT render an event if dns.question.name is not provided', () => {
+ const requestEvent = {
+ ...mockEndgameDnsRequest,
+ dns: {
+ ...mockDnsEvent.dns,
+ question: {
+ type: ['A'],
+ },
+ },
+ };
+
+ const dnsRowRenderer = createDnsRowRenderer();
+
+ const wrapper = mount(
+
+ {dnsRowRenderer.isInstance(requestEvent) &&
+ dnsRowRenderer.renderRow({
+ browserFields: mockBrowserFields,
+ data: requestEvent,
+ children: {'some children '},
+ timelineId: 'test',
+ })}
+
+ );
+
+ expect(wrapper.text()).toEqual('');
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx
index 9000e23ee0d3a..f8930fdca7ba2 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx
@@ -9,7 +9,7 @@ import React from 'react';
import { DnsRequestEventDetails } from '../dns/dns_request_event_details';
import { EndgameSecurityEventDetails } from '../endgame/endgame_security_event_details';
-import { isFimEvent, isNillEmptyOrNotFinite } from '../helpers';
+import { isFileEvent, isNillEmptyOrNotFinite } from '../helpers';
import { RowRenderer, RowRendererContainer } from '../row_renderer';
import { SystemGenericDetails } from './generic_details';
@@ -95,7 +95,7 @@ export const createFimRowRenderer = ({
const category: string | null | undefined = get('event.category[0]', ecs);
const dataset: string | null | undefined = get('event.dataset[0]', ecs);
return (
- isFimEvent({ eventCategory: category, eventDataset: dataset }) &&
+ isFileEvent({ eventCategory: category, eventDataset: dataset }) &&
action != null &&
action.toLowerCase() === actionName
);
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx
index cf03213fc34f3..2ea8c6931f00b 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx
@@ -185,5 +185,126 @@ describe('UserHostWorkingDir', () => {
);
expect(wrapper.text()).toEqual('[user-name-123]\\[user-domain-123]@[host-name-123]');
});
+
+ test('it returns hostName and userName with the default hostNameSeparator "@", when hostNameSeparator is NOT specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('[user-name-123]@[host-name-123]');
+ });
+
+ test('it returns hostName and userName with an overridden hostNameSeparator, when hostNameSeparator is specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.text()).toEqual('[user-name-123]custom separator[host-name-123]');
+ });
+
+ test('it renders a draggable `user.domain` field (by default) when userDomain is provided, and userDomainField is NOT specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-content-user.domain"]').exists()).toBe(true);
+ });
+
+ test('it renders a draggable with an overridden field name when userDomain is provided, and userDomainField is also specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="draggable-content-overridden.field.name"]').exists()
+ ).toBe(true);
+ });
+
+ test('it renders a draggable `user.name` field (by default) when userName is provided, and userNameField is NOT specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-content-user.name"]').exists()).toBe(true);
+ });
+
+ test('it renders a draggable with an overridden field name when userName is provided, and userNameField is also specified as a prop', () => {
+ const wrapper = mountWithIntl(
+
+
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="draggable-content-overridden.field.name"]').exists()
+ ).toBe(true);
+ });
});
});
diff --git a/x-pack/legacy/plugins/siem/public/mock/mock_endgame_ecs_data.ts b/x-pack/legacy/plugins/siem/public/mock/mock_endgame_ecs_data.ts
index 6f58373b508ac..e6eee3d1c1cb1 100644
--- a/x-pack/legacy/plugins/siem/public/mock/mock_endgame_ecs_data.ts
+++ b/x-pack/legacy/plugins/siem/public/mock/mock_endgame_ecs_data.ts
@@ -147,7 +147,7 @@ export const mockEndgameIpv4ConnectionAcceptEvent: Ecs = {
},
timestamp: '1569555676000',
network: {
- community_id: ['1:PFLzPy11bhJJZCUQhIVPEBoDLG4='],
+ community_id: ['1:network-community_id'],
transport: ['tcp'],
},
process: {
@@ -193,7 +193,7 @@ export const mockEndgameIpv6ConnectionAcceptEvent: Ecs = {
},
timestamp: '1569553566000',
network: {
- community_id: ['1:bUcbuDIApRZPr7rMsEG5Ngk/ids='],
+ community_id: ['1:network-community_id'],
transport: ['tcp'],
},
process: {
@@ -253,7 +253,7 @@ export const mockEndgameIpv4DisconnectReceivedEvent: Ecs = {
},
destination: {
port: [443],
- ip: ['54.156.162.53'],
+ ip: ['10.156.162.53'],
bytes: [6193],
},
endgame: {
@@ -563,8 +563,8 @@ export const mockEndgameTerminationEvent: Ecs = {
process: {
hash: {
md5: ['bd4401441a21bf1abce6404f4231db4d'],
- sha1: ['797255e72d5ed5c058d4785950eba7abaa057653]'],
- sha256: ['87976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776]'],
+ sha1: ['797255e72d5ed5c058d4785950eba7abaa057653'],
+ sha256: ['87976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776'],
},
pid: [442384],
ppid: [8],
diff --git a/x-pack/legacy/plugins/siem/public/mock/mock_timeline_data.ts b/x-pack/legacy/plugins/siem/public/mock/mock_timeline_data.ts
index 997e3d2704cec..b300053d5f227 100644
--- a/x-pack/legacy/plugins/siem/public/mock/mock_timeline_data.ts
+++ b/x-pack/legacy/plugins/siem/public/mock/mock_timeline_data.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { TimelineItem } from '../graphql/types';
+import { Ecs, TimelineItem } from '../graphql/types';
export const mockTimelineData: TimelineItem[] = [
{
@@ -1394,3 +1394,216 @@ export const mockTimelineData: TimelineItem[] = [
},
},
];
+
+export const mockFimFileCreatedEvent: Ecs = {
+ _id: 'WuBP4W0BOpWiDweSoYSg',
+ timestamp: '2019-10-18T23:59:15.091Z',
+ host: {
+ architecture: ['x86_64'],
+ os: {
+ family: ['debian'],
+ name: ['Ubuntu'],
+ kernel: ['4.15.0-1046-gcp'],
+ platform: ['ubuntu'],
+ version: ['16.04.6 LTS (Xenial Xerus)'],
+ },
+ id: ['host-id-123'],
+ name: ['foohost'],
+ },
+ file: {
+ path: ['/etc/subgid'],
+ size: [4445],
+ owner: ['root'],
+ inode: ['90027'],
+ ctime: ['2019-10-18T23:59:14.872Z'],
+ gid: ['0'],
+ type: ['file'],
+ mode: ['0644'],
+ mtime: ['2019-10-18T23:59:14.872Z'],
+ uid: ['0'],
+ group: ['root'],
+ },
+ event: {
+ module: ['file_integrity'],
+ dataset: ['file'],
+ action: ['created'],
+ },
+};
+
+export const mockFimFileDeletedEvent: Ecs = {
+ _id: 'M-BP4W0BOpWiDweSo4cm',
+ timestamp: '2019-10-18T23:59:16.247Z',
+ host: {
+ name: ['foohost'],
+ os: {
+ platform: ['ubuntu'],
+ version: ['16.04.6 LTS (Xenial Xerus)'],
+ family: ['debian'],
+ name: ['Ubuntu'],
+ kernel: ['4.15.0-1046-gcp'],
+ },
+ id: ['host-id-123'],
+ architecture: ['x86_64'],
+ },
+ event: {
+ module: ['file_integrity'],
+ dataset: ['file'],
+ action: ['deleted'],
+ },
+ file: {
+ path: ['/etc/gshadow.lock'],
+ },
+};
+
+export const mockSocketOpenedEvent: Ecs = {
+ _id: 'Vusu4m0BOpWiDweSLkXY',
+ timestamp: '2019-10-19T04:02:19.473Z',
+ network: {
+ direction: ['outbound'],
+ transport: ['tcp'],
+ community_id: ['1:network-community_id'],
+ },
+ host: {
+ name: ['foohost'],
+ architecture: ['x86_64'],
+ os: {
+ platform: ['centos'],
+ version: ['7 (Core)'],
+ family: ['redhat'],
+ name: ['CentOS Linux'],
+ kernel: ['3.10.0-1062.1.2.el7.x86_64'],
+ },
+ id: ['host-id-123'],
+ },
+ process: {
+ pid: [2166],
+ name: ['google_accounts'],
+ },
+ destination: {
+ ip: ['10.1.2.3'],
+ port: [80],
+ },
+ user: {
+ name: ['root'],
+ },
+ source: {
+ port: [59554],
+ ip: ['10.4.20.1'],
+ },
+ event: {
+ action: ['socket_opened'],
+ module: ['system'],
+ dataset: ['socket'],
+ kind: ['event'],
+ },
+ message: [
+ 'Outbound socket (10.4.20.1:59554 -> 10.1.2.3:80) OPENED by process google_accounts (PID: 2166) and user root (UID: 0)',
+ ],
+};
+
+export const mockSocketClosedEvent: Ecs = {
+ _id: 'V-su4m0BOpWiDweSLkXY',
+ timestamp: '2019-10-19T04:02:19.473Z',
+ process: {
+ pid: [2166],
+ name: ['google_accounts'],
+ },
+ user: {
+ name: ['root'],
+ },
+ source: {
+ port: [59508],
+ ip: ['10.4.20.1'],
+ },
+ event: {
+ dataset: ['socket'],
+ kind: ['event'],
+ action: ['socket_closed'],
+ module: ['system'],
+ },
+ message: [
+ 'Outbound socket (10.4.20.1:59508 -> 10.1.2.3:80) CLOSED by process google_accounts (PID: 2166) and user root (UID: 0)',
+ ],
+ network: {
+ community_id: ['1:network-community_id'],
+ direction: ['outbound'],
+ transport: ['tcp'],
+ },
+ destination: {
+ ip: ['10.1.2.3'],
+ port: [80],
+ },
+ host: {
+ name: ['foohost'],
+ architecture: ['x86_64'],
+ os: {
+ version: ['7 (Core)'],
+ family: ['redhat'],
+ name: ['CentOS Linux'],
+ kernel: ['3.10.0-1062.1.2.el7.x86_64'],
+ platform: ['centos'],
+ },
+ id: ['host-id-123'],
+ },
+};
+
+export const mockDnsEvent: Ecs = {
+ _id: 'VUTUqm0BgJt5sZM7nd5g',
+ destination: {
+ domain: ['ten.one.one.one'],
+ port: [53],
+ bytes: [137],
+ ip: ['10.1.1.1'],
+ geo: {
+ continent_name: ['Oceania'],
+ location: {
+ lat: [-33.494],
+ lon: [143.2104],
+ },
+ country_iso_code: ['AU'],
+ country_name: ['Australia'],
+ city_name: [''],
+ },
+ },
+ host: {
+ architecture: ['armv7l'],
+ id: ['host-id'],
+ os: {
+ family: ['debian'],
+ platform: ['raspbian'],
+ version: ['9 (stretch)'],
+ name: ['Raspbian GNU/Linux'],
+ kernel: ['4.19.57-v7+'],
+ },
+ name: ['iot.example.com'],
+ },
+ dns: {
+ question: {
+ name: ['lookup.example.com'],
+ type: ['A'],
+ },
+ response_code: ['NOERROR'],
+ resolved_ip: ['10.1.2.3'],
+ },
+ timestamp: '2019-10-08T10:05:23.241Z',
+ network: {
+ community_id: ['1:network-community_id'],
+ direction: ['outbound'],
+ bytes: [177],
+ transport: ['udp'],
+ protocol: ['dns'],
+ },
+ event: {
+ duration: [6937500],
+ category: ['network_traffic'],
+ dataset: ['dns'],
+ kind: ['event'],
+ end: ['2019-10-08T10:05:23.248Z'],
+ start: ['2019-10-08T10:05:23.241Z'],
+ },
+ source: {
+ port: [58732],
+ bytes: [40],
+ ip: ['10.9.9.9'],
+ },
+};