Skip to content

Commit

Permalink
[Security solution][Session view] - Add Sessions tab into the Hosts p…
Browse files Browse the repository at this point in the history
…age (#127920)

* add Session Leader Table

* WIP: Session Leader Table

* sessions search strategy

* session viewer component

* add timelineId

* remove session leader table

* cleaning

* cleaning

* updating search strategy

* add space for open in session viewer icon

* add sessionEntityId as key cache

* updating deep links

* updating headers

* adding filterQuery

* adding timeline

* add runtime fields to search strategy

* updating comment

* fixing tests

* removing unecessary intermediate component

* removing intermediary component

* adding tests for session viewer

* remove unnecessary runtime_mappings

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
opauloh and kibanamachine authored Mar 28, 2022
1 parent 065b585 commit 8d117ca
Show file tree
Hide file tree
Showing 34 changed files with 644 additions and 10 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export enum SecurityPageName {
users = 'users',
usersAnomalies = 'users-anomalies',
usersRisk = 'users-risk',
sessions = 'sessions',
}

export const TIMELINES_PATH = '/timelines' as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ export enum TimelineId {
usersPageExternalAlerts = 'users-page-external-alerts',
hostsPageEvents = 'hosts-page-events',
hostsPageExternalAlerts = 'hosts-page-external-alerts',
hostsPageSessions = 'hosts-page-sessions',
detectionsRulesDetailsPage = 'detections-rules-details-page',
detectionsPage = 'detections-page',
networkPageExternalAlerts = 'network-page-external-alerts',
Expand All @@ -332,6 +333,7 @@ export const TimelineIdLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TimelineId.usersPageExternalAlerts),
runtimeTypes.literal(TimelineId.hostsPageEvents),
runtimeTypes.literal(TimelineId.hostsPageExternalAlerts),
runtimeTypes.literal(TimelineId.hostsPageSessions),
runtimeTypes.literal(TimelineId.detectionsRulesDetailsPage),
runtimeTypes.literal(TimelineId.detectionsPage),
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/security_solution/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"taskManager",
"timelines",
"triggersActionsUi",
"uiActions"
"uiActions",
"sessionView"
],
"optionalPlugins": [
"encryptedSavedObjects",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
path: `${HOSTS_PATH}/anomalies`,
isPremium: true,
},
{
id: SecurityPageName.sessions,
title: i18n.translate('xpack.securitySolution.search.hosts.sessions', {
defaultMessage: 'Sessions',
}),
path: `${HOSTS_PATH}/sessions`,
},
],
},
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 { ColumnHeaderOptions, RowRendererId } from '../../../../common/types/timeline';
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';

export const sessionsHeaders: ColumnHeaderOptions[] = [
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.start',
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH,
},
// TODO: Using event.created as an way of getting the end time of the process. (Currently endpoint doesn't populate process.end)
// event.created of a event.action with value of "end" is what we consider that to be the end time of the process
// Current action are: 'start', 'exec', 'end', so we usually have three events per process.
{
columnHeaderType: defaultColumnHeaderType,
id: 'event.created',
display: 'process.end',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.executable',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'user.name',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.interactive',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.pid',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'host.hostname',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.entry_leader.entry_meta.type',
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'process.entry_leader.entry_meta.source.ip',
},
];

export const sessionsDefaultModel: SubsetTimelineModel = {
...timelineDefaults,
columns: sessionsHeaders,
defaultColumns: sessionsHeaders,
excludedRowRendererIds: Object.values(RowRendererId),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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, { useEffect } from 'react';
import { waitFor, render } from '@testing-library/react';
import { TestProviders } from '../../mock';
import { TEST_ID, SessionsView, defaultSessionsFilter } from '.';
import { EntityType, TimelineId } from '../../../../../timelines/common';
import { SessionsComponentsProps } from './types';
import { TimelineModel } from '../../../timelines/store/timeline/model';

jest.mock('../../../common/lib/kibana');

jest.mock('../../components/url_state/normalize_time_range.ts');

const startDate = '2022-03-22T22:10:56.794Z';
const endDate = '2022-03-21T22:10:56.791Z';

const filterQuery =
'{"bool":{"must":[],"filter":[{"match_phrase":{"host.name":{"query":"ubuntu-impish"}}}],"should":[],"must_not":[]}}';

const testProps: SessionsComponentsProps = {
timelineId: TimelineId.hostsPageSessions,
entityType: 'sessions',
pageFilters: [],
startDate,
endDate,
filterQuery,
};

type Props = Partial<TimelineModel> & {
start: string;
end: string;
entityType: EntityType;
};

const TEST_PREFIX = 'security_solution:sessions_viewer:sessions_view';

const callFilters = jest.fn();

// creating a dummy component for testing TGrid to avoid mocking all the implementation details
// but still test if the TGrid will render properly
const SessionsViewerTGrid: React.FC<Props> = ({ columns, start, end, id, filters, entityType }) => {
useEffect(() => {
callFilters(filters);
}, [filters]);

return (
<div>
<div data-test-subj={`${TEST_PREFIX}:entityType`}>{entityType}</div>
<div data-test-subj={`${TEST_PREFIX}:startDate`}>{start}</div>
<div data-test-subj={`${TEST_PREFIX}:endDate`}>{end}</div>
<div data-test-subj={`${TEST_PREFIX}:timelineId`}>{id}</div>
{columns?.map((header) => (
<div key={header.id}>{header.display ?? header.id}</div>
))}
</div>
);
};

jest.mock('../../../../../timelines/public/mock/plugin_mock.tsx', () => {
const originalModule = jest.requireActual('../../../../../timelines/public/mock/plugin_mock.tsx');
return {
...originalModule,
createTGridMocks: () => ({
...originalModule.createTGridMocks,
getTGrid: SessionsViewerTGrid,
}),
};
});

describe('SessionsView', () => {
it('renders the session view', async () => {
const wrapper = render(
<TestProviders>
<SessionsView {...testProps} />
</TestProviders>
);

await waitFor(() => {
expect(wrapper.queryByTestId(TEST_ID)).toBeInTheDocument();
});
});

it('renders correctly against snapshot', async () => {
const { asFragment } = render(
<TestProviders>
<SessionsView {...testProps} />
</TestProviders>
);

await waitFor(() => {
expect(asFragment()).toMatchSnapshot();
});
});

it('passes in the right parameters to TGrid', async () => {
const wrapper = render(
<TestProviders>
<SessionsView {...testProps} />
</TestProviders>
);
await waitFor(() => {
expect(wrapper.getByTestId(`${TEST_PREFIX}:entityType`)).toHaveTextContent('sessions');
expect(wrapper.getByTestId(`${TEST_PREFIX}:startDate`)).toHaveTextContent(startDate);
expect(wrapper.getByTestId(`${TEST_PREFIX}:endDate`)).toHaveTextContent(endDate);
expect(wrapper.getByTestId(`${TEST_PREFIX}:timelineId`)).toHaveTextContent(
'hosts-page-sessions'
);
});
});
it('passes in the right filters to TGrid', async () => {
render(
<TestProviders>
<SessionsView {...testProps} />
</TestProviders>
);
await waitFor(() => {
expect(callFilters).toHaveBeenCalledWith([
{
...defaultSessionsFilter,
query: {
...defaultSessionsFilter.query,
bool: {
...defaultSessionsFilter.query.bool,
filter: defaultSessionsFilter.query.bool.filter.concat(JSON.parse(filterQuery)),
},
},
},
]);
});
});
});
Loading

0 comments on commit 8d117ca

Please sign in to comment.