Skip to content

Commit

Permalink
Merge pull request elastic#20 from Elastic-AWP-Platform/entry-leader-…
Browse files Browse the repository at this point in the history
…table

Initial timelines integration with the session_view plugin
  • Loading branch information
Jack authored Dec 16, 2021
2 parents 2c134e4 + 867444f commit 3da5fc6
Show file tree
Hide file tree
Showing 32 changed files with 5,623 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 @@ -114,6 +114,7 @@ export enum SecurityPageName {
trustedApps = 'trusted_apps',
ueba = 'ueba',
uncommonProcesses = 'uncommon_processes',
sessions = 'sessions',
}

export const TIMELINES_PATH = '/timelines' as const;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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 {
SESSION_TABLE,
SESSION_VIEW_EMPTY_STATE,
SESSION_TABLE_HEADER,
SESSION_VIEW_CLOSE_BUTTON,
PROCESS_TREE,
PROCESS_TREE_NODE_ALERT,
SEARCH_BAR,
DETAILS_PANEL,
DETAILS_PANEL_TOGGLE,
DETAILS_PANEL_ALERT,
DETAILS_PANEL_COMMAND,
DETAILS_PANEL_SESSION,
DETAILS_PANEL_SERVER,
getProcessTreeNodeAlertDetailViewRule,
} from '../../screens/session_view';
import {
loginAndNavigateToHostSessions,
openSessionView,
} from '../../tasks/hosts/open_session_view';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';

import { cleanKibana } from '../../tasks/common';

const tableHeaders = {
'@timestamp': '@timestamp',
'process.user.name': 'process.user.name',
'event.kind': 'event.kind',
'process.session.pid': 'process.session.pid',
'process.args': 'process.args',
};

const TEST_EVENT_ID = 'cDLmwH0BLujk-6QxyflF';
const LS_TEST_COMMAND = 'ls --color=auto';
const ALERT_TEST_COMMAND = 'vi cmd/cmd.prj';
const ALERT_NODE_TEST_ID = getProcessTreeNodeAlertDetailViewRule(
'64940663527c71b1f577df2aa529c42afc1c023108154714b49966e517e395b8'
);
const ALERT_RULE_ID = 'd9f45980-5e10-11ec-b7c6-17150991b0b3';

describe('Session view', () => {
context('Rendering table empty state', () => {
before(() => {
cleanKibana();
});

it('shows the empty state', () => {
loginAndNavigateToHostSessions();
cy.get(SESSION_VIEW_EMPTY_STATE).should('be.visible');
});
});

context('Rendering with data', () => {
before(() => {
cleanKibana();
esArchiverLoad('session_view');
});

beforeEach(() => {
loginAndNavigateToHostSessions();
});

after(() => {
esArchiverUnload('session_view');
});

it('renders the session table', () => {
// Check all columns expected exist
Object.keys(tableHeaders).forEach((header: string) => {
cy.get(SESSION_TABLE_HEADER(header)).should('be.visible');
});

openSessionView(TEST_EVENT_ID);

// Check session view exists and come back to session leader table
cy.get(PROCESS_TREE).should('be.visible');
const closeSessionViewButton = cy.get(SESSION_VIEW_CLOSE_BUTTON);
closeSessionViewButton.should('be.visible');
closeSessionViewButton.click();

cy.get(SESSION_TABLE).should('be.visible');
});

it('renders the session view', () => {
openSessionView(TEST_EVENT_ID);

// Checking Search bar exist
cy.get(SEARCH_BAR).should('be.visible');

// Check detail panel and its toggle work correctly
cy.get(DETAILS_PANEL).should('not.exist');
// Checking Details panel exist
cy.get(DETAILS_PANEL_TOGGLE).click();
cy.get(DETAILS_PANEL).should('be.visible');

// Only Session, Server Detail exist when no commands selected when detail panel is open
cy.get(DETAILS_PANEL_ALERT).should('not.exist');
cy.get(DETAILS_PANEL_COMMAND).should('not.exist');
cy.get(DETAILS_PANEL_SESSION).should('be.visible');
cy.get(DETAILS_PANEL_SERVER).should('exist');

const lsCommandNode = cy.contains(LS_TEST_COMMAND);
lsCommandNode.should('exist');
lsCommandNode.click();
// Checking Command, Session, Server Detail exist for a command without alert
cy.get(DETAILS_PANEL_ALERT).should('not.exist');
cy.get(DETAILS_PANEL_COMMAND).should('be.visible');
cy.get(DETAILS_PANEL_SESSION).should('exist');
cy.get(DETAILS_PANEL_SERVER).should('exist');

const viCommand = cy.contains(ALERT_TEST_COMMAND);
viCommand.should('be.visible');
viCommand.click();
// Checking Command, Session, Server, Alert Detail exist
cy.get(DETAILS_PANEL_ALERT).should('exist');
cy.get(DETAILS_PANEL_COMMAND).should('be.visible');
cy.get(DETAILS_PANEL_SESSION).should('exist');
cy.get(DETAILS_PANEL_SERVER).should('exist');
});

it('renders alert details correctly', () => {
openSessionView(TEST_EVENT_ID);

cy.get(PROCESS_TREE_NODE_ALERT).first().click();
cy.get(ALERT_NODE_TEST_ID).first().click();
cy.location('pathname').should('contain', `app/security/rules/id/${ALERT_RULE_ID}`);
});
});
});
36 changes: 36 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/session_view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.
*/

