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 3a1b57c9cf764..8426aa2602312 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/kibana.json b/x-pack/plugins/session_view/kibana.json index 4bb9471fe85cc..13bf88487473f 100644 --- a/x-pack/plugins/session_view/kibana.json +++ b/x-pack/plugins/session_view/kibana.json @@ -7,7 +7,7 @@ "githubTeam": "security-team" }, "requiredPlugins": ["data"], - "requiredBundles": ["kibanaReact"], + "requiredBundles": ["kibanaReact", "esUiShared"], "server": true, "ui": true } 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 bf1fe3dd54742..74406bd38bf32 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/ProcessTreeAlerts/index.tsx b/x-pack/plugins/session_view/public/components/ProcessTreeAlerts/index.tsx index 9294669faf4e8..f3c71b1f4726f 100644 --- a/x-pack/plugins/session_view/public/components/ProcessTreeAlerts/index.tsx +++ b/x-pack/plugins/session_view/public/components/ProcessTreeAlerts/index.tsx @@ -49,22 +49,22 @@ export function ProcessTreeAlerts({ alerts }: ProcessTreeAlertsDeps) {
- +
{name}
- +
{query}
- +
{severity}
@@ -72,13 +72,13 @@ export function ProcessTreeAlerts({ alerts }: ProcessTreeAlertsDeps) {
- +
{event.action}
- +
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 c48d8e0b9bd2a..a0435a3e8d7b6 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,15 +107,21 @@ 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,8 +129,11 @@ export function ProcessTreeNode({ if (alerts.length) { buttons.push( - setAlertsExpanded(!alertsExpanded)}> - + setAlertsExpanded(!alertsExpanded)} + > + ); @@ -142,7 +150,7 @@ export function ProcessTreeNode({ <> {name || executable}   - +   {user.name} @@ -189,11 +197,16 @@ 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 7407c2feac680..cdf738531b14b 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 d1b6e2606995e..949a2dc7f0afa 100644 --- a/x-pack/plugins/session_view/public/components/SessionView/index.tsx +++ b/x-pack/plugins/session_view/public/components/SessionView/index.tsx @@ -7,8 +7,10 @@ import React, { useState, useEffect } from 'react'; import { useQuery } from 'react-query'; import { EuiSearchBar, EuiSearchBarOnChangeArgs, EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { CoreStart } from '../../../../../../src/core/public'; +import { SectionLoading } from '../../shared_imports'; import { ProcessTree } from '../ProcessTree'; import { Process, ProcessEvent } from '../../hooks/use_process_tree'; import { useStyles } from './styles'; @@ -62,14 +64,16 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { } }; - const { data: getData } = useQuery( - ['process-tree', 'process_tree'], - () => - http.get(PROCESS_EVENTS_ROUTE, { - query: { - sessionEntityId, - }, - }) + const { + isLoading, + isError, + data: getData, + } = useQuery(['process-tree', 'process_tree'], () => + http.get(PROCESS_EVENTS_ROUTE, { + query: { + sessionEntityId, + }, + }) ); const sortEvents = (a: ProcessEvent, b: ProcessEvent) => { @@ -106,14 +110,29 @@ export const SessionView = ({ sessionEntityId, height }: SessionViewDeps) => { ); }; - if (!data.length) { - return renderNoData(); - } - - return ( - <> - - {data && ( + const renderProcessTree = () => { + if (isLoading) { + return ( + + + + ); + } + if (isError) { + return ( + Error loading Session View} + body={

There was an error loading the Session View.

} + /> + ); + } + if (data) { + return (
{ onProcessSelected={onProcessSelected} />
- )} + ); + } + }; + + if (!(isLoading || isError || data.length)) { + return renderNoData(); + } + + return ( + <> + + {renderProcessTree()} ); }; 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 cadfcdafe7b4e..9479aa252ede8 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 dd6f02ba24477..4414f4728d865 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); } @@ -160,12 +160,14 @@ class ProcessImpl implements Process { } getDetails() { - const execsForks = this.events.filter(({ event }) => event.action === EventAction.exec || event.action === EventAction.fork); + const execsForks = this.events.filter( + ({ event }) => event.action === EventAction.exec || event.action === EventAction.fork + ); if (execsForks.length === 0) { debugger; } - + return execsForks[execsForks.length - 1]; } @@ -206,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/public/shared_imports.ts b/x-pack/plugins/session_view/public/shared_imports.ts new file mode 100644 index 0000000000000..0a087e1ac36ae --- /dev/null +++ b/x-pack/plugins/session_view/public/shared_imports.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SectionLoading } from '../../../../src/plugins/es_ui_shared/public'; 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 5d53ebf67d096..10c0954f6ad07 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 @@ -52,7 +52,7 @@ export const registerProcessEventsRoute = (router: IRouter) => { 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 6eb1b415f9002..62e92cb98e8b0 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' }], }, }); 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 2906548f1562a..68b6561cd218e 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!', }, }); - }, - ) + } + ); };