From 751a077483632bc71b7cececf4749619f36275f2 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Sun, 14 Nov 2021 23:52:05 -0500 Subject: [PATCH 01/13] Add session view detail panel --- .../public/components/ProcessTree/styles.ts | 3 - .../public/components/SessionView/index.tsx | 103 +++++++++++++++--- .../public/components/SessionView/styles.ts | 41 +++++++ 3 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/session_view/public/components/SessionView/styles.ts diff --git a/x-pack/plugins/session_view/public/components/ProcessTree/styles.ts b/x-pack/plugins/session_view/public/components/ProcessTree/styles.ts index 8304ed455ea371..cf98e161077d9d 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTree/styles.ts +++ b/x-pack/plugins/session_view/public/components/ProcessTree/styles.ts @@ -13,15 +13,12 @@ export const useStyles = () => { const cached = useMemo(() => { const defaultSelectionColor = euiTheme.colors.accent; - const padding = euiTheme.size.s; const scroller = ` font-family: ${euiTheme.font.familyCode}; overflow: auto; height: 100%; background-color: ${euiTheme.colors.lightestShade}; - padding-top: ${padding}; - padding-left: ${padding}; display: flex; flex-direction: column; `; diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index c5619b0af36a6b..1b6f365482fbc1 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -8,21 +8,25 @@ import React, { useState, useEffect } from 'react'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; import { useQuery, useMutation } from 'react-query'; -import { - EuiSearchBar, - EuiSearchBarOnChangeArgs, - EuiButton, - EuiPage, +import { + EuiSearchBar, + EuiSearchBarOnChangeArgs, + EuiButton, + EuiDescriptionList, + EuiPage, EuiPageContent, EuiPageHeader, EuiSpacer, + EuiSplitPanel, EuiFlexGroup, EuiFlexItem, - EuiEmptyPrompt + EuiEmptyPrompt, + EuiTitle, } from '@elastic/eui'; import { ProcessTree } from '../ProcessTree'; import { getStart, getEnd, getEvent } from '../../../common/test/mock_data'; -import { Process } from '../../hooks/use_process_tree'; +import { Process, ProcessEvent } from '../../hooks/use_process_tree'; +import { useStyles } from './styles'; import { INTERNAL_TEST_ROUTE, @@ -50,7 +54,9 @@ interface MockESReturnData { export const SessionView = ({ sessionId }: SessionViewDeps) => { const [searchQuery, setSearchQuery] = useState(''); const [data, setData] = useState([]); + const [isDetailOpen, setIsDetailOpen] = useState(false); const [selectedProcess, setSelectedProcess] = useState(null); + const styles = useStyles(); const { http } = useKibana().services; @@ -115,6 +121,7 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { deleteMutate(undefined, { onSuccess: () => { setData([]); + setSelectedProcess(null); } }) } @@ -145,7 +152,12 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { } /> ) - } + }; + + const toggleDetailPanel = () => { + console.log('jack', data) + setIsDetailOpen(!isDetailOpen); + }; const renderInsertButtons = () => { return ( @@ -190,16 +202,71 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { {renderNoData()} {!!data.length && <> - -
- -
+ + + + + + + Detail Panel + + + + + +
+ +
+
+ {isDetailOpen && ( + + {selectedProcess && ( + <> + Command Detail + + {selectedProcess.events.map((event) => { + return ( ({ + title, + description: JSON.stringify(event[title as keyof ProcessEvent], null, 4), + }))} + />) + })} + + + )} + + Session Detail + + ({ + title, + description: JSON.stringify(data?.[0].process.session[title], null, 4), + }))} + /> + + + + Server Detail + {/* Add server detail */} + + + + Alert Detail + {/* Add alert detail conditionally */} + + )} +
} diff --git a/x-pack/plugins/session_view/public/components/SessionView/styles.ts b/x-pack/plugins/session_view/public/components/SessionView/styles.ts new file mode 100644 index 00000000000000..d65ca0f465da97 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/SessionView/styles.ts @@ -0,0 +1,41 @@ +/* + * 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 { useMemo } from 'react'; +import { useEuiTheme } from '@elastic/eui'; +import { keyframes, css } from '@emotion/react'; + +export const useStyles = () => { + const { euiTheme } = useEuiTheme(); + + const cached = useMemo(() => { + const padding = euiTheme.size.s; + + const outerPanel = ` + font-family: ${euiTheme.font.familyCode}; + position: relative; + `; + + const treePanel = ` + padding: ${padding} 0 0 ${padding}; + `; + + const detailPanel = ` + max-width: 424px; + height: 300px; + overflow-y: auto; + `; + + return { + outerPanel, + treePanel, + detailPanel, + }; + }, [euiTheme]); + + return cached; +}; From 301e9885b5a51d732aad1e54b360afa21fb1d03b Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Mon, 15 Nov 2021 11:20:02 -0500 Subject: [PATCH 02/13] Remove unused dependencies --- .../plugins/session_view/public/components/SessionView/styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/session_view/public/components/SessionView/styles.ts b/x-pack/plugins/session_view/public/components/SessionView/styles.ts index d65ca0f465da97..a1ab283e563f05 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/styles.ts +++ b/x-pack/plugins/session_view/public/components/SessionView/styles.ts @@ -7,7 +7,6 @@ import { useMemo } from 'react'; import { useEuiTheme } from '@elastic/eui'; -import { keyframes, css } from '@emotion/react'; export const useStyles = () => { const { euiTheme } = useEuiTheme(); From 084b5a38f0e0c08fcf70b225531d5b39b18bf0ed Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Tue, 16 Nov 2021 16:24:10 -0500 Subject: [PATCH 03/13] Add animation to detail panel slide in and out --- examples/hello_world/public/plugin.tsx | 3 +- .../public/components/SessionView/index.tsx | 34 ++++++---- .../public/components/SessionView/styles.ts | 64 +++++++++++++++---- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/examples/hello_world/public/plugin.tsx b/examples/hello_world/public/plugin.tsx index 49acf07e6ae12f..d47e91b2429d20 100755 --- a/examples/hello_world/public/plugin.tsx +++ b/examples/hello_world/public/plugin.tsx @@ -9,6 +9,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import { Comp } from './Comp'; interface SetupDeps { developerExamples: DeveloperExamplesSetup; @@ -21,7 +22,7 @@ export class HelloWorldPlugin implements Plugin { id: 'helloWorld', title: 'Hello World', async mount({ element }: AppMountParameters) { - ReactDOM.render(
Hello World!
, element); + ReactDOM.render(
, element); return () => ReactDOM.unmountComponentAtNode(element); }, }); diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index 1b6f365482fbc1..3ef4ec38564f0b 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -55,6 +55,7 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { const [searchQuery, setSearchQuery] = useState(''); const [data, setData] = useState([]); const [isDetailOpen, setIsDetailOpen] = useState(false); + const [isDetailMounted, setIsDetailMounted] = useState(false); const [selectedProcess, setSelectedProcess] = useState(null); const styles = useStyles(); @@ -155,8 +156,8 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { }; const toggleDetailPanel = () => { - console.log('jack', data) - setIsDetailOpen(!isDetailOpen); + setIsDetailMounted(!isDetailMounted); + if (!isDetailOpen) setIsDetailOpen(true); }; const renderInsertButtons = () => { @@ -189,7 +190,7 @@ export const SessionView = ({ sessionId }: SessionViewDeps) => { return ( - { -
- -
+
+ +
{isDetailOpen && ( - + { + if (!isDetailMounted) setIsDetailOpen(false); + }} + > {selectedProcess && ( <> Command Detail diff --git a/x-pack/plugins/session_view/public/components/SessionView/styles.ts b/x-pack/plugins/session_view/public/components/SessionView/styles.ts index a1ab283e563f05..ae5a216fdb3d68 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/styles.ts +++ b/x-pack/plugins/session_view/public/components/SessionView/styles.ts @@ -7,6 +7,7 @@ import { useMemo } from 'react'; import { useEuiTheme } from '@elastic/eui'; +import { keyframes, CSSObject } from '@emotion/react'; export const useStyles = () => { const { euiTheme } = useEuiTheme(); @@ -14,25 +15,62 @@ export const useStyles = () => { const cached = useMemo(() => { const padding = euiTheme.size.s; - const outerPanel = ` - font-family: ${euiTheme.font.familyCode}; - position: relative; - `; + const outerPanel: CSSObject = { + fontFamily: euiTheme.font.familyCode, + position: 'relative', + overflowX: 'hidden', + }; + + const treePanel: CSSObject = { + paddingTop: padding, + paddingLeft: padding, + }; - const treePanel = ` - padding: ${padding} 0 0 ${padding}; - `; + const slideIn = keyframes({ + to: { + right: '0', + } + }); + + const slideOut = keyframes({ + from: { + right: '0', + }, + to: { + right: '-100%', + } + }); + + const detailPanel: CSSObject = { + width: '424px', + height: '300px', + overflowY: 'auto', + position: 'absolute', + top: '8px', + right: '-100%', + }; - const detailPanel = ` - max-width: 424px; - height: 300px; - overflow-y: auto; - `; + const detailPanelIn: Array = [ + slideIn.styles, + { + ...detailPanel, + animation: `${slideIn.name} 200ms ease forwards`, + }, + ]; + + const detailPanelOut: Array = [ + slideOut.styles, + { + ...detailPanel, + animation: `${slideOut.name} 150ms ease`, + }, + ]; return { outerPanel, treePanel, - detailPanel, + detailPanelIn, + detailPanelOut, }; }, [euiTheme]); From 3aa81cbbfdb220ff0bd6e2bfb8eb54fa28f5b581 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Wed, 17 Nov 2021 15:59:22 -0300 Subject: [PATCH 04/13] reverting changes --- examples/hello_world/public/plugin.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/hello_world/public/plugin.tsx b/examples/hello_world/public/plugin.tsx index d47e91b2429d20..49acf07e6ae12f 100755 --- a/examples/hello_world/public/plugin.tsx +++ b/examples/hello_world/public/plugin.tsx @@ -9,7 +9,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; import { DeveloperExamplesSetup } from '../../developer_examples/public'; -import { Comp } from './Comp'; interface SetupDeps { developerExamples: DeveloperExamplesSetup; @@ -22,7 +21,7 @@ export class HelloWorldPlugin implements Plugin { id: 'helloWorld', title: 'Hello World', async mount({ element }: AppMountParameters) { - ReactDOM.render(
, element); + ReactDOM.render(
Hello World!
, element); return () => ReactDOM.unmountComponentAtNode(element); }, }); From d9aa8bb603e6357ecc0e201176cd32c84ea74537 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Wed, 17 Nov 2021 17:25:05 -0500 Subject: [PATCH 05/13] Update detail panel content using flattened json and MonacoEditor to display data --- .../session_view/common/test/mock_data.ts | 264 +++++++++--------- .../session_view/common/utils/flatten_json.ts | 29 ++ .../public/components/SessionView/index.tsx | 188 ++++++++++--- .../public/components/SessionView/styles.ts | 12 +- .../public/hooks/use_process_tree.ts | 10 +- .../server/routes/recent_session_route.ts | 2 +- .../session_view/server/routes/test_route.ts | 14 +- 7 files changed, 328 insertions(+), 191 deletions(-) create mode 100644 x-pack/plugins/session_view/common/utils/flatten_json.ts diff --git a/x-pack/plugins/session_view/common/test/mock_data.ts b/x-pack/plugins/session_view/common/test/mock_data.ts index 3a1b57c9cf7648..8426aa26023122 100644 --- a/x-pack/plugins/session_view/common/test/mock_data.ts +++ b/x-pack/plugins/session_view/common/test/mock_data.ts @@ -5,51 +5,19 @@ * 2.0. */ // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EventAction, ProcessEvent } from '../../public/hooks/use_process_tree'; import uuid from 'uuid'; +import { EventAction, ProcessEvent } from '../../public/hooks/use_process_tree'; export const getStart = () => { - return [{ - '@timestamp': 'Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)', - event: { - kind: 'event', - category: 'process', - action: EventAction.exec, - }, - process: { - args: ['bash'], - args_count: 1, - command_line: 'bash', - entity_id: '4321', - executable: '/bin/bash', - name: 'bash', - interactive: true, - working_directory: '/home/kg', - pid: 3, - pgid: 2, - - user: { - id: '2', - name: 'kg', - }, - - parent: { - args: ['/bin/sshd'], - args_count: 1, - command_line: 'sshd', - entity_id: '4322', - executable: '/bin/sshd', - name: 'sshd', - interactive: true, - working_directory: '/', - pid: 2, - pgid: 2, - user: { - id: '0', - name: 'root', - }, + return [ + { + '@timestamp': 'Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)', + event: { + kind: 'event', + category: 'process', + action: EventAction.exec, }, - session: { + process: { args: ['bash'], args_count: 1, command_line: 'bash', @@ -60,29 +28,63 @@ export const getStart = () => { working_directory: '/home/kg', pid: 3, pgid: 2, + user: { id: '2', name: 'kg', }, - }, - entry: { - args: ['bash'], - args_count: 1, - command_line: 'bash', - entity_id: '4321', - executable: '/bin/bash', - name: 'bash', - interactive: true, - working_directory: '/home/kg', - pid: 3, - pgid: 2, - user: { - id: '2', - name: 'kg', + + parent: { + args: ['/bin/sshd'], + args_count: 1, + command_line: 'sshd', + entity_id: '4322', + executable: '/bin/sshd', + name: 'sshd', + interactive: true, + working_directory: '/', + pid: 2, + pgid: 2, + user: { + id: '0', + name: 'root', + }, + }, + session: { + args: ['bash'], + args_count: 1, + command_line: 'bash', + entity_id: '4321', + executable: '/bin/bash', + name: 'bash', + interactive: true, + working_directory: '/home/kg', + pid: 3, + pgid: 2, + user: { + id: '2', + name: 'kg', + }, + }, + entry: { + args: ['bash'], + args_count: 1, + command_line: 'bash', + entity_id: '4321', + executable: '/bin/bash', + name: 'bash', + interactive: true, + working_directory: '/home/kg', + pid: 3, + pgid: 2, + user: { + id: '2', + name: 'kg', + }, }, }, }, - }]; + ]; }; export const getEvent = () => { @@ -90,94 +92,96 @@ export const getEvent = () => { { args: ['ls', '-l'], command_line: 'ls -l', - name: 'ls' + name: 'ls', }, { args: ['pwd'], command_line: 'pwd', - name: 'pwd' + name: 'pwd', }, { args: ['dir'], command_line: 'dir', - name: 'dir' - } - ] + name: 'dir', + }, + ]; const randomElement = random[Math.floor(Math.random() * random.length)]; - return [{ - '@timestamp': 'Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)', - event: { - kind: 'event', - category: 'process', - action: EventAction.exec, - }, - process: { - args: randomElement.args, - args_count: 2, - command_line: randomElement.command_line, - entity_id: uuid.v4().toString(), - executable: '/bin/ls', - name: randomElement.name, - interactive: true, - working_directory: '/home/kg', - pid: 4, - pgid: 3, - user: { - id: '2', - name: 'kg', + return [ + { + '@timestamp': 'Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)', + event: { + kind: 'event', + category: 'process', + action: EventAction.exec, }, - parent: { - args: ['bash'], - args_count: 1, - command_line: 'bash', - entity_id: '4321', - executable: '/bin/bash', - name: 'bash', + process: { + args: randomElement.args, + args_count: 2, + command_line: randomElement.command_line, + entity_id: uuid.v4().toString(), + executable: '/bin/ls', + name: randomElement.name, interactive: true, working_directory: '/home/kg', - pid: 3, - pgid: 2, + pid: 4, + pgid: 3, user: { id: '2', name: 'kg', }, - }, - session: { - args: ['bash'], - args_count: 1, - command_line: 'bash', - entity_id: '4321', - executable: '/bin/bash', - name: 'bash', - interactive: true, - working_directory: '/home/kg', - pid: 3, - pgid: 2, - user: { - id: '2', - name: 'kg', + parent: { + args: ['bash'], + args_count: 1, + command_line: 'bash', + entity_id: '4321', + executable: '/bin/bash', + name: 'bash', + interactive: true, + working_directory: '/home/kg', + pid: 3, + pgid: 2, + user: { + id: '2', + name: 'kg', + }, }, - }, - entry: { - args: ['bash'], - args_count: 1, - command_line: 'bash', - entity_id: '4321', - executable: '/bin/bash', - name: 'bash', - interactive: true, - working_directory: '/home/kg', - pid: 3, - pgid: 2, - user: { - id: '2', - name: 'kg', + session: { + args: ['bash'], + args_count: 1, + command_line: 'bash', + entity_id: '4321', + executable: '/bin/bash', + name: 'bash', + interactive: true, + working_directory: '/home/kg', + pid: 3, + pgid: 2, + user: { + id: '2', + name: 'kg', + }, + }, + entry: { + args: ['bash'], + args_count: 1, + command_line: 'bash', + entity_id: '4321', + executable: '/bin/bash', + name: 'bash', + interactive: true, + working_directory: '/home/kg', + pid: 3, + pgid: 2, + user: { + id: '2', + name: 'kg', + }, }, }, }, - }]; + ]; }; export const getEnd = () => { @@ -272,12 +276,12 @@ export const getEnd = () => { working_directory: '/home/kg', pid: 6, pgid: 4, - + user: { id: '2', name: 'kg', }, - + parent: { args: ['df'], args_count: 1, @@ -346,12 +350,12 @@ export const getEnd = () => { working_directory: '/home/kg', pid: 6, pgid: 4, - + user: { id: '2', name: 'kg', }, - + parent: { args: ['df'], args_count: 1, @@ -422,12 +426,12 @@ export const getEnd = () => { pgid: 4, end: 'Thu Oct 14 2021 12: 08: 56 GMT-0700 (Pacific Daylight Time)', exit_code: 137, - + user: { id: '2', name: 'kg', }, - + parent: { args: ['df'], args_count: 1, @@ -479,7 +483,7 @@ export const getEnd = () => { }, }, ]; -} +}; export const mockData: ProcessEvent[] = [ { diff --git a/x-pack/plugins/session_view/common/utils/flatten_json.ts b/x-pack/plugins/session_view/common/utils/flatten_json.ts new file mode 100644 index 00000000000000..dddd014d64592e --- /dev/null +++ b/x-pack/plugins/session_view/common/utils/flatten_json.ts @@ -0,0 +1,29 @@ +/* + * 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 const flattenJSON = (data: any) => { + const result: any = {}; + function recurse(cur: any, prop: string) { + if (Object(cur) !== cur) { + result[prop] = cur; + } else if (Array.isArray(cur)) { + for (let i = 0; i < cur.length; i++) recurse(cur[i], prop + '[' + i + ']'); + if (cur.length === 0) result[prop] = []; + } else { + let isEmpty = true; + for (const p in cur) { + if ({}.hasOwnProperty.call(cur, p)) { + isEmpty = false; + recurse(cur[p], prop ? prop + '.' + p : p); + } + } + if (isEmpty) result[prop] = {}; + } + } + recurse(data, ''); + return result; +}; diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index f80b20d50250f6..25713941e788cb 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -6,6 +6,7 @@ */ import React, { useState, useEffect } from 'react'; import { useQuery } from 'react-query'; +import MonacoEditor from 'react-monaco-editor'; import { EuiSearchBar, EuiSearchBarOnChangeArgs, @@ -17,6 +18,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle, + EuiTabs, + EuiTab, } from '@elastic/eui'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; @@ -24,6 +27,7 @@ import { ProcessTree } from '../ProcessTree'; import { Process, ProcessEvent } from '../../hooks/use_process_tree'; import { useStyles } from './styles'; import { PROCESS_EVENTS_ROUTE } from '../../../common/constants'; +import { flattenJSON } from '../../../common/utils/flatten_json'; interface SessionViewDeps { // the root node of the process tree to render. e.g process.entry.entity_id or process.session.entity_id @@ -47,8 +51,9 @@ interface ProcessEventResults { export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { const [searchQuery, setSearchQuery] = useState(''); const [data, setData] = useState([]); - const [isDetailOpen, setIsDetailOpen] = useState(false); - const [isDetailMounted, setIsDetailMounted] = useState(false); + const [isDetailOpen, setIsDetailOpen] = useState(false); + const [isDetailMounted, setIsDetailMounted] = useState(false); + const [selectedDetailTab, setSelectedDetailTab] = useState('tree'); const [selectedProcess, setSelectedProcess] = useState(null); const { http } = useKibana().services; @@ -75,7 +80,7 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { http.get(PROCESS_EVENTS_ROUTE, { query: { indexes: ['cmd*', '.siem-signals-*'], - sessionEntityId + sessionEntityId, }, }) ); @@ -107,10 +112,127 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { if (!isDetailOpen) setIsDetailOpen(true); }; + const detailPanelTabs = [ + { + id: 'tree', + name: 'Tree view', + content: ( + <> + {selectedProcess && ( + <> + + Command Detail + + + {selectedProcess.events.map((event) => { + const flattenedSelectedProcess = flattenJSON(event); + return ( + <> + + {flattenedSelectedProcess['event.action']} + + ({ + title, + description: flattenedSelectedProcess[title], + }))} + /> + + + ); + })} + + + )} + + Session Detail + + + {(() => { + const flattenedSession = flattenJSON(data?.[0]?.process.session); + return ( + ({ + title, + description: flattenedSession[title], + }))} + /> + ); + })()} + + + Server Detail + + {/* Add server detail */} + + + Alert Detail + + {/* Add alert detail conditionally */} + + ), + }, + { + id: 'json', + name: 'JSON view', + content: ( + <> + {selectedProcess && ( + <> + + Command Detail + + + + + + )} + + Session Detail + + + + + + Server Detail + + {/* Add server detail */} + + + Alert Detail + + {/* Add alert detail conditionally */} + + ), + }, + ]; + if (!data.length) { return renderNoData(); } -console.log(selectedProcess) + return ( <> @@ -123,7 +245,12 @@ console.log(selectedProcess) - +
- {selectedProcess && ( - <> - Command Detail - - {selectedProcess.events.map((event) => { - return ( ({ - title, - description: JSON.stringify(event[title as keyof ProcessEvent], null, 4), - }))} - />) - })} - - - )} - - Session Detail - - ({ - title, - description: JSON.stringify(data?.[0].process.session[title], null, 4), - }))} - /> - - - - Server Detail - {/* Add server detail */} - - - - Alert Detail - {/* Add alert detail conditionally */} + + {detailPanelTabs.map((tab, idx) => ( + setSelectedDetailTab(tab.id)} + isSelected={tab.id === selectedDetailTab} + > + {tab.name} + + ))} + + + {detailPanelTabs.find((obj) => obj.id === selectedDetailTab)?.content} )} diff --git a/x-pack/plugins/session_view/public/components/SessionView/styles.ts b/x-pack/plugins/session_view/public/components/SessionView/styles.ts index 24dd18b60aa503..5212a4e4812829 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/styles.ts +++ b/x-pack/plugins/session_view/public/components/SessionView/styles.ts @@ -38,18 +38,18 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { const slideIn = keyframes({ to: { right: '0', - } + }, }); - + const slideOut = keyframes({ from: { right: '0', }, to: { right: '-100%', - } + }, }); - + const detailPanel: CSSObject = { width: '424px', height: `${height}px`, @@ -66,7 +66,7 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { animation: `${slideIn.name} 200ms ease forwards`, }, ]; - + const detailPanelOut: Array = [ slideOut.styles, { @@ -82,7 +82,7 @@ export const useStyles = ({ height = 500 }: StylesDeps) => { detailPanelIn, detailPanelOut, }; - }, [euiTheme]); + }, [height, euiTheme]); return cached; }; diff --git a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts index 03b3c774a7dee5..ec3b580da5f79b 100644 --- a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts +++ b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts @@ -47,7 +47,7 @@ interface ProcessFields { exit_code?: number; } -interface ProcessSelf extends ProcessFields { +export interface ProcessSelf extends ProcessFields { parent: ProcessFields; session: ProcessFields; entry: ProcessFields; @@ -134,7 +134,9 @@ class ProcessImpl implements Process { } getDetails() { - const execsForks = this.events.filter(({ event }) => [EventAction.exec, EventAction.fork].includes(event.action)); + const execsForks = this.events.filter(({ event }) => + [EventAction.exec, EventAction.fork].includes(event.action) + ); return execsForks[execsForks.length - 1]; } @@ -199,8 +201,8 @@ export const useProcessTree = ({ if (processMap[sessionEntityId].events.length === 0) { processMap[sessionEntityId].events.push({ ...events[0], - ...events[0].process.entry - }) + ...events[0].process.entry, + }); } }; diff --git a/x-pack/plugins/session_view/server/routes/recent_session_route.ts b/x-pack/plugins/session_view/server/routes/recent_session_route.ts index 71a95f3d4d0b95..10790714c70b00 100644 --- a/x-pack/plugins/session_view/server/routes/recent_session_route.ts +++ b/x-pack/plugins/session_view/server/routes/recent_session_route.ts @@ -30,7 +30,7 @@ export const registerRecentSessionRoute = (router: IRouter) => { 'process.entry.interactive': true, }, }, - size: 1 + size: 1, }); return response.ok({ body: search.body.hits }); diff --git a/x-pack/plugins/session_view/server/routes/test_route.ts b/x-pack/plugins/session_view/server/routes/test_route.ts index 2906548f1562a6..68b6561cd218ea 100644 --- a/x-pack/plugins/session_view/server/routes/test_route.ts +++ b/x-pack/plugins/session_view/server/routes/test_route.ts @@ -27,7 +27,7 @@ export const registerTestRoute = (router: IRouter) => { const { index } = request.query; const search = await client.search({ - index: [`${index}`] + index: [`${index}`], }); return response.ok({ body: search.body.hits }); @@ -59,7 +59,7 @@ export const registerTestRoute = (router: IRouter) => { timestamp: new Date().toISOString(), }, }); - }) + }); await Promise.all(requests); @@ -76,7 +76,7 @@ export const registerTestRoute = (router: IRouter) => { path: INTERNAL_TEST_ROUTE, validate: { body: schema.object({ - index: schema.string() + index: schema.string(), }), }, }, @@ -84,18 +84,18 @@ export const registerTestRoute = (router: IRouter) => { const { index } = request.body; const client = context.core.elasticsearch.client.asCurrentUser; - await client.deleteByQuery({ + await client.deleteByQuery({ index, body: { query: { match_all: {} }, }, }); - + return response.ok({ body: { message: 'ok!', }, }); - }, - ) + } + ); }; From ea0daaf09f53f10c2c9864b21bfb7126f7d5f655 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Thu, 18 Nov 2021 13:15:05 -0300 Subject: [PATCH 06/13] using accordion --- .../public/components/SessionView/index.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index 25713941e788cb..744cef6ff01751 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -20,6 +20,7 @@ import { EuiTitle, EuiTabs, EuiTab, + EuiAccordion, } from '@elastic/eui'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; @@ -127,10 +128,10 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { {selectedProcess.events.map((event) => { const flattenedSelectedProcess = flattenJSON(event); return ( - <> - - {flattenedSelectedProcess['event.action']} - + { }))} /> - + ); })} From 600198aebd1f1a37aa1ed8fa3c8fce45dcbff5cf Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Thu, 18 Nov 2021 18:59:38 -0500 Subject: [PATCH 07/13] Refactor detail panel to own component and fix styling --- .../public/components/ProcessTree/index.tsx | 4 +- .../components/ProcessTreeNode/index.tsx | 31 +-- .../components/ProcessTreeNode/styles.ts | 4 +- .../public/components/SessionView/index.tsx | 192 +++--------------- .../SessionViewDetailPanel/index.tsx | 148 ++++++++++++++ .../SessionViewDetailPanel/styles.ts | 64 ++++++ .../components/SessionViewPage/index.tsx | 1 - .../public/hooks/use_process_tree.ts | 20 +- .../server/routes/process_events_route.ts | 8 +- .../server/routes/recent_session_route.ts | 4 +- 10 files changed, 281 insertions(+), 195 deletions(-) create mode 100644 x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx create mode 100644 x-pack/plugins/session_view/public/components/SessionViewDetailPanel/styles.ts diff --git a/x-pack/plugins/session_view/public/components/ProcessTree/index.tsx b/x-pack/plugins/session_view/public/components/ProcessTree/index.tsx index bf1fe3dd54742e..74406bd38bf323 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTree/index.tsx +++ b/x-pack/plugins/session_view/public/components/ProcessTree/index.tsx @@ -135,9 +135,9 @@ export const ProcessTree = ({ onProcessSelected={onProcessSelected} /> ); - }) + }); } - } + }; return (
diff --git a/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx b/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx index c48d8e0b9bd2aa..dcfbd8a02bf432 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx +++ b/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx @@ -47,7 +47,6 @@ export function ProcessTreeNode({ setChildrenExpanded(isSessionLeader || process.autoExpand); }, [isSessionLeader, process.autoExpand]); - const processDetails = useMemo(() => { return process.getDetails(); }, [process.events.length]); @@ -55,17 +54,17 @@ export function ProcessTreeNode({ const hasExec = useMemo(() => { return process.hasExec(); }, [process.events.length]); - + const alerts = useMemo(() => { return process.getAlerts(); }, [process.events.length]); - + if (!processDetails) { return null; } - + const styles = useStyles({ depth, hasAlerts: !!alerts.length }); - + useLayoutEffect(() => { if (searchMatched !== null && textRef.current) { const regex = new RegExp(searchMatched); @@ -108,14 +107,17 @@ export function ProcessTreeNode({ const getExpandedIcon = (expanded: boolean) => { return expanded ? 'arrowUp' : 'arrowDown'; - } + }; const renderButtons = () => { const buttons = []; if (!isSessionLeader && process.children.length > 0) { buttons.push( - setChildrenExpanded(!childrenExpanded)}> + setChildrenExpanded(!childrenExpanded)} + > @@ -124,7 +126,10 @@ export function ProcessTreeNode({ if (alerts.length) { buttons.push( - setAlertsExpanded(!alertsExpanded)}> + setAlertsExpanded(!alertsExpanded)} + > @@ -189,11 +194,13 @@ export function ProcessTreeNode({ const { user, parent } = processDetails.process; if (user.name === 'root' && user.id !== parent.user.id) { - return - - + return ( + + + + ); } - } + }; const onProcessClicked = (e: MouseEvent) => { e.stopPropagation(); diff --git a/x-pack/plugins/session_view/public/components/ProcessTreeNode/styles.ts b/x-pack/plugins/session_view/public/components/ProcessTreeNode/styles.ts index 7407c2feac680b..cdf738531b14bb 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTreeNode/styles.ts +++ b/x-pack/plugins/session_view/public/components/ProcessTreeNode/styles.ts @@ -26,7 +26,7 @@ export const useStyles = ({ depth, hasAlerts }: StylesDeps) => { children = 'children', alerts = 'alerts', output = 'output', - userChanged = 'user' + userChanged = 'user', } const darkText: CSSObject = { @@ -101,7 +101,7 @@ export const useStyles = ({ depth, hasAlerts }: StylesDeps) => { */ const getHighlightColors = () => { let bgColor = 'none'; - let hoverColor = '#6B5FC6'; + const hoverColor = '#6B5FC6'; let borderColor = 'transparent'; // TODO: alerts highlight colors diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index 1c7fd70093b23b..207705084a6393 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -6,29 +6,23 @@ */ import React, { useState, useEffect } from 'react'; import { useQuery } from 'react-query'; -import MonacoEditor from 'react-monaco-editor'; +import { i18n } from '@kbn/i18n'; import { EuiSearchBar, EuiSearchBarOnChangeArgs, EuiEmptyPrompt, EuiButton, - EuiDescriptionList, - EuiSpacer, EuiSplitPanel, EuiFlexGroup, EuiFlexItem, - EuiTitle, - EuiTabs, - EuiTab, - EuiAccordion, } from '@elastic/eui'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; import { ProcessTree } from '../ProcessTree'; import { Process, ProcessEvent } from '../../hooks/use_process_tree'; +import { SessionViewDetailPanel } from '../SessionViewDetailPanel'; import { useStyles } from './styles'; import { PROCESS_EVENTS_ROUTE } from '../../../common/constants'; -import { flattenJSON } from '../../../common/utils/flatten_json'; interface SessionViewDeps { // the root node of the process tree to render. e.g process.entry.entity_id or process.session.entity_id @@ -47,6 +41,13 @@ interface ProcessEventResults { }; } +const BUTTON_OPEN_DETAIL_PANEL = i18n.translate( + 'xpack.sessionView.detailPanel.buttonOpenDetailPanel', + { + defaultMessage: 'Detail panel', + } +); + /** * The main wrapper component for the session view. * TODO: @@ -57,10 +58,9 @@ interface ProcessEventResults { */ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { const [searchQuery, setSearchQuery] = useState(''); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [isDetailOpen, setIsDetailOpen] = useState(false); const [isDetailMounted, setIsDetailMounted] = useState(false); - const [selectedDetailTab, setSelectedDetailTab] = useState('tree'); const [selectedProcess, setSelectedProcess] = useState(null); const { http } = useKibana().services; @@ -106,10 +106,10 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { return; } - const events: ProcessEvent[] = getData.events.hits.map( + const events: ProcessEvent[] = (getData.events?.hits || []).map( (event: any) => event._source as ProcessEvent ); - const alerts: ProcessEvent[] = getData.alerts.hits.map((event: any) => { + const alerts: ProcessEvent[] = (getData.alerts?.hits || []).map((event: any) => { return event._source as ProcessEvent; }); const all: ProcessEvent[] = events.concat(alerts).sort(sortEvents); @@ -130,123 +130,6 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { if (!isDetailOpen) setIsDetailOpen(true); }; - const detailPanelTabs = [ - { - id: 'tree', - name: 'Tree view', - content: ( - <> - {selectedProcess && ( - <> - - Command Detail - - - {selectedProcess.events.map((event) => { - const flattenedSelectedProcess = flattenJSON(event); - return ( - - ({ - title, - description: flattenedSelectedProcess[title], - }))} - /> - - - ); - })} - - - )} - - Session Detail - - - {(() => { - const flattenedSession = flattenJSON(data?.[0]?.process.session); - return ( - ({ - title, - description: flattenedSession[title], - }))} - /> - ); - })()} - - - Server Detail - - {/* Add server detail */} - - - Alert Detail - - {/* Add alert detail conditionally */} - - ), - }, - { - id: 'json', - name: 'JSON view', - content: ( - <> - {selectedProcess && ( - <> - - Command Detail - - - - - - )} - - Session Detail - - - - - - Server Detail - - {/* Add server detail */} - - - Alert Detail - - {/* Add alert detail conditionally */} - - ), - }, - ]; - if (!data.length) { return renderNoData(); } @@ -259,7 +142,7 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { - Detail Panel + {BUTTON_OPEN_DETAIL_PANEL} @@ -270,39 +153,26 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { css={styles.outerPanel} > - {data &&
- -
} + {data && ( +
+ +
+ )}
{isDetailOpen && ( - { - if (!isDetailMounted) setIsDetailOpen(false); - }} - > - - {detailPanelTabs.map((tab, idx) => ( - setSelectedDetailTab(tab.id)} - isSelected={tab.id === selectedDetailTab} - > - {tab.name} - - ))} - - - {detailPanelTabs.find((obj) => obj.id === selectedDetailTab)?.content} - + )} diff --git a/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx new file mode 100644 index 00000000000000..cba77f6fa21753 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx @@ -0,0 +1,148 @@ +/* + * 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 React, { useState, useEffect, ReactNode } from 'react'; +import MonacoEditor from 'react-monaco-editor'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiSplitPanel, EuiTitle, EuiTabs, EuiTab } from '@elastic/eui'; +import { Process } from '../../hooks/use_process_tree'; +import { useStyles } from './styles'; + +interface SessionViewDetailPanelDeps { + isDetailMounted: boolean; + height?: number; + selectedProcess: Process | null; + setIsDetailOpen(isDetailOpen: boolean): void; + session?: any; +} + +interface ProcessEventTabData { + id: string | number; + name: string; + content: ReactNode; +} + +const DETAIL_PANEL_COMMAND = i18n.translate('xpack.sessionView.detailPanel.detailPanelCommand', { + defaultMessage: 'Command detail', +}); + +const DETAIL_PANEL_SESSION = i18n.translate('xpack.sessionView.detailPanel.detailPanelSession', { + defaultMessage: 'Session detail', +}); + +const DETAIL_PANEL_SERVER = i18n.translate('xpack.sessionView.detailPanel.detailPanelServer', { + defaultMessage: 'Server detail', +}); + +const DETAIL_PANEL_ALERT = i18n.translate('xpack.sessionView.detailPanel.detailPanelAlert', { + defaultMessage: 'Alert detail', +}); + +/** + * Detail panel in the session view. + */ +export const SessionViewDetailPanel = ({ + isDetailMounted, + height, + selectedProcess, + setIsDetailOpen, + session, +}: SessionViewDetailPanelDeps) => { + const [selectedDetailTab, setSelectedDetailTab] = useState(''); + const [processEventsTabs, setProcessEventsTabs] = useState([]); + + const styles = useStyles({ height }); + + useEffect(() => { + const selectedProcessEvents = (selectedProcess?.events || []).map((processEvent, idx) => ({ + id: `${processEvent?.event.action}-${idx + 1}` || `event-${idx + 1}`, + name: `${processEvent?.event.action}-${idx + 1}` || `event-${idx + 1}`, + content: ( +
+ + +
+ ), + })); + + setProcessEventsTabs(selectedProcessEvents); + setSelectedDetailTab(selectedProcessEvents?.[0].id || ''); + }, [selectedProcess]); + + const handleAnimationEnd = () => { + if (!isDetailMounted) { + setIsDetailOpen(false); + } + }; + + const renderSelectedProcessEvents = () => { + if (selectedProcess) { + return ( +
+ + {DETAIL_PANEL_COMMAND} + + + + {processEventsTabs.map((tab, idx) => ( + setSelectedDetailTab(tab.id)} + isSelected={tab.id === selectedDetailTab} + > + {tab.name} + + ))} + + + {processEventsTabs.find((tab) => tab.id === selectedDetailTab)?.content} +
+ ); + } + }; + + return ( + + {renderSelectedProcessEvents()} + + {DETAIL_PANEL_SESSION} + + + + + + {DETAIL_PANEL_SERVER} + + {/* Add server detail */} + + + {DETAIL_PANEL_ALERT} + + {/* Add alert detail conditionally */} + + ); +}; diff --git a/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/styles.ts b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/styles.ts new file mode 100644 index 00000000000000..bb9959cffc0a5b --- /dev/null +++ b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/styles.ts @@ -0,0 +1,64 @@ +/* + * 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 { useMemo } from 'react'; +import { keyframes, CSSObject } from '@emotion/react'; + +interface StylesDeps { + height: number | undefined; +} + +export const useStyles = ({ height = 500 }: StylesDeps) => { + const cached = useMemo(() => { + const slideIn = keyframes({ + to: { + right: '0', + }, + }); + + const slideOut = keyframes({ + from: { + right: '0', + }, + to: { + right: '-100%', + }, + }); + + const detailPanel: CSSObject = { + width: '424px', + height: `${height}px`, + overflowY: 'auto', + position: 'absolute', + top: '8px', + right: '-100%', + }; + + const detailPanelIn: Array = [ + slideIn.styles, + { + ...detailPanel, + animation: `${slideIn.name} 200ms ease forwards`, + }, + ]; + + const detailPanelOut: Array = [ + slideOut.styles, + { + ...detailPanel, + animation: `${slideOut.name} 150ms ease`, + }, + ]; + + return { + detailPanelIn, + detailPanelOut, + }; + }, [height]); + + return cached; +}; diff --git a/x-pack/plugins/session_view/public/components/SessionViewPage/index.tsx b/x-pack/plugins/session_view/public/components/SessionViewPage/index.tsx index cadfcdafe7b4e5..9479aa252ede80 100644 --- a/x-pack/plugins/session_view/public/components/SessionViewPage/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionViewPage/index.tsx @@ -52,7 +52,6 @@ export const SessionViewPage = (props: RouteComponentProps) => { } }, [data]); - return ( diff --git a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts index df24984fb95cde..d4dcb7ee3bd536 100644 --- a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts +++ b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts @@ -89,7 +89,7 @@ export interface ProcessEvent { original_time: Date; original_event: { action: string; - }, + }; rule: { category: string; consumer: string; @@ -100,9 +100,9 @@ export interface ProcessEvent { risk_score: number; severity: string; uuid: string; - } - } - } + }; + }; + }; } export interface Process { @@ -146,7 +146,7 @@ class ProcessImpl implements Process { hasAlerts() { return !!this.events.find(({ event }) => event.kind === EventKind.signal); } - + getAlerts() { return this.events.filter(({ event }) => event.kind === EventKind.signal); } @@ -167,7 +167,7 @@ class ProcessImpl implements Process { if (execsForks.length === 0) { debugger; } - + return execsForks[execsForks.length - 1]; } @@ -208,16 +208,16 @@ export const useProcessTree = ({ // we add a fake session leader event, sourced from wide event data. // this is because we might not always have a session leader event // especially if we are paging in reverse from deep within a large session - const fakeLeaderEvent = forward.find(event => event.event.kind === EventKind.event); + const fakeLeaderEvent = forward.find((event) => event.event.kind === EventKind.event); const sessionLeaderProcess = new ProcessImpl(sessionEntityId); - + if (fakeLeaderEvent) { - fakeLeaderEvent.process = { ...fakeLeaderEvent.process, ...fakeLeaderEvent.process.entry}; + fakeLeaderEvent.process = { ...fakeLeaderEvent.process, ...fakeLeaderEvent.process.entry }; sessionLeaderProcess.events.push(fakeLeaderEvent); } const initializedProcessMap: ProcessMap = { - [sessionEntityId]: sessionLeaderProcess, + [sessionEntityId]: sessionLeaderProcess, }; const [processMap, setProcessMap] = useState(initializedProcessMap); diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.ts b/x-pack/plugins/session_view/server/routes/process_events_route.ts index 5d53ebf67d0968..cbb6e661e3741b 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.ts @@ -21,7 +21,7 @@ export const registerProcessEventsRoute = (router: IRouter) => { }, async (context, request, response) => { const client = context.core.elasticsearch.client.asCurrentUser; - + // TODO: would be good to figure out how to add securitySolution as a dep // and make use of this way of getting the siem-signals index, instead of // hardcoding it. @@ -40,7 +40,7 @@ export const registerProcessEventsRoute = (router: IRouter) => { }, size: PROCESS_EVENTS_PER_PAGE, sort: [{ '@timestamp': 'asc' }], - } + }, }); // temporary approach. ideally we'd pull from both these indexes above, but unfortunately @@ -48,11 +48,11 @@ export const registerProcessEventsRoute = (router: IRouter) => { // this should hopefully change once we update ECS or endpoint-package.. // for demo purpose we just load all alerts, and stich it together on the frontend. const alerts = await client.search({ - index: ['.siem-signals-default'], + index: ['.siem-signals-default*'], body: { size: PROCESS_EVENTS_PER_PAGE, sort: [{ '@timestamp': 'asc' }], - } + }, }); alerts.body.hits.hits = alerts.body.hits.hits.map((hit: any) => { diff --git a/x-pack/plugins/session_view/server/routes/recent_session_route.ts b/x-pack/plugins/session_view/server/routes/recent_session_route.ts index 6eb1b415f90023..62e92cb98e8b0b 100644 --- a/x-pack/plugins/session_view/server/routes/recent_session_route.ts +++ b/x-pack/plugins/session_view/server/routes/recent_session_route.ts @@ -32,9 +32,7 @@ export const registerRecentSessionRoute = (router: IRouter) => { }, }, size: 1, - sort: [ - {'@timestamp' :'desc'} - ], + sort: [{ '@timestamp': 'desc' }], }, }); From 8fe7522e663b2e7d5df40ec1d5dd614424b1e6f9 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Thu, 18 Nov 2021 19:06:48 -0500 Subject: [PATCH 08/13] Remove flattenJSON function as it's not needed anymore --- .../session_view/common/utils/flatten_json.ts | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 x-pack/plugins/session_view/common/utils/flatten_json.ts diff --git a/x-pack/plugins/session_view/common/utils/flatten_json.ts b/x-pack/plugins/session_view/common/utils/flatten_json.ts deleted file mode 100644 index dddd014d64592e..00000000000000 --- a/x-pack/plugins/session_view/common/utils/flatten_json.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 const flattenJSON = (data: any) => { - const result: any = {}; - function recurse(cur: any, prop: string) { - if (Object(cur) !== cur) { - result[prop] = cur; - } else if (Array.isArray(cur)) { - for (let i = 0; i < cur.length; i++) recurse(cur[i], prop + '[' + i + ']'); - if (cur.length === 0) result[prop] = []; - } else { - let isEmpty = true; - for (const p in cur) { - if ({}.hasOwnProperty.call(cur, p)) { - isEmpty = false; - recurse(cur[p], prop ? prop + '.' + p : p); - } - } - if (isEmpty) result[prop] = {}; - } - } - recurse(data, ''); - return result; -}; From 1d131f9a26c635ec97ea04308fe1511e90afc926 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Fri, 19 Nov 2021 10:53:03 -0500 Subject: [PATCH 09/13] Use formatted message for localization + minor undefined fix --- .../SessionViewDetailPanel/index.tsx | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx index cba77f6fa21753..ab50f4d67ce248 100644 --- a/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionViewDetailPanel/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useEffect, ReactNode } from 'react'; import MonacoEditor from 'react-monaco-editor'; -import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiSpacer, EuiSplitPanel, EuiTitle, EuiTabs, EuiTab } from '@elastic/eui'; import { Process } from '../../hooks/use_process_tree'; import { useStyles } from './styles'; @@ -25,22 +25,6 @@ interface ProcessEventTabData { content: ReactNode; } -const DETAIL_PANEL_COMMAND = i18n.translate('xpack.sessionView.detailPanel.detailPanelCommand', { - defaultMessage: 'Command detail', -}); - -const DETAIL_PANEL_SESSION = i18n.translate('xpack.sessionView.detailPanel.detailPanelSession', { - defaultMessage: 'Session detail', -}); - -const DETAIL_PANEL_SERVER = i18n.translate('xpack.sessionView.detailPanel.detailPanelServer', { - defaultMessage: 'Server detail', -}); - -const DETAIL_PANEL_ALERT = i18n.translate('xpack.sessionView.detailPanel.detailPanelAlert', { - defaultMessage: 'Alert detail', -}); - /** * Detail panel in the session view. */ @@ -49,7 +33,6 @@ export const SessionViewDetailPanel = ({ height, selectedProcess, setIsDetailOpen, - session, }: SessionViewDetailPanelDeps) => { const [selectedDetailTab, setSelectedDetailTab] = useState(''); const [processEventsTabs, setProcessEventsTabs] = useState([]); @@ -77,7 +60,7 @@ export const SessionViewDetailPanel = ({ })); setProcessEventsTabs(selectedProcessEvents); - setSelectedDetailTab(selectedProcessEvents?.[0].id || ''); + setSelectedDetailTab(selectedProcessEvents?.[0]?.id || ''); }, [selectedProcess]); const handleAnimationEnd = () => { @@ -91,7 +74,12 @@ export const SessionViewDetailPanel = ({ return (
- {DETAIL_PANEL_COMMAND} + + + @@ -121,26 +109,23 @@ export const SessionViewDetailPanel = ({ > {renderSelectedProcessEvents()} - {DETAIL_PANEL_SESSION} + + + - - + {/* Add session detail */} - {DETAIL_PANEL_SERVER} + + + {/* Add server detail */} - {DETAIL_PANEL_ALERT} + + + {/* Add alert detail conditionally */} From d6c907cc9a3078f9f7fffad428d4981ce61f8527 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Fri, 19 Nov 2021 10:57:02 -0500 Subject: [PATCH 10/13] Use FormattedMessage for open detail panel button --- .../public/components/SessionView/index.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index 207705084a6393..291b238cc0baff 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -6,7 +6,6 @@ */ import React, { useState, useEffect } from 'react'; import { useQuery } from 'react-query'; -import { i18n } from '@kbn/i18n'; import { EuiSearchBar, EuiSearchBarOnChangeArgs, @@ -16,6 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; import { ProcessTree } from '../ProcessTree'; @@ -41,13 +41,6 @@ interface ProcessEventResults { }; } -const BUTTON_OPEN_DETAIL_PANEL = i18n.translate( - 'xpack.sessionView.detailPanel.buttonOpenDetailPanel', - { - defaultMessage: 'Detail panel', - } -); - /** * The main wrapper component for the session view. * TODO: @@ -142,7 +135,10 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { - {BUTTON_OPEN_DETAIL_PANEL} + From b424250f62f86e5fd2eaa8f4c9098430ea7a49a5 Mon Sep 17 00:00:00 2001 From: Zizhou Wang Date: Mon, 22 Nov 2021 18:25:58 -0500 Subject: [PATCH 11/13] Fix looping proces parent circular structure --- x-pack/plugins/session_view/public/hooks/use_process_tree.ts | 4 ++-- .../session_view/server/routes/process_events_route.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts index d4dcb7ee3bd536..2eab78fe4cf286 100644 --- a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts +++ b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts @@ -183,7 +183,7 @@ class ProcessImpl implements Process { isUserEntered() { const event = this.getDetails(); - const { interactive, pgid, parent } = event.process; + const { interactive, pgid, parent } = event?.process || {}; return interactive && pgid !== parent.pgid; } @@ -298,7 +298,7 @@ export const useProcessTree = ({ if (process.searchMatched || process.isUserEntered()) { let { parent } = process; - while (parent) { + while (parent && parent.id !== parent.parent?.id) { parent.autoExpand = true; parent = parent.parent; } diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.ts b/x-pack/plugins/session_view/server/routes/process_events_route.ts index cbb6e661e3741b..10c0954f6ad076 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.ts @@ -48,7 +48,7 @@ export const registerProcessEventsRoute = (router: IRouter) => { // this should hopefully change once we update ECS or endpoint-package.. // for demo purpose we just load all alerts, and stich it together on the frontend. const alerts = await client.search({ - index: ['.siem-signals-default*'], + index: ['.siem-signals-default'], body: { size: PROCESS_EVENTS_PER_PAGE, sort: [{ '@timestamp': 'asc' }], From 1b286ce659392f17720f43bb610a275442ed9e4d Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 23 Nov 2021 10:56:48 -0300 Subject: [PATCH 12/13] fix lint errors --- .../session_view/common/test/mock_data.ts | 44 +++++++++---------- .../components/ProcessTreeNode/index.tsx | 14 +++--- .../public/hooks/use_process_tree.ts | 1 + 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/session_view/common/test/mock_data.ts b/x-pack/plugins/session_view/common/test/mock_data.ts index 8426aa26023122..df10d0d85bb3ba 100644 --- a/x-pack/plugins/session_view/common/test/mock_data.ts +++ b/x-pack/plugins/session_view/common/test/mock_data.ts @@ -4,14 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import uuid from 'uuid'; -import { EventAction, ProcessEvent } from '../../public/hooks/use_process_tree'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { EventAction, ProcessEvent, EventKind } from '../../public/hooks/use_process_tree'; export const getStart = () => { return [ { - '@timestamp': 'Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', @@ -110,7 +110,7 @@ export const getEvent = () => { return [ { - '@timestamp': 'Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', @@ -187,7 +187,7 @@ export const getEvent = () => { export const getEnd = () => { return [ { - '@timestamp': 'Thu Oct 14 2021 12: 07: 52 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 52 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', @@ -259,7 +259,7 @@ export const getEnd = () => { }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', @@ -333,7 +333,7 @@ export const getEnd = () => { }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', @@ -407,11 +407,11 @@ export const getEnd = () => { }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 08: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 08: 56 GMT-0700 (Pacific Daylight Time)'), event: { kind: 'event', category: 'process', - action: EventAction.end, + action: EventAction.exit, }, process: { args: ['df', 'nested'], @@ -487,9 +487,9 @@ export const getEnd = () => { export const mockData: ProcessEvent[] = [ { - '@timestamp': 'Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 06: 48 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', action: EventAction.exec, }, @@ -561,9 +561,9 @@ export const mockData: ProcessEvent[] = [ }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 06: 52 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', action: EventAction.exec, }, @@ -633,9 +633,9 @@ export const mockData: ProcessEvent[] = [ }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 07: 52 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 52 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', action: EventAction.exec, }, @@ -705,9 +705,9 @@ export const mockData: ProcessEvent[] = [ }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', action: EventAction.fork, }, @@ -779,9 +779,9 @@ export const mockData: ProcessEvent[] = [ }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 07: 56 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', action: EventAction.exec, }, @@ -853,11 +853,11 @@ export const mockData: ProcessEvent[] = [ }, }, { - '@timestamp': 'Thu Oct 14 2021 12: 08: 56 GMT-0700 (Pacific Daylight Time)', + '@timestamp': new Date('Thu Oct 14 2021 12: 08: 56 GMT-0700 (Pacific Daylight Time)'), event: { - kind: 'event', + kind: EventKind.event, category: 'process', - action: EventAction.end, + action: EventAction.exit, }, process: { args: ['df', 'nested'], diff --git a/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx b/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx index a0435a3e8d7b62..097dcd58b9670c 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx +++ b/x-pack/plugins/session_view/public/components/ProcessTreeNode/index.tsx @@ -49,20 +49,19 @@ export function ProcessTreeNode({ const processDetails = useMemo(() => { return process.getDetails(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [process.events.length]); const hasExec = useMemo(() => { return process.hasExec(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [process.events.length]); const alerts = useMemo(() => { return process.getAlerts(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [process.events.length]); - if (!processDetails) { - return null; - } - const styles = useStyles({ depth, hasAlerts: !!alerts.length }); useLayoutEffect(() => { @@ -74,9 +73,14 @@ export function ProcessTreeNode({ return `${match}`; }); + // eslint-disable-next-line no-unsanitized/property textRef.current.innerHTML = html; } - }, [searchMatched]); + }, [searchMatched, styles.searchHighlight]); + + if (!processDetails) { + return null; + } const { interactive } = processDetails.process; diff --git a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts index 9621cded652d82..75e39f17078cdd 100644 --- a/x-pack/plugins/session_view/public/hooks/use_process_tree.ts +++ b/x-pack/plugins/session_view/public/hooks/use_process_tree.ts @@ -165,6 +165,7 @@ class ProcessImpl implements Process { ); if (execsForks.length === 0) { + // eslint-disable-next-line no-debugger debugger; } From b2b98a1d7026db014cb81efcfdba06d1f688e261 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 23 Nov 2021 11:08:09 -0300 Subject: [PATCH 13/13] implementing nitpicks --- .../public/components/SessionView/index.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/session_view/public/components/SessionView/index.tsx b/x-pack/plugins/session_view/public/components/SessionView/index.tsx index e8d72df1de4633..3a9ee2a0dfc2a6 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -157,9 +157,25 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { } }; + const renderSessionViewDetailPanel = () => { + if (selectedProcess && isDetailOpen) { + return ( + + ); + } + }; + const toggleDetailPanel = () => { setIsDetailMounted(!isDetailMounted); - if (!isDetailOpen) setIsDetailOpen(true); + if (!isDetailOpen) { + setIsDetailOpen(true); + } }; if (!(isLoading || isError || data.length)) { @@ -190,15 +206,7 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { {renderProcessTree()} - {isDetailOpen && ( - - )} + {renderSessionViewDetailPanel()} );