// Session leader table elements
export const SESSION_VIEW_TAB = '[data-test-subj="navigation-sessions"]';
export const SESSION_TABLE = '[data-test-subj="session-leader-table"]';
export const SESSION_VIEW_EMPTY_STATE = `${SESSION_TABLE} [data-test-subj="tGridEmptyState"]`;
export const SESSION_TABLE_HEADER = (column: string) =>
`${SESSION_TABLE} [data-test-subj="dataGridHeaderCell-${column}"]`;
export const SESSION_TABLE_HEADER_ACTIONS = (column: string) =>
`[data-test-subj="dataGridHeaderCellActionGroup-${column}"]`;
export const SESSION_TABLE_ROW_CONTROL = `${SESSION_TABLE} [data-test-subj="dataGridRowCell"].euiDataGridRowCell--firstColumn`;
export const SESSION_TABLE_ROW_MORE_BUTTON = (eventId: string) =>
`[data-test-subj="session-leader-table-more-actions-${eventId}"]`;
export const SESSION_TABLE_OPEN_SESSION_VIEW_TEXT = 'Open in session viewer';
export const SESSION_VIEW_CLOSE_BUTTON = '[data-test-subj="session-view-close-button"]';

// Process tree elements
export const PROCESS_TREE = '[data-test-subj="sessionViewProcessTree"]';
export const PROCESS_TREE_NODE_ALERT = '[data-test-subj="processTreeNodeAlertButton"]';
export const SEARCH_BAR = '[data-test-subj="sessionViewProcessEventsSearch"]';

// Details panel elements
export const DETAILS_PANEL = '[data-test-subj="sessionViewDetailPanel"]';
export const DETAILS_PANEL_TOGGLE = '[data-test-subj="sessionViewDetailPanelToggle"]';
export const DETAILS_PANEL_ALERT = '[data-test-subj="sessionViewDetailPanelAlertDetail"]';
export const DETAILS_PANEL_COMMAND = '[data-test-subj="sessionViewDetailPanelCommandDetail"]';
export const DETAILS_PANEL_SESSION = '[data-test-subj="sessionViewDetailPanelSessionDetail"]';
export const DETAILS_PANEL_SERVER = '[data-test-subj="sessionViewDetailPanelServerDetail"]';

export const getProcessTreeNodeAlertDetailViewRule = (alertUUID: string) =>
`[data-test-subj="sessionViewAlertDetailViewRule-${alertUUID}"]`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 {
SESSION_VIEW_TAB,
SESSION_TABLE_ROW_MORE_BUTTON,
SESSION_TABLE_OPEN_SESSION_VIEW_TEXT,
} from '../../screens/session_view';
import { loginAndWaitForPage } from '../login';
import { HOSTS_URL } from '../../urls/navigation';

export const loginAndNavigateToHostSessions = () => {
loginAndWaitForPage(HOSTS_URL);
cy.get(SESSION_VIEW_TAB).click();
};

