131034 format detail panel array values (#131349)

* Refactor detail panel copy and host tab data

* Refactor render data for process tab and add test and fix constants

* Update process tab tests

* Fix CI checks

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Jack 2022-05-02 18:21:29 -04:00 committed by GitHub
parent 5a7a243069
commit e3476a168b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 561 additions and 318 deletions

View file

@ -819,8 +819,8 @@ export const mockAlerts: ProcessEvent[] = [
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
@ -1006,8 +1006,8 @@ export const mockAlerts: ProcessEvent[] = [
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
@ -1285,8 +1285,8 @@ export const childProcessMock: Process = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
@ -1370,8 +1370,8 @@ export const processMock: Process = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',

View file

@ -24,8 +24,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
Ext: {
@ -427,8 +427,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
Ext: {
@ -836,8 +836,8 @@ export const sessionViewProcessEventsMock: ProcessEventResults = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809',
mac: '42:01:0a:84:00:32',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
Ext: {

View file

@ -97,8 +97,8 @@ export interface ProcessEventHost {
architecture?: string;
hostname?: string;
id?: string;
ip?: string;
mac?: string;
ip?: string[];
mac?: string[];
name?: string;
os?: {
family?: string;

View file

@ -24,7 +24,9 @@ describe('DetailPanelCopy component', () => {
describe('When DetailPanelCopy is mounted', () => {
it('renders DetailPanelCopy correctly', async () => {
renderResult = mockedContext.render(
<DetailPanelCopy textToCopy={TEST_TEXT_COPY}>{TEST_CHILD}</DetailPanelCopy>
<DetailPanelCopy textToCopy={TEST_TEXT_COPY} tooltipContent={TEST_TEXT_COPY}>
{TEST_CHILD}
</DetailPanelCopy>
);
expect(renderResult.queryByText(TEST_TEXT_COPY)).toBeVisible();

View file

@ -8,12 +8,12 @@ import React, { ReactNode } from 'react';
import { EuiButtonIcon, EuiCopy, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DetailPanelListItem } from '../detail_panel_list_item';
import { dataOrDash } from '../../utils/data_or_dash';
import { useStyles } from './styles';
interface DetailPanelCopyDeps {
children: ReactNode;
textToCopy: string;
tooltipContent: ReactNode;
display?: 'inlineBlock' | 'block' | undefined;
}
@ -28,6 +28,7 @@ interface DetailPanelListItemProps {
export const DetailPanelCopy = ({
children,
textToCopy,
tooltipContent,
display = 'inlineBlock',
}: DetailPanelCopyDeps) => {
const styles = useStyles();
@ -57,7 +58,7 @@ export const DetailPanelCopy = ({
return (
<DetailPanelListItem {...props}>
<EuiToolTip position="top" content={dataOrDash(textToCopy)}>
<EuiToolTip position="top" content={tooltipContent}>
<>{children}</>
</EuiToolTip>
</DetailPanelListItem>

View file

@ -0,0 +1,85 @@
/*
* 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 { ProcessEventHost } from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { getHostData } from './helpers';
const MOCK_HOST_DATA: ProcessEventHost = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
full: 'CentOS 7.9.2009',
kernel: '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021',
name: 'Linux',
platform: 'centos',
version: '7.9.2009',
},
};
describe('detail panel host tab helpers tests', () => {
it('getHostData returns fields with a dash with undefined host', () => {
const result = getHostData(undefined);
expect(result.architecture).toEqual(DASH);
expect(result.hostname).toEqual(DASH);
expect(result.id).toEqual(DASH);
expect(result.ip).toEqual(DASH);
expect(result.mac).toEqual(DASH);
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(DASH);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(DASH);
expect(result.os.name).toEqual(DASH);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(DASH);
});
it('getHostData returns dashes for missing fields', () => {
const result = getHostData({
...MOCK_HOST_DATA,
ip: ['127.0.0.1', '', '', 'fe80::7d39:3147:4d9a:f809'],
name: undefined,
os: {
...MOCK_HOST_DATA.os,
full: undefined,
platform: undefined,
},
});
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(['127.0.0.1', DASH, DASH, 'fe80::7d39:3147:4d9a:f809'].join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
it('getHostData returns all data provided', () => {
const result = getHostData(MOCK_HOST_DATA);
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(MOCK_HOST_DATA.ip?.join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(MOCK_HOST_DATA.name);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(MOCK_HOST_DATA.os?.full);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(MOCK_HOST_DATA.os?.platform);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
});

View file

@ -0,0 +1,49 @@
/*
* 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 { ProcessEventHost } from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { DetailPanelHost } from '../../types';
import { dataOrDash } from '../../utils/data_or_dash';
export const getHostData = (host: ProcessEventHost | undefined): DetailPanelHost => {
const detailPanelHost: DetailPanelHost = {
architecture: DASH,
hostname: DASH,
id: DASH,
ip: DASH,
mac: DASH,
name: DASH,
os: {
family: DASH,
full: DASH,
kernel: DASH,
name: DASH,
platform: DASH,
version: DASH,
},
};
if (!host) {
return detailPanelHost;
}
detailPanelHost.hostname = dataOrDash(host.hostname).toString();
detailPanelHost.id = dataOrDash(host.id).toString();
detailPanelHost.ip = host.ip?.map?.((ip) => dataOrDash(ip)).join(', ') ?? DASH;
detailPanelHost.mac = host.mac?.map?.((mac) => dataOrDash(mac)).join(', ') ?? DASH;
detailPanelHost.name = dataOrDash(host.name).toString();
detailPanelHost.architecture = dataOrDash(host.architecture).toString();
detailPanelHost.os.family = dataOrDash(host.os?.family).toString();
detailPanelHost.os.full = dataOrDash(host.os?.full).toString();
detailPanelHost.os.kernel = dataOrDash(host.os?.kernel).toString();
detailPanelHost.os.name = dataOrDash(host.os?.name).toString();
detailPanelHost.os.platform = dataOrDash(host.os?.platform).toString();
detailPanelHost.os.version = dataOrDash(host.os?.version).toString();
return detailPanelHost;
};

View file

@ -13,8 +13,8 @@ import { DetailPanelHostTab } from '.';
const TEST_ARCHITECTURE = 'x86_64';
const TEST_HOSTNAME = 'host-james-fleet-714-2';
const TEST_ID = '48c1b3f1ac5da4e0057fc9f60f4d1d5d';
const TEST_IP = '127.0.0.1,::1,10.132.0.50,fe80::7d39:3147:4d9a:f809';
const TEST_MAC = '42:01:0a:84:00:32';
const TEST_IP = ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'];
const TEST_MAC = ['42:01:0a:84:00:32'];
const TEST_NAME = 'name-james-fleet-714-2';
const TEST_OS_FAMILY = 'family-centos';
const TEST_OS_FULL = 'full-CentOS 7.9.2009';
@ -62,8 +62,8 @@ describe('DetailPanelHostTab component', () => {
expect(renderResult.queryByText(TEST_ARCHITECTURE)).toBeVisible();
expect(renderResult.queryByText(TEST_HOSTNAME)).toBeVisible();
expect(renderResult.queryByText(TEST_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_IP)).toBeVisible();
expect(renderResult.queryByText(TEST_MAC)).toBeVisible();
expect(renderResult.queryByText(TEST_IP.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_MAC.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_NAME)).toBeVisible();
// expand host os accordion

View file

@ -4,15 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import React, { useMemo } from 'react';
import { EuiTextColor } from '@elastic/eui';
import { ProcessEventHost } from '../../../common/types/process_tree';
import { DetailPanelAccordion } from '../detail_panel_accordion';
import { DetailPanelCopy } from '../detail_panel_copy';
import { DetailPanelDescriptionList } from '../detail_panel_description_list';
import { DetailPanelListItem } from '../detail_panel_list_item';
import { dataOrDash } from '../../utils/data_or_dash';
import { useStyles } from '../detail_panel_process_tab/styles';
import { getHostData } from './helpers';
interface DetailPanelHostTabDeps {
processHost?: ProcessEventHost;
@ -23,6 +23,7 @@ interface DetailPanelHostTabDeps {
*/
export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
const styles = useStyles();
const hostData = useMemo(() => getHostData(processHost), [processHost]);
return (
<>
@ -31,9 +32,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>hostname</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.hostname: "${dataOrDash(processHost?.hostname)}"`}>
<DetailPanelCopy
textToCopy={`host.hostname: "${hostData.hostname}"`}
tooltipContent={hostData.hostname}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.hostname)}
{hostData.hostname}
</EuiTextColor>
</DetailPanelCopy>
),
@ -41,9 +45,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>id</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.id: "${dataOrDash(processHost?.id)}"`}>
<DetailPanelCopy
textToCopy={`host.id: "${hostData.id}"`}
tooltipContent={hostData.id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.id)}
{hostData.id}
</EuiTextColor>
</DetailPanelCopy>
),
@ -51,9 +58,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>ip</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.ip: "${dataOrDash(processHost?.ip)}"`}>
<DetailPanelCopy
textToCopy={`host.ip: "${hostData.ip}"`}
tooltipContent={hostData.ip}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.ip)}
{hostData.ip}
</EuiTextColor>
</DetailPanelCopy>
),
@ -61,9 +71,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>mac</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.mac: "${dataOrDash(processHost?.mac)}"`}>
<DetailPanelCopy
textToCopy={`host.mac: "${hostData.mac}"`}
tooltipContent={hostData.mac}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.mac)}
{hostData.mac}
</EuiTextColor>
</DetailPanelCopy>
),
@ -71,9 +84,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>name</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.name: "${dataOrDash(processHost?.name)}"`}>
<DetailPanelCopy
textToCopy={`host.name: "${hostData.name}"`}
tooltipContent={hostData.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.name)}
{hostData.name}
</EuiTextColor>
</DetailPanelCopy>
),
@ -88,10 +104,11 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
title: <DetailPanelListItem>architecture</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.architecture: "${dataOrDash(processHost?.architecture)}"`}
textToCopy={`host.architecture: "${hostData.architecture}"`}
tooltipContent={hostData.architecture}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.architecture)}
{hostData.architecture}
</EuiTextColor>
</DetailPanelCopy>
),
@ -100,10 +117,11 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
title: <DetailPanelListItem>os.family</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.family: "${dataOrDash(processHost?.os?.family)}"`}
textToCopy={`host.os.family: "${hostData.os.family}"`}
tooltipContent={hostData.os.family}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.family)}
{hostData.os.family}
</EuiTextColor>
</DetailPanelCopy>
),
@ -111,9 +129,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>os.full</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.os.full: "${dataOrDash(processHost?.os?.full)}"`}>
<DetailPanelCopy
textToCopy={`host.os.full: "${hostData.os.full}"`}
tooltipContent={hostData.os.full}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.full)}
{hostData.os.full}
</EuiTextColor>
</DetailPanelCopy>
),
@ -122,10 +143,11 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
title: <DetailPanelListItem>os.kernel</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.kernel: "${dataOrDash(processHost?.os?.kernel)}"`}
textToCopy={`host.os.kernel: "${hostData.os.kernel}"`}
tooltipContent={hostData.os.kernel}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.kernel)}
{hostData.os.kernel}
</EuiTextColor>
</DetailPanelCopy>
),
@ -133,9 +155,12 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
{
title: <DetailPanelListItem>os.name</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`host.os.name: "${dataOrDash(processHost?.os?.name)}"`}>
<DetailPanelCopy
textToCopy={`host.os.name: "${hostData.os.name}"`}
tooltipContent={hostData.os.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.name)}
{hostData.os.name}
</EuiTextColor>
</DetailPanelCopy>
),
@ -144,10 +169,11 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
title: <DetailPanelListItem>os.platform</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.platform: "${dataOrDash(processHost?.os?.platform)}"`}
textToCopy={`host.os.platform: "${hostData.os.platform}"`}
tooltipContent={hostData.os.platform}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.platform)}
{hostData.os.platform}
</EuiTextColor>
</DetailPanelCopy>
),
@ -156,10 +182,11 @@ export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
title: <DetailPanelListItem>os.version</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.version: "${dataOrDash(processHost?.os?.version)}"`}
textToCopy={`host.os.version: "${hostData.os.version}"`}
tooltipContent={hostData.os.version}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(processHost?.os?.version)}
{hostData.os.version}
</EuiTextColor>
</DetailPanelCopy>
),

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { DASH } from '../../constants';
import { getProcessExecutableCopyText, formatProcessArgs, getIsInterativeString } from './helpers';
describe('detail panel process tab helpers tests', () => {
@ -36,7 +37,7 @@ describe('detail panel process tab helpers tests', () => {
it("formatProcessArgs returns '-' when given empty args array", () => {
const result = formatProcessArgs([]);
expect(result).toEqual('-');
expect(result).toEqual(DASH);
});
it('formatProcessArgs returns formatted args string', () => {

View file

@ -5,7 +5,29 @@
* 2.0.
*/
import { Teletype } from '../../../common/types/process_tree';
import { EventAction, Process, ProcessFields, Teletype } from '../../../common/types/process_tree';
import { DetailPanelProcess, DetailPanelProcessLeader } from '../../types';
import { DASH } from '../../constants';
import { dataOrDash } from '../../utils/data_or_dash';
const FILTER_FORKS_EXECS = [EventAction.fork, EventAction.exec];
const DEFAULT_PROCESS_DATA: DetailPanelProcessLeader = {
id: DASH,
name: DASH,
start: DASH,
end: DASH,
exitCode: DASH,
userName: DASH,
groupName: DASH,
workingDirectory: DASH,
interactive: DASH,
args: DASH,
pid: DASH,
entryMetaType: DASH,
entryMetaSourceIp: DASH,
executable: [[DASH]],
};
/**
* Serialize an array of executable tuples to a copyable text.
@ -32,11 +54,11 @@ export const getProcessExecutableCopyText = (executable: string[][]): string =>
/**
* Format an array of args for display.
*
* @param {String[]} args
* @param {String[] | undefined} args
* @return {String} formatted string of process args
*/
export const formatProcessArgs = (args: string[]): string =>
args.length ? `[${args.map((arg) => `'${arg}'`).join(', ')}]` : '-';
export const formatProcessArgs = (args: string[] | undefined): string =>
args && args.length && args.map ? `[${args.map((arg) => `'${arg}'`).join(', ')}]` : DASH;
/**
* Get isInteractive boolean string from tty.
@ -46,3 +68,86 @@ export const formatProcessArgs = (args: string[]): string =>
*/
export const getIsInterativeString = (tty: Teletype | undefined): string =>
!!tty ? 'True' : 'False';
const getDetailPanelProcessLeader = (
leader: ProcessFields | undefined
): DetailPanelProcessLeader => ({
...leader,
id: leader?.entity_id ?? DEFAULT_PROCESS_DATA.id,
name: leader?.name ?? DEFAULT_PROCESS_DATA.name,
start: leader?.start ?? DEFAULT_PROCESS_DATA.start,
end: leader?.end ?? DEFAULT_PROCESS_DATA.end,
exitCode: leader?.exit_code?.toString() ?? DEFAULT_PROCESS_DATA.exitCode,
interactive: getIsInterativeString(leader?.tty),
userName: leader?.user?.name ?? DEFAULT_PROCESS_DATA.userName,
groupName: leader?.group?.name ?? DEFAULT_PROCESS_DATA.groupName,
workingDirectory: leader?.working_directory ?? DEFAULT_PROCESS_DATA.workingDirectory,
args: formatProcessArgs(leader?.args) ?? DEFAULT_PROCESS_DATA.args,
pid: leader?.pid?.toString() ?? DEFAULT_PROCESS_DATA.pid,
// TODO: get the event action of leader
executable: leader?.executable ? [[leader?.executable]] : DEFAULT_PROCESS_DATA.executable,
entryMetaType: leader?.entry_meta?.type ?? DEFAULT_PROCESS_DATA.entryMetaType,
entryMetaSourceIp: leader?.entry_meta?.source?.ip ?? DEFAULT_PROCESS_DATA.entryMetaSourceIp,
});
export const getDetailPanelProcess = (process: Process | null): DetailPanelProcess => {
const processData = {
id: DEFAULT_PROCESS_DATA.id,
start: DEFAULT_PROCESS_DATA.start,
end: DEFAULT_PROCESS_DATA.end,
exitCode: DEFAULT_PROCESS_DATA.exitCode,
interactive: DEFAULT_PROCESS_DATA.interactive,
userName: DEFAULT_PROCESS_DATA.userName,
groupName: DEFAULT_PROCESS_DATA.groupName,
args: DEFAULT_PROCESS_DATA.args,
pid: DEFAULT_PROCESS_DATA.pid,
executable: DEFAULT_PROCESS_DATA.executable,
workingDirectory: DEFAULT_PROCESS_DATA.workingDirectory,
entryLeader: DEFAULT_PROCESS_DATA,
sessionLeader: DEFAULT_PROCESS_DATA,
groupLeader: DEFAULT_PROCESS_DATA,
parent: DEFAULT_PROCESS_DATA,
} as DetailPanelProcess;
if (!process) {
return processData;
}
const details = process.getDetails();
processData.id = `${dataOrDash(process.id)}`;
processData.start = `${dataOrDash(details.process?.start)}`;
processData.end = `${dataOrDash(process.getEndTime())}`;
processData.exitCode = `${dataOrDash(details.process?.exit_code)}`;
processData.interactive = getIsInterativeString(details.process?.tty);
processData.userName = `${dataOrDash(details.process?.user?.name)}`;
processData.groupName = `${dataOrDash(details.process?.group?.name)}`;
processData.pid = `${dataOrDash(details.process?.pid)}`;
processData.workingDirectory = `${dataOrDash(details.process?.working_directory)}`;
if (details.process?.args) {
processData.args = formatProcessArgs(details.process.args);
}
// we grab the executable from each process lifecycle event to give an indication
// of the processes journey. Processes can sometimes exec multiple times, so it's good
// information to have.
processData.executable = [];
process.events.forEach((event) => {
if (
event.process?.executable &&
event.event?.action &&
FILTER_FORKS_EXECS.includes(event.event.action)
) {
processData.executable.push([event.process.executable, `(${event.event.action})`]);
}
});
if (!processData.executable.length) {
processData.executable = DEFAULT_PROCESS_DATA.executable;
}
processData.entryLeader = getDetailPanelProcessLeader(details?.process?.entry_leader);
processData.sessionLeader = getDetailPanelProcessLeader(details?.process?.session_leader);
processData.groupLeader = getDetailPanelProcessLeader(details?.process?.group_leader);
processData.parent = getDetailPanelProcessLeader(details?.process?.parent);
return processData;
};

View file

@ -7,60 +7,15 @@
import React from 'react';
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
import { DetailPanelProcess, DetailPanelProcessLeader } from '../../types';
import { sessionViewBasicProcessMock } from '../../../common/mocks/constants/session_view_process.mock';
import { DetailPanelProcessTab } from '.';
const getLeaderDetail = (leader: string): DetailPanelProcessLeader => ({
id: `${leader}-id`,
name: `${leader}-name`,
start: new Date('2022-02-24').toISOString(),
entryMetaType: 'sshd',
working_directory: '/home/jack',
tty: {
char_device: {
major: 8,
minor: 1,
},
},
args: ['ls'],
userName: `${leader}-jack`,
groupName: `${leader}-jack-group`,
pid: 1234,
entryMetaSourceIp: '10.132.0.50',
executable: '/usr/bin/bash',
});
const TEST_PROCESS_DETAIL: DetailPanelProcess = {
id: 'process-id',
start: new Date('2022-02-22').toISOString(),
end: new Date('2022-02-23').toISOString(),
exit_code: 137,
userName: 'process-jack',
groupName: 'process-jack-group',
args: ['vi', 'test.txt'],
executable: [
['test-executable-cmd', '(fork)'],
['test-executable-cmd', '(exec)'],
['test-executable-cmd', '(end)'],
],
working_directory: '/home/jack',
tty: {
char_device: {
major: 8,
minor: 1,
},
},
pid: 1233,
entryLeader: getLeaderDetail('entryLeader'),
sessionLeader: getLeaderDetail('sessionLeader'),
groupLeader: getLeaderDetail('groupLeader'),
parent: getLeaderDetail('parent'),
};
describe('DetailPanelProcessTab component', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let mockedContext: AppContextTestRender;
const processDetail = sessionViewBasicProcessMock.getDetails();
const MOCK_PROCESS_END = '2021-11-24T15:25:04.210Z';
beforeEach(() => {
mockedContext = createAppRootMockRenderer();
@ -69,21 +24,29 @@ describe('DetailPanelProcessTab component', () => {
describe('When DetailPanelProcessTab is mounted', () => {
it('renders DetailPanelProcessTab correctly', async () => {
renderResult = mockedContext.render(
<DetailPanelProcessTab processDetail={TEST_PROCESS_DETAIL} />
<DetailPanelProcessTab
selectedProcess={{
...sessionViewBasicProcessMock,
getEndTime: () => MOCK_PROCESS_END,
}}
/>
);
// Process detail rendered correctly
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.id)).toBeVisible();
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.start)).toBeVisible();
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.end)).toBeVisible();
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.exit_code!)).toBeVisible();
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.userName)).toBeVisible();
expect(renderResult.queryByText(`['vi', 'test.txt']`)).toBeVisible();
expect(renderResult.queryAllByText('test-executable-cmd')).toHaveLength(3);
expect(renderResult.queryByText(processDetail!.process!.entity_id!)).toBeVisible();
expect(renderResult.queryByText(processDetail!.process!.start!)).toBeVisible();
expect(renderResult.queryByText(MOCK_PROCESS_END)).toBeVisible();
expect(renderResult.queryByText(processDetail!.process!.exit_code!)).toBeVisible();
expect(renderResult.queryAllByText(processDetail!.process!.user!.name!)).toHaveLength(10);
expect(renderResult.queryAllByText(processDetail!.process!.working_directory!)).toHaveLength(
5
);
expect(renderResult.queryByText(`['bash']`)).toBeVisible();
expect(renderResult.queryAllByText('/usr/bin/bash')).toHaveLength(5);
expect(renderResult.queryByText('/usr/bin/vi')).toBeVisible();
expect(renderResult.queryByText('(fork)')).toBeVisible();
expect(renderResult.queryByText('(exec)')).toBeVisible();
expect(renderResult.queryByText('(end)')).toBeVisible();
expect(renderResult.queryByText(TEST_PROCESS_DETAIL.pid!)).toBeVisible();
expect(renderResult.queryByText(processDetail!.process!.pid!)).toBeVisible();
// Process tab accordions rendered correctly
// TODO: revert back when we have jump to leaders button working

View file

@ -4,20 +4,20 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { ReactNode } from 'react';
import React, { ReactNode, useCallback, useMemo } from 'react';
import { EuiTextColor } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DetailPanelProcess } from '../../types';
import { Process } from '../../../common/types/process_tree';
import { DetailPanelAccordion } from '../detail_panel_accordion';
import { DetailPanelCopy } from '../detail_panel_copy';
import { DetailPanelDescriptionList } from '../detail_panel_description_list';
import { DetailPanelListItem } from '../detail_panel_list_item';
import { dataOrDash } from '../../utils/data_or_dash';
import { getProcessExecutableCopyText, formatProcessArgs, getIsInterativeString } from './helpers';
import { getProcessExecutableCopyText, getDetailPanelProcess } from './helpers';
import { useStyles } from './styles';
interface DetailPanelProcessTabDeps {
processDetail: DetailPanelProcess;
selectedProcess: Process | null;
}
type ListItems = Array<{
@ -70,8 +70,30 @@ const LEADER_FIELD_PREFIX = [
/**
* Detail panel in the session view.
*/
export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDeps) => {
export const DetailPanelProcessTab = ({ selectedProcess }: DetailPanelProcessTabDeps) => {
const styles = useStyles();
const processDetail = useMemo(() => getDetailPanelProcess(selectedProcess), [selectedProcess]);
const renderExecs = useCallback(
(executable: string[][]) =>
executable.map((execTuple, idx) => {
const [exec, eventAction] = execTuple;
return (
<div key={`executable-${idx}`} css={styles.ellipsis}>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{exec}
</EuiTextColor>
{eventAction && (
<EuiTextColor color="subdued" css={styles.executableAction}>
{eventAction}
</EuiTextColor>
)}
</div>
);
}),
[styles.descriptionSemibold, styles.ellipsis, styles.executableAction]
);
const leaderListItems = [
processDetail.entryLeader,
processDetail.sessionLeader,
@ -82,27 +104,29 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
id,
start,
end,
exit_code: exitCode,
exitCode,
entryMetaType,
tty,
working_directory: workingDirectory,
interactive,
workingDirectory,
args,
executable,
pid,
userName,
groupName,
entryMetaSourceIp,
} = leader;
const leaderArgs = formatProcessArgs(args);
const isLeaderInteractive = getIsInterativeString(tty);
const leaderExecutableText = getProcessExecutableCopyText(executable);
const listItems: ListItems = [
{
title: <DetailPanelListItem>entity_id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entity_id: "${dataOrDash(id)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entity_id: "${id}"`}
tooltipContent={id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(id)}
{id}
</EuiTextColor>
</DetailPanelCopy>
),
@ -110,8 +134,22 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>args</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${LEADER_FIELD_PREFIX[idx]}.args: "${leaderArgs}"`}>
{leaderArgs}
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.args: "${args}"`}
tooltipContent={args}
>
{args}
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>executable</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.executable: "${leaderExecutableText}"`}
tooltipContent={leaderExecutableText}
>
{renderExecs(executable)}
</DetailPanelCopy>
),
},
@ -119,10 +157,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>interactive</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.interactive: "${isLeaderInteractive}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.interactive: "${interactive}"`}
tooltipContent={interactive}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{isLeaderInteractive}
{interactive}
</EuiTextColor>
</DetailPanelCopy>
),
@ -131,12 +170,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>working_directory</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.working_directory: "${dataOrDash(
workingDirectory
)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.working_directory: "${workingDirectory}"`}
tooltipContent={workingDirectory}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(workingDirectory)}
{workingDirectory}
</EuiTextColor>
</DetailPanelCopy>
),
@ -144,9 +182,12 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>pid</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${LEADER_FIELD_PREFIX[idx]}.pid: "${dataOrDash(pid)}"`}>
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.pid: "${pid}"`}
tooltipContent={pid}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(pid)}
{pid}
</EuiTextColor>
</DetailPanelCopy>
),
@ -154,16 +195,22 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>start</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${LEADER_FIELD_PREFIX[idx]}.start: "${dataOrDash(start)}"`}>
{dataOrDash(start)}
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.start: "${start}"`}
tooltipContent={start}
>
{start}
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>end</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${LEADER_FIELD_PREFIX[idx]}.end: "${dataOrDash(end)}"`}>
{dataOrDash(end)}
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.end: "${end}"`}
tooltipContent={end}
>
{end}
</DetailPanelCopy>
),
},
@ -171,10 +218,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>exit_code</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.exit_code: "${dataOrDash(exitCode)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.exit_code: "${exitCode}"`}
tooltipContent={exitCode}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(exitCode)}
{exitCode}
</EuiTextColor>
</DetailPanelCopy>
),
@ -183,9 +231,10 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>user.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.user.name: "${dataOrDash(userName)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.user.name: "${userName}"`}
tooltipContent={userName}
>
{dataOrDash(userName)}
{userName}
</DetailPanelCopy>
),
},
@ -193,9 +242,10 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>group.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.group.name: "${dataOrDash(groupName)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.group.name: "${groupName}"`}
tooltipContent={groupName}
>
{dataOrDash(groupName)}
{groupName}
</DetailPanelCopy>
),
},
@ -207,12 +257,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>entry_meta.type</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entry_meta.type: "${dataOrDash(
entryMetaType
)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entry_meta.type: "${entryMetaType}"`}
tooltipContent={entryMetaType}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(entryMetaType)}
{entryMetaType}
</EuiTextColor>
</DetailPanelCopy>
),
@ -221,9 +270,8 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>entry_meta.source.ip</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entry_meta.source.ip: "${dataOrDash(
entryMetaSourceIp
)}"`}
textToCopy={`${LEADER_FIELD_PREFIX[idx]}.entry_meta.source.ip: "${entryMetaSourceIp}"`}
tooltipContent={entryMetaSourceIp}
>
{dataOrDash(entryMetaSourceIp)}
</DetailPanelCopy>
@ -244,17 +292,15 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
start,
end,
executable,
exit_code: exitCode,
exitCode,
pid,
working_directory: workingDirectory,
tty,
workingDirectory,
interactive,
userName,
groupName,
args,
} = processDetail;
const isInteractive = getIsInterativeString(tty);
const processArgs = formatProcessArgs(args);
const executableText = getProcessExecutableCopyText(executable);
return (
<>
@ -264,10 +310,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>entity_id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.entity_id: "${dataOrDash(id)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.entity_id: "${id}"`}
tooltipContent={id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(id)}
{id}
</EuiTextColor>
</DetailPanelCopy>
),
@ -275,8 +322,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>args</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${PROCESS_FIELD_PREFIX}.args: "${processArgs}"`}>
{processArgs}
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.args: "${args}"`}
tooltipContent={args}
>
{args}
</DetailPanelCopy>
),
},
@ -284,24 +334,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>executable</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.executable: "${getProcessExecutableCopyText(
executable
)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.executable: "${executableText}"`}
tooltipContent={executableText}
display="block"
>
{executable.map((execTuple, idx) => {
const [exec, eventAction] = execTuple;
return (
<div key={`executable-${idx}`} css={styles.ellipsis}>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(exec)}
</EuiTextColor>
<EuiTextColor color="subdued" css={styles.executableAction}>
{eventAction}
</EuiTextColor>
</div>
);
})}
{renderExecs(executable)}
</DetailPanelCopy>
),
},
@ -309,10 +346,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>interactive</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.interactive: "${isInteractive}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.interactive: "${interactive}"`}
tooltipContent={interactive}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{isInteractive}
{interactive}
</EuiTextColor>
</DetailPanelCopy>
),
@ -321,12 +359,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>working_directory</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.working_directory: "${dataOrDash(
workingDirectory
)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.working_directory: "${workingDirectory}"`}
tooltipContent={workingDirectory}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(workingDirectory)}
{workingDirectory}
</EuiTextColor>
</DetailPanelCopy>
),
@ -334,9 +371,12 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>pid</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${PROCESS_FIELD_PREFIX}.pid: "${dataOrDash(pid)}"`}>
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.pid: "${pid}"`}
tooltipContent={pid}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(pid)}
{pid}
</EuiTextColor>
</DetailPanelCopy>
),
@ -344,16 +384,22 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
{
title: <DetailPanelListItem>start</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${PROCESS_FIELD_PREFIX}.start: "${dataOrDash(start)}"`}>
{dataOrDash(start)}
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.start: "${start}"`}
tooltipContent={start}
>
{start}
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>end</DetailPanelListItem>,
description: (
<DetailPanelCopy textToCopy={`${PROCESS_FIELD_PREFIX}.end: "${dataOrDash(end)}"`}>
{dataOrDash(end)}
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.end: "${end}"`}
tooltipContent={end}
>
{end}
</DetailPanelCopy>
),
},
@ -361,10 +407,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>exit_code</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.exit_code: "${dataOrDash(exitCode)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.exit_code: "${exitCode}"`}
tooltipContent={exitCode}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{dataOrDash(exitCode)}
{exitCode}
</EuiTextColor>
</DetailPanelCopy>
),
@ -373,9 +420,10 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>user.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.user.name: "${dataOrDash(userName)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.user.name: "${userName}"`}
tooltipContent={userName}
>
{dataOrDash(userName)}
{userName}
</DetailPanelCopy>
),
},
@ -383,9 +431,10 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe
title: <DetailPanelListItem>group.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`${PROCESS_FIELD_PREFIX}.group.name: "${dataOrDash(groupName)}"`}
textToCopy={`${PROCESS_FIELD_PREFIX}.group.name: "${groupName}"`}
tooltipContent={groupName}
>
{dataOrDash(groupName)}
{groupName}
</DetailPanelCopy>
),
},

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { getSelectedTabContent } from './helpers';
import { EuiTabProps } from '../../types';
const TABS: EuiTabProps[] = [
{
id: '1',
name: 'Process',
content: 'process content',
},
{
id: '2',
name: 'Host',
content: 'host content',
},
{
id: '3',
name: 'Alert',
content: 'alert content',
},
];
describe('session view detail panel helpers tests', () => {
it('getSelectedTabContent works', () => {
const result = getSelectedTabContent(TABS, '1');
expect(result).toBe(TABS[0].content);
});
it('getSelectedTabContent returns null if tab id not found', () => {
const result = getSelectedTabContent(TABS, 'process');
expect(result).toBeNull();
});
});

