Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] Use session view plugin to render session viewer in alerts, events and timeline #127520

Merged
merged 29 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
af077cd
Cherry-pick security/timelines changes
kqualters-elastic Mar 10, 2022
0abc22b
Changes to make session_view work with generated data
kqualters-elastic Mar 10, 2022
821c0c2
Demo tweaks
kqualters-elastic Mar 17, 2022
21889d3
Merge branch 'main' into session-view-updated
kqualters-elastic Mar 21, 2022
86aa348
Cherry-pick security/timelines changes
kqualters-elastic Mar 10, 2022
c3e7708
Changes to make session_view work with generated data
kqualters-elastic Mar 10, 2022
d7cbe6a
create detail panel hook for session view
michaelolo24 Mar 23, 2022
dd9452c
add some tests
michaelolo24 Mar 23, 2022
19ad8df
Merge branch 'main' of github.com:elastic/kibana into session-view-up…
kqualters-elastic Mar 24, 2022
fa00f8b
Changes to make session_view work with generated data
kqualters-elastic Mar 10, 2022
b6d2b49
Merge branch 'safest-session-view-flyout' of github.com:michaelolo24/…
michaelolo24 Mar 24, 2022
c811902
Merge branch 'session-view-updated' into safest-session-view-flyout
michaelolo24 Mar 24, 2022
8e9c818
remove duplicate sessionViewId prop
michaelolo24 Mar 25, 2022
a8d9d13
Update with main
kqualters-elastic Mar 25, 2022
608f7c8
Merge remote-tracking branch 'upstream/main' into session-view-updated
kqualters-elastic Mar 25, 2022
638bcfe
Merge branch 'session-view-updated' into integrate-session-view-with-…
michaelolo24 Mar 25, 2022
9b18ccd
Merge pull request #3 from michaelolo24/integrate-session-view-with-d…
kqualters-elastic Mar 25, 2022
ebe4d56
Update tests and types, improve styles around full screen
kqualters-elastic Mar 29, 2022
63d2a5d
Merge remote-tracking branch 'upstream/main' into session-view-updated
kqualters-elastic Mar 29, 2022
e99a71b
Fix mistake in intl message, clean up tests
kqualters-elastic Mar 29, 2022
9b592b5
Create a hook for session view components and handlers
kqualters-elastic Mar 29, 2022
169e9d0
Use hook to share logic between tgrid and timeline, clean up css
kqualters-elastic Mar 29, 2022
794efea
Merge remote-tracking branch 'upstream/main' into session-view-updated
kqualters-elastic Mar 29, 2022
545cddd
Fix flyout usage with new hook
kqualters-elastic Mar 29, 2022
83502cd
Update failing snapshot
kqualters-elastic Mar 29, 2022
678fdc6
Remove feature flag
kqualters-elastic Mar 29, 2022
5293a14
Use correct sourcerer scope for panel
kqualters-elastic Mar 29, 2022
bf284c6
Fix embarrassment
kqualters-elastic Mar 29, 2022
bac80f5
Merge branch 'main' into session-view-updated
opauloh Mar 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ export const ENABLE_NEWS_FEED_SETTING = 'securitySolution:enableNewsFeed' as con
/** This Kibana Advanced Setting enables the warnings for CCS read permissions */
export const ENABLE_CCS_READ_WARNING_SETTING = 'securitySolution:enableCcsWarning' as const;

/** This Kibana Advanced Setting enables the session view component in the UI */
export const ENABLE_SESSION_VIEW_PLUGIN = 'securitySolution:enableSessionView' as const;