// Picks a session by eventId from session leader table and open session view
export const openSessionView = (eventId: string) => {
// Open session view
cy.get(SESSION_TABLE_ROW_MORE_BUTTON(eventId)).click();
cy.contains(SESSION_TABLE_OPEN_SESSION_VIEW_TEXT).click();
};
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 @@ -24,7 +24,8 @@
"taskManager",
"timelines",
"triggersActionsUi",
"uiActions"
"uiActions",
"sessionView"
],
"optionalPlugins": [
"encryptedSavedObjects",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const mockGlobalState: State = {
uncommonProcesses: { activePage: 0, limit: 10 },
anomalies: null,
externalAlerts: { activePage: 0, limit: 10 },
sessions: null,
},
},
details: {
Expand All @@ -98,6 +99,7 @@ export const mockGlobalState: State = {
uncommonProcesses: { activePage: 0, limit: 10 },
anomalies: null,
externalAlerts: { activePage: 0, limit: 10 },
sessions: null,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const TabNameMappedToI18nKey: Record<HostsTableType, string> = {
[HostsTableType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
[HostsTableType.events]: i18n.NAVIGATION_EVENTS_TITLE,
[HostsTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE,
[HostsTableType.sessions]: i18n.NAVIGATION_SESSIONS_TITLE,
};

export const getBreadcrumbs = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HostsTabsProps } from './types';
import { scoreIntervalToDateTime } from '../../common/components/ml/score/score_interval_to_datetime';
import { Anomaly } from '../../common/components/ml/types';
import { HostsTableType } from '../store/model';
import { useKibana } from '../../common/lib/kibana';
import { AnomaliesQueryTabBody } from '../../common/containers/anomalies/anomalies_query_tab_body';
import { AnomaliesHostTable } from '../../common/components/ml/tables/anomalies_host_table';
import { UpdateDateRange } from '../../common/components/charts/common';
Expand All @@ -37,6 +38,7 @@ export const HostsTabs = memo<HostsTabsProps>(
to,
type,
}) => {
const { sessionView } = useKibana().services;
const narrowDateRange = useCallback(
(score: Anomaly, interval: string) => {
const fromTo = scoreIntervalToDateTime(score, interval);
Expand Down Expand Up @@ -97,6 +99,9 @@ export const HostsTabs = memo<HostsTabsProps>(
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.alerts})`}>
<HostAlertsQueryTabBody {...tabProps} />
</Route>
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.sessions})`}>
{sessionView.getSessionViewTableProcessTree()}
</Route>
</Switch>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const getHostsTabPath = () =>
`${HostsTableType.uncommonProcesses}|` +
`${HostsTableType.anomalies}|` +
`${HostsTableType.events}|` +
`${HostsTableType.alerts})`;
`${HostsTableType.alerts}|` +
`${HostsTableType.sessions})`;

const getHostDetailsTabPath = () =>
`${hostDetailsPagePath}/:tabName(` +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => {
href: getTabsOnHostsUrl(HostsTableType.alerts),
disabled: false,
},
[HostsTableType.sessions]: {
id: HostsTableType.sessions,
name: i18n.NAVIGATION_SESSIONS_TITLE,
href: getTabsOnHostsUrl(HostsTableType.sessions),
disabled: false,
},
};

return hasMlUserPermissions ? hostsNavTabs : omit([HostsTableType.anomalies], hostsNavTabs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ export const NAVIGATION_ALERTS_TITLE = i18n.translate(
}
);

export const NAVIGATION_SESSIONS_TITLE = i18n.translate(
'xpack.securitySolution.hosts.navigation.sessionsTitle',
{
defaultMessage: 'Sessions',
}
);

export const ERROR_FETCHING_AUTHENTICATIONS_DATA = i18n.translate(
'xpack.securitySolution.hosts.navigaton.matrixHistogram.errorFetchingAuthenticationsData',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const mockHostsState: HostsModel = {
activePage: 4,
limit: DEFAULT_TABLE_LIMIT,
},
[HostsTableType.sessions]: null,
},
},
details: {
Expand Down Expand Up @@ -63,6 +64,7 @@ export const mockHostsState: HostsModel = {
activePage: 4,
limit: DEFAULT_TABLE_LIMIT,
},
[HostsTableType.sessions]: null,
},
},
};
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/security_solution/public/hosts/store/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export enum HostsTableType {
uncommonProcesses = 'uncommonProcesses',
anomalies = 'anomalies',
alerts = 'externalAlerts',
sessions = 'sessions',
}

export interface BasicQueryPaginated {
Expand All @@ -39,6 +40,7 @@ export interface Queries {
[HostsTableType.uncommonProcesses]: BasicQueryPaginated;
[HostsTableType.anomalies]: null | undefined;
[HostsTableType.alerts]: BasicQueryPaginated;
[HostsTableType.sessions]: null | undefined;
}

export interface GenericHostsModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const initialHostsState: HostsState = {
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
limit: DEFAULT_TABLE_LIMIT,
},
[HostsTableType.sessions]: null,
},
},
details: {
Expand Down Expand Up @@ -78,6 +79,7 @@ export const initialHostsState: HostsState = {
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
limit: DEFAULT_TABLE_LIMIT,
},
[HostsTableType.sessions]: null,
},
},
};
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security_solution/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export interface StartPlugins {
ml?: MlPluginStart;
spaces?: SpacesPluginStart;
dataViewFieldEditor: IndexPatternFieldEditorStart;
sessionView: any;
}

export type StartServices = CoreStart &
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/session_view/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const INTERNAL_TEST_ROUTE = '/internal/session_view/test_route';
export const INTERNAL_TEST_SAVED_OBJECT_ROUTE = '/internal/session_view/test_saved_object_route';
export const PROCESS_EVENTS_ROUTE = '/internal/session_view/process_events_route';
export const RECENT_SESSION_ROUTE = '/internal/session_view/recent_session_route';

export const SESSION_ENTRY_LEADERS_ROUTE = '/internal/session_view/session_entry_leaders_route';
export const TEST_SAVED_OBJECT = 'session_view_test_saved_object';

export const PROCESS_EVENTS_PER_PAGE = 2000;
10 changes: 8 additions & 2 deletions x-pack/plugins/session_view/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
"name": "Security Team",
"githubTeam": "security-team"
},
"requiredPlugins": ["data"],
"requiredBundles": ["kibanaReact", "esUiShared"],
"requiredPlugins": [
"data",
"timelines"
],
"requiredBundles": [
"kibanaReact",
"esUiShared"
],
"server": true,
"ui": true
}
Loading

0 comments on commit 3da5fc6

Please sign in to comment.