View file

@ -4,108 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EventAction, Process, ProcessFields } from '../../../common/types/process_tree';
import { DetailPanelProcess, EuiTabProps } from '../../types';
const FILTER_FORKS_EXECS = [EventAction.fork, EventAction.exec];
const DEFAULT_PROCESS_DATA = {
id: '',
name: '',
start: '',
end: '',
userName: '',
groupName: '',
working_directory: '',
args: [],
entryMetaType: '',
entryMetaSourceIp: '',
executable: '',
};
const getDetailPanelProcessLeader = (leader: ProcessFields | undefined) => ({
...leader,
name: leader?.name ?? DEFAULT_PROCESS_DATA.name,
start: leader?.start ?? DEFAULT_PROCESS_DATA.start,
working_directory: leader?.working_directory ?? DEFAULT_PROCESS_DATA.working_directory,
args: leader?.args ?? DEFAULT_PROCESS_DATA.args,
executable: leader?.executable ?? DEFAULT_PROCESS_DATA.executable,
id: leader?.entity_id ?? DEFAULT_PROCESS_DATA.id,
entryMetaType: leader?.entry_meta?.type ?? DEFAULT_PROCESS_DATA.entryMetaType,
userName: leader?.user?.name ?? DEFAULT_PROCESS_DATA.userName,
groupName: leader?.group?.name ?? DEFAULT_PROCESS_DATA.groupName,
entryMetaSourceIp: leader?.entry_meta?.source?.ip ?? DEFAULT_PROCESS_DATA.entryMetaSourceIp,
});
export const getDetailPanelProcess = (process: Process | null) => {
const processData = {} as DetailPanelProcess;
if (!process) {
return {
id: DEFAULT_PROCESS_DATA.id,
start: DEFAULT_PROCESS_DATA.start,
end: DEFAULT_PROCESS_DATA.end,
userName: DEFAULT_PROCESS_DATA.userName,
groupName: DEFAULT_PROCESS_DATA.groupName,
args: DEFAULT_PROCESS_DATA.args,
executable: [],
working_directory: DEFAULT_PROCESS_DATA.working_directory,
entryLeader: DEFAULT_PROCESS_DATA,
sessionLeader: DEFAULT_PROCESS_DATA,
groupLeader: DEFAULT_PROCESS_DATA,
parent: DEFAULT_PROCESS_DATA,
};
}
const details = process.getDetails();
processData.id = process.id;
processData.start = details.process?.start ?? '';
processData.args = [];
processData.executable = [];
if (!processData.userName) {
processData.userName = details.process?.user?.name ?? '';
}
if (!processData.groupName) {
processData.groupName = details.process?.group?.name ?? '';
}
if (!processData.pid) {
processData.pid = details.process?.pid;
}
if (!processData.working_directory) {
processData.working_directory = details.process?.working_directory ?? '';
}
if (!processData.tty) {
processData.tty = details.process?.tty;
}
if (details.process?.args && details.process.args.length > 0) {
processData.args = details.process.args;
}
if (details.process?.exit_code !== undefined) {
processData.exit_code = details.process.exit_code;
}
// we grab the executable from each process lifecycle event to give an indication
// of the processes journey. Processes can sometimes exec multiple times, so it's good
// information to have.
process.events.forEach((event) => {
if (
event.process?.executable &&
event.event?.action &&
FILTER_FORKS_EXECS.includes(event.event.action)
) {
processData.executable.push([event.process.executable, `(${event.event.action})`]);
}
});
processData.end = process.getEndTime();
processData.entryLeader = getDetailPanelProcessLeader(details?.process?.entry_leader);
processData.sessionLeader = getDetailPanelProcessLeader(details?.process?.session_leader);
processData.groupLeader = getDetailPanelProcessLeader(details?.process?.group_leader);
processData.parent = getDetailPanelProcessLeader(details?.process?.parent);
return processData;
};
import { EuiTabProps } from '../../types';
export const getSelectedTabContent = (tabs: EuiTabProps[], selectedTabId: string) => {
const selectedTab = tabs.find((tab) => tab.id === selectedTabId);

View file

@ -9,7 +9,7 @@ import { EuiTabs, EuiTab, EuiNotificationBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiTabProps } from '../../types';
import { Process, ProcessEvent } from '../../../common/types/process_tree';
import { getDetailPanelProcess, getSelectedTabContent } from './helpers';
import { getSelectedTabContent } from './helpers';
import { DetailPanelProcessTab } from '../detail_panel_process_tab';
import { DetailPanelHostTab } from '../detail_panel_host_tab';
import { useStyles } from './styles';
@ -35,7 +35,6 @@ export const SessionViewDetailPanel = ({
onShowAlertDetails,
}: SessionViewDetailPanelDeps) => {
const [selectedTabId, setSelectedTabId] = useState('process');
const processDetail = useMemo(() => getDetailPanelProcess(selectedProcess), [selectedProcess]);
const alertsCount = useMemo(() => {
if (!alerts) {
@ -54,7 +53,7 @@ export const SessionViewDetailPanel = ({
name: i18n.translate('xpack.sessionView.detailsPanel.process', {
defaultMessage: 'Process',
}),
content: <DetailPanelProcessTab processDetail={processDetail} />,
content: <DetailPanelProcessTab selectedProcess={selectedProcess} />,
},
{
id: 'host',
@ -85,12 +84,11 @@ export const SessionViewDetailPanel = ({
];
}, [
alerts,
selectedProcess,
alertsCount,
processDetail,
selectedProcess?.events,
onJumpToEvent,
onShowAlertDetails,
investigatedAlertId,
onJumpToEvent,
]);
const onSelectedTabChanged = useCallback((id: string) => {

View file

@ -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 const DASH = '-';

View file

@ -6,7 +6,6 @@
*/
import { ReactNode } from 'react';
import { CoreStart } from '@kbn/core/public';
import { Teletype } from '../common/types/process_tree';
export type SessionViewServices = CoreStart;
@ -43,14 +42,14 @@ export interface DetailPanelProcess {
id: string;
start: string;
end: string;
exit_code?: number;
exitCode: string;
userName: string;
groupName: string;
args: string[];
args: string;
executable: string[][];
working_directory: string;
tty?: Teletype;
pid?: number;
workingDirectory: string;
interactive: string;
pid: string;
entryLeader: DetailPanelProcessLeader;
sessionLeader: DetailPanelProcessLeader;
groupLeader: DetailPanelProcessLeader;
@ -61,17 +60,34 @@ export interface DetailPanelProcessLeader {
id: string;
name: string;
start: string;
end?: string;
exit_code?: number;
end: string;
exitCode: string;
userName: string;
groupName: string;
working_directory: string;
tty?: Teletype;
args: string[];
pid?: number;
workingDirectory: string;
interactive: string;
args: string;
pid: string;
entryMetaType: string;
entryMetaSourceIp: string;
executable: string;
executable: string[][];
}
export interface DetailPanelHost {
architecture: string;
hostname: string;
id: string;
ip: string;
mac: string;
name: string;
os: {
family: string;
full: string;
kernel: string;
name: string;
platform: string;
version: string;
};
}
export interface SessionViewStart {

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import { DASH } from '../constants';
import { dataOrDash } from './data_or_dash';
const TEST_STRING = '123';
const TEST_NUMBER = 123;
const DASH = '-';
describe('dataOrDash(data)', () => {
it('works for a valid string', () => {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import { DASH } from '../constants';
/**
* Returns a dash ('-') if data is undefined, and empty string, or a NaN.
*
@ -15,7 +17,7 @@
*/
export const dataOrDash = (data: string | number | undefined): string | number => {
if (data === undefined || data === '' || (typeof data === 'number' && isNaN(data))) {
return '-';
return DASH;
}
return data;