/** This Kibana Advanced Setting sets the auto refresh interval for the detections all rules table */
export const DEFAULT_RULES_TABLE_REFRESH_SETTING = 'securitySolution:rulesTableRefresh' as const;

Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/security_solution/common/ecs/process/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export interface ProcessEcs {
Ext?: Ext;
command_line?: string[];
entity_id?: string[];
entry_leader?: ProcessSessionData;
session_leader?: ProcessSessionData;
group_leader?: ProcessSessionData;
exit_code?: number[];
hash?: ProcessHashData;
parent?: ProcessParentData;
Expand All @@ -25,6 +28,12 @@ export interface ProcessEcs {
working_directory?: string[];
}

export interface ProcessSessionData {
entity_id?: string[];
pid?: string[];
name?: string[];
}

export interface ProcessHashData {
md5?: string[];
sha1?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ export enum TimelineTabs {
notes = 'notes',
pinned = 'pinned',
eql = 'eql',
session = 'session',
}

/**
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"licensing",
"maps",
"ruleRegistry",
"sessionView",
"taskManager",
"timelines",
"triggersActionsUi",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_fe
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
import type { EntityType } from '../../../../../timelines/common';
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
import { ENABLE_SESSION_VIEW_PLUGIN } from '../../../../common/constants';

export interface OwnProps {
end: string;
Expand Down Expand Up @@ -79,8 +80,16 @@ const AlertsTableComponent: React.FC<Props> = ({
}) => {
const dispatch = useDispatch();
const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]);
const { filterManager } = useKibana().services.data.query;
const ACTION_BUTTON_COUNT = 4;
const {
uiSettings,
data: {
query: { filterManager },
},
} = useKibana().services;

const isSessionViewEnabled = uiSettings.get(ENABLE_SESSION_VIEW_PLUGIN);

const ACTION_BUTTON_COUNT = isSessionViewEnabled ? 5 : 4;

const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');

Expand All @@ -106,7 +115,10 @@ const AlertsTableComponent: React.FC<Props> = ({
);
}, [dispatch, filterManager, tGridEnabled, timelineId]);

const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
const leadingControlColumns = useMemo(
() => getDefaultControlColumn(ACTION_BUTTON_COUNT),
[ACTION_BUTTON_COUNT]
);

return (
<StatefulEventsViewer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { GlobalTimeArgs } from '../../containers/use_global_time';
import { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
import { QueryTabBodyProps as UserQueryTabBodyProps } from '../../../users/pages/navigation/types';
import { QueryTabBodyProps as HostQueryTabBodyProps } from '../../../hosts/pages/navigation/types';
import { ENABLE_SESSION_VIEW_PLUGIN } from '../../../../common/constants';
import { useKibana } from '../../lib/kibana';

const EVENTS_HISTOGRAM_ID = 'eventsHistogramQuery';

Expand Down Expand Up @@ -84,7 +86,11 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
}) => {
const dispatch = useDispatch();
const { globalFullScreen } = useGlobalFullScreen();
const ACTION_BUTTON_COUNT = 4;
const { uiSettings } = useKibana().services;

const isSessionViewEnabled = uiSettings.get(ENABLE_SESSION_VIEW_PLUGIN);

const ACTION_BUTTON_COUNT = isSessionViewEnabled ? 5 : 4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might add the unit test later(follow up PR?) for checking the buttons count on dependency if the session view is enabled

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya no problem, once someone confirms if we are keeping the flag or not 😆

Copy link
Contributor

@opauloh opauloh Mar 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @kqualters-elastic, we can drop the Feature Flag, we will only have the sessions flag for capturing sessions data from Endpoint, and won't have a Feature flag for hiding Session View UI

cc @qcorporation

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @opauloh for cc.ing me. I can confirm we want to keep the empty state.
I remember we had originally talked about having a Kibana flag that would turn off Session View within the initial integration discussions but there's value in having an empty state.

visibility: @norrietaylor @snehsach19

const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');

useEffect(() => {
Expand All @@ -111,7 +117,10 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
};
}, [deleteQuery]);

const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
const leadingControlColumns = useMemo(
() => getDefaultControlColumn(ACTION_BUTTON_COUNT),
[ACTION_BUTTON_COUNT]
);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import React from 'react';
import useResizeObserver from 'use-resize-observer/polyfilled';

import '../../mock/match_media';
import { waitFor } from '@testing-library/react';
import { render } from '@testing-library/react';
import { TestProviders } from '../../mock';
import { useMountAppended } from '../../utils/use_mount_appended';

import { mockEventViewerResponse } from './mock';
import { StatefulEventsViewer } from '.';
Expand Down Expand Up @@ -61,37 +60,27 @@ const testProps = {
start: from,
};
describe('StatefulEventsViewer', () => {
const mount = useMountAppended();

(useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]);

test('it renders the events viewer', async () => {
const wrapper = mount(
const wrapper = render(
<TestProviders>
<StatefulEventsViewer {...testProps} />
</TestProviders>
);

await waitFor(() => {
wrapper.update();

expect(wrapper.text()).toMatchInlineSnapshot(`"hello grid"`);
});
expect(wrapper.getByText('hello grid')).toBeTruthy();
});

// InspectButtonContainer controls displaying InspectButton components
test('it renders InspectButtonContainer', async () => {
const wrapper = mount(
const wrapper = render(
<TestProviders>
<StatefulEventsViewer {...testProps} />
</TestProviders>
);

await waitFor(() => {
wrapper.update();

expect(wrapper.find(`InspectButtonContainer`).exists()).toBe(true);
});
expect(wrapper.findByTestId(`InspectButtonContainer`)).toBeTruthy();
});

test('it closes field editor when unmounted', async () => {
Expand All @@ -101,14 +90,14 @@ describe('StatefulEventsViewer', () => {
return {};
});

const wrapper = mount(
const { unmount } = render(
<TestProviders>
<StatefulEventsViewer {...testProps} />
</TestProviders>
);
expect(mockCloseEditor).not.toHaveBeenCalled();

wrapper.unmount();
unmount();
expect(mockCloseEditor).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import styled from 'styled-components';
import type { Filter } from '@kbn/es-query';
import { inputsModel, State } from '../../store';
import { inputsActions } from '../../store/actions';
import { ControlColumnProps, RowRenderer, TimelineId } from '../../../../common/types/timeline';
import {
ControlColumnProps,
RowRenderer,
TimelineId,
TimelineTabs,
} from '../../../../common/types/timeline';
import { APP_ID, APP_UI_ID } from '../../../../common/constants';
import { timelineActions } from '../../../timelines/store/timeline';
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
Expand All @@ -24,7 +29,6 @@ import { SourcererScopeName } from '../../store/sourcerer/model';
import { useSourcererDataView } from '../../containers/sourcerer';
import type { EntityType } from '../../../../../timelines/common';
import { TGridCellAction } from '../../../../../timelines/common/types';
import { DetailsPanel } from '../../../timelines/components/side_panel';
import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering';
import { FIELDS_WITHOUT_CELL_ACTIONS } from '../../lib/cell_actions/constants';
import { useGetUserCasesPermissions, useKibana } from '../../lib/kibana';
Expand All @@ -33,6 +37,7 @@ import {
useFieldBrowserOptions,
FieldEditorActions,
} from '../../../timelines/components/fields_browser';
import { useDetailPanel } from '../../../timelines/components/side_panel/hooks/use_detail_panel';

const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = [];

Expand Down Expand Up @@ -105,6 +110,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
itemsPerPage,
itemsPerPageOptions,
kqlMode,
sessionViewId,
showCheckboxes,
sort,
} = defaultModel,
Expand Down Expand Up @@ -155,11 +161,22 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({

const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]);
const trailingControlColumns: ControlColumnProps[] = EMPTY_CONTROL_COLUMNS;
const graphOverlay = useMemo(
() =>
graphEventId != null && graphEventId.length > 0 ? <GraphOverlay timelineId={id} /> : null,
[graphEventId, id]
);

const { openDetailsPanel, DetailsPanel } = useDetailPanel({
isFlyoutView: true,
entityType,
sourcererScope: SourcererScopeName.timeline,
timelineId: id,
tabType: TimelineTabs.query,
});

const graphOverlay = useMemo(() => {
const shouldShowOverlay =
(graphEventId != null && graphEventId.length > 0) || sessionViewId !== null;
Copy link
Contributor

@michaelolo24 michaelolo24 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need graphEventId.length > 0 ? Can we get away with shouldShowOverlay = graphEventId ?? sessionViewId?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we unify the != and !== approach in this statement just to use one? Could be graphEventId or sessionViewId as undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nervous to change it, for whatever reason, graph event id is set to empty string sometimes and is undefined at other times

return shouldShowOverlay ? (
<GraphOverlay timelineId={id} openDetailsPanel={openDetailsPanel} />
) : null;
}, [graphEventId, id, sessionViewId, openDetailsPanel]);
const setQuery = useCallback(
(inspect, loading, refetch) => {
dispatch(inputsActions.setQuery({ id, inputId: 'global', inspect, loading, refetch }));
Expand Down Expand Up @@ -239,14 +256,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
})}
</InspectButtonContainer>
</FullScreenContainer>
<DetailsPanel
browserFields={browserFields}
entityType={entityType}
docValueFields={docValueFields}
isFlyoutView
runtimeMappings={runtimeMappings}
timelineId={id}
/>
{DetailsPanel}
</CasesContext>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ export const mockGlobalState: State = {
end: '2020-07-08T08:20:18.966Z',
},
selectedEventIds: {},
sessionViewId: null,
show: false,
showCheckboxes: false,
pinnedEventIds: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,7 @@ export const mockTimelineModel: TimelineModel = {
savedObjectId: 'ef579e40-jibber-jabber',
selectAll: false,
selectedEventIds: {},
sessionViewId: null,
show: false,
showCheckboxes: false,
sort: [
Expand Down Expand Up @@ -2132,6 +2133,7 @@ export const defaultTimelineProps: CreateTimelineProps = {
savedObjectId: null,
selectAll: false,
selectedEventIds: {},
sessionViewId: null,
show: false,
showCheckboxes: false,
sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ describe('alert actions', () => {
savedObjectId: null,
selectAll: false,
selectedEventIds: {},
sessionViewId: null,
show: true,
showCheckboxes: false,
sort: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,5 @@ export const requiredFieldsForActions = [
'file.hash.sha256',
'host.os.family',
'event.code',
'process.entry_leader.entity_id',
];
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { connect, ConnectedProps, useDispatch } from 'react-redux';
import { Dispatch } from 'redux';
import type { Filter } from '@kbn/es-query';
import { APP_ID } from '../../../../common/constants';
import { APP_ID, ENABLE_SESSION_VIEW_PLUGIN } from '../../../../common/constants';
import { getEsQueryConfig } from '../../../../../../../src/plugins/data/common';
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
import { RowRendererId, TimelineIdLiteral } from '../../../../common/types/timeline';
Expand Down Expand Up @@ -104,7 +104,16 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
const kibana = useKibana();
const [, dispatchToaster] = useStateToaster();
const { addWarning } = useAppToasts();
const ACTION_BUTTON_COUNT = 4;
const {
uiSettings,
data: {
query: { filterManager },
},
} = useKibana().services;

const isSessionViewEnabled = uiSettings.get(ENABLE_SESSION_VIEW_PLUGIN);

const ACTION_BUTTON_COUNT = isSessionViewEnabled ? 5 : 4;

const getGlobalQuery = useCallback(
(customFilters: Filter[]) => {
Expand Down Expand Up @@ -334,7 +343,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
return [...defaultFilters, ...alertStatusFilter];
}
}, [defaultFilters, filterGroup]);
const { filterManager } = useKibana().services.data.query;

const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');

Expand Down Expand Up @@ -363,7 +371,10 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
);
}, [dispatch, filterManager, tGridEnabled, timelineId]);

const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
const leadingControlColumns = useMemo(
() => getDefaultControlColumn(ACTION_BUTTON_COUNT),
[ACTION_BUTTON_COUNT]
);

const casesPermissions = useGetUserCasesPermissions();
const CasesContext = kibana.services.cases.ui.getCasesContext();
Expand Down
Loading