mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Session View] Fixes to slow process event queries + xterm.js updated. (#155326)
## Summary Issue: https://github.com/elastic/kibana/issues/155183 This PR improves the process_events_route and io_event_route queries. We now pass in the index and **process.entry_leader.start** time to greatly improve the loading time of session view. Prior to this change, we were doing a cross cluster search across the entire logs-endpoint.events.process datastream. Session view will now use the index from the entry leader event to limit the scope of the query. It also ensures a range query is added with the entry leader start time, to prevent data prior to the session from being hit. I've also updated the npm package for xterm.js which addresses this renovate ticket: https://github.com/elastic/kibana/pull/147815 ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e933ad50fe
commit
672e9925b0
47 changed files with 447 additions and 115 deletions
|
@ -952,7 +952,7 @@
|
|||
"whatwg-fetch": "^3.0.0",
|
||||
"xml2js": "^0.4.22",
|
||||
"xstate": "^4.35.2",
|
||||
"xterm": "^5.0.0",
|
||||
"xterm": "^5.1.0",
|
||||
"yauzl": "^2.10.0",
|
||||
"yazl": "^2.5.1"
|
||||
},
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface ProcessSessionData {
|
|||
entity_id?: string[];
|
||||
pid?: string[];
|
||||
name?: string[];
|
||||
start?: string[];
|
||||
}
|
||||
|
||||
export interface ProcessHashData {
|
||||
|
|
|
@ -26,4 +26,7 @@ export type SignalEcsAAD = Exclude<SignalEcs, 'rule' | 'status'> & {
|
|||
suppression?: {
|
||||
docs_count: string[];
|
||||
};
|
||||
ancestors?: {
|
||||
index?: string;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
*/
|
||||
|
||||
export interface SessionViewConfig {
|
||||
processIndex: string;
|
||||
sessionEntityId: string;
|
||||
sessionStartTime: string;
|
||||
jumpToEntityId?: string;
|
||||
jumpToCursor?: string;
|
||||
investigatedAlertId?: string;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants';
|
||||
import { AGENT_ID_ROUTE } from '../../common/constants';
|
||||
|
||||
export const registerAgentIdRoute = (router: IRouter) => {
|
||||
|
@ -34,10 +33,10 @@ export const registerAgentIdRoute = (router: IRouter) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const getAgentId = async (client: ElasticsearchClient, query: string, index?: string) => {
|
||||
export const getAgentId = async (client: ElasticsearchClient, query: string, index: string) => {
|
||||
const queryDSL = JSON.parse(query);
|
||||
const search = await client.search({
|
||||
index: [index || PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: queryDSL,
|
||||
size: 1,
|
||||
|
|
|
@ -8,7 +8,6 @@ import type { SortCombinations } from '@elastic/elasticsearch/lib/api/typesWithB
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants';
|
||||
import {
|
||||
AGGREGATE_ROUTE,
|
||||
AGGREGATE_PAGE_SIZE,
|
||||
|
@ -26,12 +25,12 @@ export const registerAggregateRoute = (router: IRouter) => {
|
|||
path: AGGREGATE_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
query: schema.string(),
|
||||
countBy: schema.maybe(schema.string()),
|
||||
groupBy: schema.string(),
|
||||
page: schema.number(),
|
||||
perPage: schema.maybe(schema.number()),
|
||||
index: schema.maybe(schema.string()),
|
||||
sortByCount: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
|
@ -43,11 +42,11 @@ export const registerAggregateRoute = (router: IRouter) => {
|
|||
try {
|
||||
const body = await doSearch(
|
||||
client,
|
||||
index,
|
||||
query,
|
||||
groupBy,
|
||||
page,
|
||||
perPage,
|
||||
index,
|
||||
countBy,
|
||||
sortByCount
|
||||
);
|
||||
|
@ -62,11 +61,11 @@ export const registerAggregateRoute = (router: IRouter) => {
|
|||
|
||||
export const doSearch = async (
|
||||
client: ElasticsearchClient,
|
||||
index: string,
|
||||
query: string,
|
||||
groupBy: string,
|
||||
page: number, // zero based
|
||||
perPage = AGGREGATE_PAGE_SIZE,
|
||||
index?: string,
|
||||
countBy?: string,
|
||||
sortByCount?: string
|
||||
): Promise<AggregateBucketPaginationResult> => {
|
||||
|
@ -88,7 +87,7 @@ export const doSearch = async (
|
|||
}
|
||||
|
||||
const search = await client.search({
|
||||
index: [index || PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: queryDSL,
|
||||
size: 0,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants';
|
||||
import { COUNT_ROUTE } from '../../common/constants';
|
||||
|
||||
export const registerCountRoute = (router: IRouter) => {
|
||||
|
@ -16,9 +15,9 @@ export const registerCountRoute = (router: IRouter) => {
|
|||
path: COUNT_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
query: schema.string(),
|
||||
field: schema.string(),
|
||||
index: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -27,7 +26,7 @@ export const registerCountRoute = (router: IRouter) => {
|
|||
const { query, field, index } = request.query;
|
||||
|
||||
try {
|
||||
const body = await doCount(client, query, field, index);
|
||||
const body = await doCount(client, index, query, field);
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (err) {
|
||||
|
@ -39,14 +38,14 @@ export const registerCountRoute = (router: IRouter) => {
|
|||
|
||||
export const doCount = async (
|
||||
client: ElasticsearchClient,
|
||||
index: string,
|
||||
query: string,
|
||||
field: string,
|
||||
index?: string
|
||||
field: string
|
||||
) => {
|
||||
const queryDSL = JSON.parse(query);
|
||||
|
||||
const search = await client.search({
|
||||
index: [index || PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: queryDSL,
|
||||
size: 0,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { PROCESS_EVENTS_INDEX } from '@kbn/session-view-plugin/common/constants';
|
||||
import { MULTI_TERMS_AGGREGATE_ROUTE, AGGREGATE_PAGE_SIZE } from '../../common/constants';
|
||||
import {
|
||||
MultiTermsAggregateGroupBy,
|
||||
|
@ -20,6 +19,7 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => {
|
|||
path: MULTI_TERMS_AGGREGATE_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
query: schema.string(),
|
||||
countBy: schema.maybe(schema.string()),
|
||||
groupBys: schema.arrayOf(
|
||||
|
@ -31,7 +31,6 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => {
|
|||
),
|
||||
page: schema.number(),
|
||||
perPage: schema.maybe(schema.number()),
|
||||
index: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -40,7 +39,7 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => {
|
|||
const { query, countBy, groupBys, page, perPage, index } = request.query;
|
||||
|
||||
try {
|
||||
const body = await doSearch(client, query, groupBys, page, perPage, index, countBy);
|
||||
const body = await doSearch(client, index, query, groupBys, page, perPage, countBy);
|
||||
|
||||
return response.ok({ body });
|
||||
} catch (err) {
|
||||
|
@ -52,11 +51,11 @@ export const registerMultiTermsAggregateRoute = (router: IRouter) => {
|
|||
|
||||
export const doSearch = async (
|
||||
client: ElasticsearchClient,
|
||||
index: string,
|
||||
query: string,
|
||||
groupBys: MultiTermsAggregateGroupBy[],
|
||||
page: number, // zero based
|
||||
perPage = AGGREGATE_PAGE_SIZE,
|
||||
index?: string,
|
||||
countBy?: string
|
||||
): Promise<MultiTermsAggregateBucketPaginationResult> => {
|
||||
const queryDSL = JSON.parse(query);
|
||||
|
@ -72,7 +71,7 @@ export const doSearch = async (
|
|||
: undefined;
|
||||
|
||||
const search = await client.search({
|
||||
index: [index || PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: queryDSL,
|
||||
size: 0,
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
*/
|
||||
|
||||
export interface SessionViewConfig {
|
||||
processIndex: string;
|
||||
sessionEntityId: string;
|
||||
sessionStartTime: string;
|
||||
jumpToEntityId?: string;
|
||||
jumpToCursor?: string;
|
||||
investigatedAlertId?: string;
|
||||
|
|
|
@ -383,7 +383,8 @@ describe('Actions', () => {
|
|||
...mockTimelineData[0].ecs,
|
||||
event: { kind: ['alert'] },
|
||||
agent: { type: ['endpoint'] },
|
||||
process: { entry_leader: { entity_id: ['test_id'] } },
|
||||
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
|
||||
_index: '.ds-logs-endpoint.events.process-default',
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -400,7 +401,8 @@ describe('Actions', () => {
|
|||
...mockTimelineData[0].ecs,
|
||||
event: { kind: ['alert'] },
|
||||
agent: { type: ['endpoint'] },
|
||||
process: { entry_leader: { entity_id: ['test_id'] } },
|
||||
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
|
||||
_index: '.ds-logs-endpoint.events.process-default',
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
|
@ -425,7 +427,8 @@ describe('Actions', () => {
|
|||
...mockTimelineData[0].ecs,
|
||||
event: { kind: ['alert'] },
|
||||
agent: { type: ['endpoint'] },
|
||||
process: { entry_leader: { entity_id: ['test_id'] } },
|
||||
process: { entry_leader: { entity_id: ['test_id'], start: ['2022-05-08T13:44:00.13Z'] } },
|
||||
_index: '.ds-logs-endpoint.events.process-default',
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
|
|
|
@ -39,7 +39,7 @@ import { useTourContext } from '../guided_onboarding_tour';
|
|||
import { AlertsCasesTourSteps, SecurityStepId } from '../guided_onboarding_tour/tour_config';
|
||||
import { isDetectionsAlertsTable } from '../top_n/helpers';
|
||||
import { GuidedOnboardingTourStep } from '../guided_onboarding_tour/tour_step';
|
||||
import { DEFAULT_ACTION_BUTTON_WIDTH, isAlert } from './helpers';
|
||||
import { DEFAULT_ACTION_BUTTON_WIDTH, isAlert, getSessionViewProcessIndex } from './helpers';
|
||||
|
||||
const ActionsContainer = styled.div`
|
||||
align-items: center;
|
||||
|
@ -149,10 +149,16 @@ const ActionsComponent: React.FC<ActionProps> = ({
|
|||
]);
|
||||
|
||||
const sessionViewConfig = useMemo(() => {
|
||||
const { process, _id, timestamp } = ecsData;
|
||||
const { process, _id, _index, timestamp, kibana } = ecsData;
|
||||
const sessionEntityId = process?.entry_leader?.entity_id?.[0];
|
||||
const sessionStartTime = process?.entry_leader?.start?.[0];
|
||||
const processIndex = getSessionViewProcessIndex(kibana?.alert?.ancestors?.index?.[0] || _index);
|
||||
|
||||
if (sessionEntityId === undefined) {
|
||||
if (
|
||||
processIndex === undefined ||
|
||||
sessionEntityId === undefined ||
|
||||
sessionStartTime === undefined
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -162,7 +168,9 @@ const ActionsComponent: React.FC<ActionProps> = ({
|
|||
(investigatedAlertId && ecsData.kibana?.alert.original_time?.[0]) || timestamp;
|
||||
|
||||
return {
|
||||
processIndex,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
jumpToEntityId,
|
||||
jumpToCursor,
|
||||
investigatedAlertId,
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { DEFAULT_ACTION_BUTTON_WIDTH, getActionsColumnWidth, isAlert } from './helpers';
|
||||
import {
|
||||
DEFAULT_ACTION_BUTTON_WIDTH,
|
||||
getActionsColumnWidth,
|
||||
isAlert,
|
||||
getSessionViewProcessIndex,
|
||||
} from './helpers';
|
||||
|
||||
describe('isAlert', () => {
|
||||
test('it returns true when the eventType is an alert', () => {
|
||||
|
@ -48,3 +53,67 @@ describe('getActionsColumnWidth', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSessionViewProcessIndex', () => {
|
||||
test('it returns process index for cloud_defend alert event index', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'.ds-logs-cloud_defend.alerts-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('logs-cloud_defend.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for cloud_defend file event index', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'.ds-logs-cloud_defend.file-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('logs-cloud_defend.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for cloud_defend process event index', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'.ds-logs-cloud_defend.process-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('logs-cloud_defend.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for cloud_defend that includes cluster', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'aws_ec2:.ds-logs-cloud_defend.process-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('aws_ec2:logs-cloud_defend.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for endpoint file index', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'.ds-logs-endpoint.events.file-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('logs-endpoint.events.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for endpoint alerts index', () => {
|
||||
const result = getSessionViewProcessIndex('.ds-logs-endpoint.alerts-default-2023.04.25-000001');
|
||||
|
||||
expect(result).toEqual('logs-endpoint.events.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for endpoint process index', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'.ds-logs-endpoint.events.process-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('logs-endpoint.events.process*');
|
||||
});
|
||||
|
||||
test('it returns process index for endpoint that includes cluster', () => {
|
||||
const result = getSessionViewProcessIndex(
|
||||
'azure-01:.ds-logs-endpoint.events.process-default-2023.04.25-000001'
|
||||
);
|
||||
|
||||
expect(result).toEqual('azure-01:logs-endpoint.events.process*');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -50,3 +50,23 @@ export const getActionsColumnWidth = (actionButtonCount: number): number => {
|
|||
|
||||
return contentWidth + leftRightCellPadding;
|
||||
};
|
||||
|
||||
// Currently both logs-endpoint.events.process* and logs-cloud_defend.process* are valid sources for session data.
|
||||
// To avoid cross cluster searches, the original index of the event is used to infer the index to find data for the
|
||||
// rest of the session.
|
||||
export const getSessionViewProcessIndex = (eventIndex?: string | null) => {
|
||||
if (!eventIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = eventIndex.match(/([a-z0-9_-]+:)?\.ds-logs-(endpoint|cloud_defend)/i);
|
||||
const cluster = match?.[1];
|
||||
const clusterStr = cluster ? `${cluster}` : '';
|
||||
const service = match?.[2];
|
||||
|
||||
if (service === 'endpoint') {
|
||||
return `${clusterStr}logs-endpoint.events.process*`;
|
||||
} else if (service === 'cloud_defend') {
|
||||
return `${clusterStr}logs-cloud_defend.process*`;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,7 +12,26 @@ import type { LeftPanelContext } from '../context';
|
|||
import { LeftFlyoutContext } from '../context';
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids';
|
||||
import { SessionView } from './session_view';
|
||||
import {
|
||||
SessionView,
|
||||
SESSION_ENTITY_ID,
|
||||
SESSION_START_TIME,
|
||||
KIBANA_ANCESTOR_INDEX,
|
||||
} from './session_view';
|
||||
|
||||
interface MockData {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const mockData: MockData = {
|
||||
[SESSION_ENTITY_ID]: 'id',
|
||||
[SESSION_START_TIME]: '2023-04-25T04:33:23.676Z',
|
||||
[KIBANA_ANCESTOR_INDEX]: '.ds-logs-endpoint.events.process-default',
|
||||
};
|
||||
|
||||
const mockFieldsData = (prop: string) => {
|
||||
return mockData[prop];
|
||||
};
|
||||
|
||||
jest.mock('../../../common/lib/kibana', () => {
|
||||
const originalModule = jest.requireActual('../../../common/lib/kibana');
|
||||
|
@ -31,7 +50,24 @@ jest.mock('../../../common/lib/kibana', () => {
|
|||
describe('<SessionView />', () => {
|
||||
it('renders session view correctly', () => {
|
||||
const contextValue = {
|
||||
getFieldsData: () => 'id',
|
||||
getFieldsData: mockFieldsData,
|
||||
indexName: '.ds-logs-endpoint.events.process-default',
|
||||
} as unknown as LeftPanelContext;
|
||||
|
||||
const wrapper = render(
|
||||
<TestProviders>
|
||||
<LeftFlyoutContext.Provider value={contextValue}>
|
||||
<SessionView />
|
||||
</LeftFlyoutContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.getByTestId(SESSION_VIEW_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders session view from an alert correctly', () => {
|
||||
const contextValue = {
|
||||
getFieldsData: mockFieldsData,
|
||||
indexName: '.alerts-security', // it should prioritize KIBANA_ANCESTOR_INDEX above indexName
|
||||
} as unknown as LeftPanelContext;
|
||||
|
||||
const wrapper = render(
|
||||
|
|
|
@ -14,20 +14,27 @@ import { SESSION_VIEW_ERROR_MESSAGE } from './translations';
|
|||
import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { useLeftPanelContext } from '../context';
|
||||
import { getSessionViewProcessIndex } from '../../../common/components/header_actions/helpers';
|
||||
|
||||
export const SESSION_VIEW_ID = 'session_view';
|
||||
const SESSION_ENTITY_ID = 'process.entry_leader.entity_id';
|
||||
export const SESSION_ENTITY_ID = 'process.entry_leader.entity_id';
|
||||
export const SESSION_START_TIME = 'process.entry_leader.start';
|
||||
export const KIBANA_ANCESTOR_INDEX = 'kibana.alert.ancestors.index';
|
||||
|
||||
/**
|
||||
* Session view displayed in the document details expandable flyout left section under the Visualize tab
|
||||
*/
|
||||
export const SessionView: FC = () => {
|
||||
const { sessionView } = useKibana().services;
|
||||
const { getFieldsData } = useLeftPanelContext();
|
||||
const { getFieldsData, indexName } = useLeftPanelContext();
|
||||
|
||||
const processIndex = getSessionViewProcessIndex(
|
||||
getField(getFieldsData(KIBANA_ANCESTOR_INDEX)) || indexName
|
||||
);
|
||||
const sessionEntityId = getField(getFieldsData(SESSION_ENTITY_ID));
|
||||
const sessionStartTime = getField(getFieldsData(SESSION_START_TIME));
|
||||
|
||||
if (!sessionEntityId) {
|
||||
if (!processIndex || !sessionEntityId || !sessionStartTime) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="error"
|
||||
|
@ -42,7 +49,9 @@ export const SessionView: FC = () => {
|
|||
return (
|
||||
<div data-test-subj={SESSION_VIEW_TEST_ID}>
|
||||
{sessionView.getSessionView({
|
||||
processIndex,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
isFullScreen: true,
|
||||
})}
|
||||
</div>
|
||||
|
|
|
@ -253,7 +253,9 @@ describe('GraphOverlay', () => {
|
|||
[timelineId]: {
|
||||
...mockGlobalState.timeline.timelineById[timelineId],
|
||||
sessionViewConfig: {
|
||||
processIndex: 'logs-endpoint.events.process*',
|
||||
sessionEntityId: 'testId',
|
||||
sessionStartTime: '2021-10-14T08:05:34.853Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -18,10 +18,6 @@ export const SECURITY_APP_ID = 'security';
|
|||
export const POLICIES_PAGE_PATH = '/administration/policy';
|
||||
|
||||
// index patterns
|
||||
const ENDPOINT_PROCESS_EVENTS_INDEX =
|
||||
'*:logs-endpoint.events.process*,logs-endpoint.events.process*';
|
||||
const CLOUD_DEFEND_PROCESS_EVENTS_INDEX = '*:logs-cloud_defend.process*,logs-cloud_defend.process*';
|
||||
export const PROCESS_EVENTS_INDEX = `${ENDPOINT_PROCESS_EVENTS_INDEX},${CLOUD_DEFEND_PROCESS_EVENTS_INDEX}`; // match on both cross cluster and local indices
|
||||
export const PREVIEW_ALERTS_INDEX = '.preview.alerts-security.alerts-default';
|
||||
|
||||
// field properties
|
||||
|
|
|
@ -17,6 +17,9 @@ import {
|
|||
ProcessEventAlertCategory,
|
||||
} from '../../types/process_tree';
|
||||
|
||||
export const TEST_PROCESS_INDEX = 'logs-endpoint.events.process*';
|
||||
export const TEST_SESSION_START_TIME = '2021-10-14T08:05:34.853Z';
|
||||
|
||||
export const mockEvents: ProcessEvent[] = [
|
||||
{
|
||||
'@timestamp': '2021-11-23T15:25:04.210Z',
|
||||
|
|
|
@ -193,6 +193,7 @@ export interface ProcessEvent {
|
|||
kind?: EventKind;
|
||||
category?: string | string[];
|
||||
action?: EventAction | EventAction[];
|
||||
type?: string | string[];
|
||||
id?: string;
|
||||
};
|
||||
file?: {
|
||||
|
@ -289,6 +290,7 @@ export interface ProcessEventCloud {
|
|||
};
|
||||
project?: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
};
|
||||
provider?: string;
|
||||
region?: string;
|
||||
|
|
|
@ -132,6 +132,7 @@ export const getCloudData = (cloud: ProcessEventCloud | undefined): DetailPanelC
|
|||
},
|
||||
project: {
|
||||
id: DASH,
|
||||
name: DASH,
|
||||
},
|
||||
provider: DASH,
|
||||
region: DASH,
|
||||
|
@ -144,6 +145,7 @@ export const getCloudData = (cloud: ProcessEventCloud | undefined): DetailPanelC
|
|||
detailPanelCloud.instance.name = dataOrDash(cloud?.instance?.name).toString();
|
||||
detailPanelCloud.account.id = dataOrDash(cloud?.account?.id).toString();
|
||||
detailPanelCloud.project.id = dataOrDash(cloud?.project?.id).toString();
|
||||
detailPanelCloud.project.name = dataOrDash(cloud?.project?.name).toString();
|
||||
detailPanelCloud.provider = dataOrDash(cloud?.provider).toString();
|
||||
detailPanelCloud.region = dataOrDash(cloud?.region).toString();
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ describe('DetailPanelMetadataTab component', () => {
|
|||
expect(renderResult.queryByText(TEST_NAME)).toBeVisible();
|
||||
|
||||
// expand host os accordion
|
||||
renderResult.queryByText('Host OS')?.click();
|
||||
renderResult.queryByText('OS')?.click();
|
||||
expect(renderResult.queryByText('architecture')).toBeVisible();
|
||||
expect(renderResult.queryByText('os.family')).toBeVisible();
|
||||
expect(renderResult.queryByText('os.full')).toBeVisible();
|
||||
|
@ -182,7 +182,7 @@ describe('DetailPanelMetadataTab component', () => {
|
|||
expect(renderResult.queryAllByText('name').length).toBe(2);
|
||||
|
||||
// expand host os accordion
|
||||
renderResult.queryByText('Host OS')?.click();
|
||||
renderResult.queryByText('OS')?.click();
|
||||
expect(renderResult.queryByText('architecture')).toBeVisible();
|
||||
expect(renderResult.queryByText('os.family')).toBeVisible();
|
||||
expect(renderResult.queryByText('os.full')).toBeVisible();
|
||||
|
|
|
@ -50,24 +50,11 @@ export const DetailPanelMetadataTab = ({
|
|||
<>
|
||||
<DetailPanelAccordion
|
||||
id="metadataHost"
|
||||
title={i18n.translate('xpack.sessionView.metadataDetailsTab.metadata', {
|
||||
defaultMessage: 'Metadata',
|
||||
title={i18n.translate('xpack.sessionView.metadataDetailsTab.metadataHost', {
|
||||
defaultMessage: 'Host',
|
||||
})}
|
||||
initialIsOpen={true}
|
||||
listItems={[
|
||||
{
|
||||
title: <DetailPanelListItem>hostname</DetailPanelListItem>,
|
||||
description: (
|
||||
<DetailPanelCopy
|
||||
textToCopy={`host.hostname: "${hostData.hostname}"`}
|
||||
tooltipContent={hostData.hostname}
|
||||
>
|
||||
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
|
||||
{hostData.hostname}
|
||||
</EuiTextColor>
|
||||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>id</DetailPanelListItem>,
|
||||
description: (
|
||||
|
@ -81,6 +68,19 @@ export const DetailPanelMetadataTab = ({
|
|||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>hostname</DetailPanelListItem>,
|
||||
description: (
|
||||
<DetailPanelCopy
|
||||
textToCopy={`host.hostname: "${hostData.hostname}"`}
|
||||
tooltipContent={hostData.hostname}
|
||||
>
|
||||
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
|
||||
{hostData.hostname}
|
||||
</EuiTextColor>
|
||||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>ip</DetailPanelListItem>,
|
||||
description: (
|
||||
|
@ -133,7 +133,7 @@ export const DetailPanelMetadataTab = ({
|
|||
<DetailPanelAccordion
|
||||
id="hostOS"
|
||||
title={i18n.translate('xpack.sessionView.metadataDetailsTab.host', {
|
||||
defaultMessage: 'Host OS',
|
||||
defaultMessage: 'OS',
|
||||
})}
|
||||
listItems={[
|
||||
{
|
||||
|
@ -305,6 +305,19 @@ export const DetailPanelMetadataTab = ({
|
|||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>project.name</DetailPanelListItem>,
|
||||
description: (
|
||||
<DetailPanelCopy
|
||||
textToCopy={`cloud.project.name: "${cloudData.project.name}"`}
|
||||
tooltipContent={cloudData.project.name}
|
||||
>
|
||||
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
|
||||
{cloudData.project.name}
|
||||
</EuiTextColor>
|
||||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -453,17 +453,6 @@ export const DetailPanelProcessTab = ({ selectedProcess }: DetailPanelProcessTab
|
|||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>user.id</DetailPanelListItem>,
|
||||
description: (
|
||||
<DetailPanelCopy
|
||||
textToCopy={`${PROCESS_FIELD_PREFIX}.user.id: "${userId}"`}
|
||||
tooltipContent={userId}
|
||||
>
|
||||
{userId}
|
||||
</DetailPanelCopy>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <DetailPanelListItem>user.name</DetailPanelListItem>,
|
||||
description: (
|
||||
|
|
|
@ -95,6 +95,9 @@ Object {
|
|||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem emotion-euiFlexItem-growZero"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -190,6 +193,9 @@ Object {
|
|||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem emotion-euiFlexItem-growZero"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
EuiToolTip,
|
||||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ALERT_ICONS } from '../../../common/constants';
|
||||
import {
|
||||
ProcessEvent,
|
||||
|
@ -78,6 +79,7 @@ export const ProcessTreeAlert = ({
|
|||
const processEventAlertCategory = category ?? ProcessEventAlertCategory.process;
|
||||
const alertCategoryDetailDisplayText = getAlertCategoryDisplayText(alert, category);
|
||||
const alertIconTooltipContent = getAlertIconTooltipContent(processEventAlertCategory);
|
||||
const eventType = Array.isArray(event?.type) ? event?.type?.[0] : event?.type;
|
||||
|
||||
return (
|
||||
<div key={uuid} css={styles.alert} data-id={uuid}>
|
||||
|
@ -137,6 +139,13 @@ export const ProcessTreeAlert = ({
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge css={styles.actionBadge}>{event?.action}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
{eventType === 'denied' && (
|
||||
<EuiBadge css={styles.actionBadge} color="danger">
|
||||
<FormattedMessage id="xpack.sessionView.blockedBadge" defaultMessage="Blocked" />
|
||||
</EuiBadge>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -44,6 +44,10 @@ export const EXEC_USER_CHANGE = i18n.translate('xpack.sessionView.execUserChange
|
|||
defaultMessage: 'Exec user change',
|
||||
});
|
||||
|
||||
export const COLLAPSE_ALL = i18n.translate('xpack.sessionView.collapseAll', {
|
||||
defaultMessage: 'Collapse all',
|
||||
});
|
||||
|
||||
export interface ProcessDeps {
|
||||
process: Process;
|
||||
isSessionLeader?: boolean;
|
||||
|
@ -263,7 +267,7 @@ export function ProcessTreeNode({
|
|||
const shouldRenderChildren = isSessionLeader || (childrenExpanded && children?.length > 0);
|
||||
const childrenTreeDepth = depth + 1;
|
||||
|
||||
const showUserEscalation = !isSessionLeader && !!user?.name && user.name !== parent?.user?.name;
|
||||
const showUserEscalation = !isSessionLeader && !!user?.id && user.id !== parent?.user?.id;
|
||||
const interactiveSession = !!tty;
|
||||
const sessionIcon = interactiveSession ? 'desktop' : 'gear';
|
||||
const iconTestSubj = hasExec
|
||||
|
@ -303,12 +307,11 @@ export function ProcessTreeNode({
|
|||
<Nbsp />
|
||||
<b css={styles.darkText}>{userName}</b>
|
||||
<Nbsp />
|
||||
<EuiButtonIcon
|
||||
size="xs"
|
||||
iconType="fold"
|
||||
onClick={handleCollapseProcessTree}
|
||||
css={styles.jumpToTop}
|
||||
/>
|
||||
<span css={styles.jumpToTop}>
|
||||
<EuiToolTip title={COLLAPSE_ALL}>
|
||||
<EuiButtonIcon size="xs" iconType="fold" onClick={handleCollapseProcessTree} />
|
||||
</EuiToolTip>
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
|
@ -344,7 +347,7 @@ export function ProcessTreeNode({
|
|||
css={buttonStyles.userChangedButton}
|
||||
aria-label={EXEC_USER_CHANGE}
|
||||
>
|
||||
{EXEC_USER_CHANGE} :<span>{user.name}</span>
|
||||
{EXEC_USER_CHANGE} ({userName})
|
||||
</EuiButton>
|
||||
)}
|
||||
{!isSessionLeader && children.length > 0 && (
|
||||
|
|
|
@ -28,14 +28,16 @@ import {
|
|||
} from '../../../common/constants';
|
||||
|
||||
export const useFetchSessionViewProcessEvents = (
|
||||
index: string,
|
||||
sessionEntityId: string,
|
||||
sessionStartTime: string,
|
||||
jumpToCursor?: string
|
||||
) => {
|
||||
const { http } = useKibana<CoreStart>().services;
|
||||
const [currentJumpToCursor, setCurrentJumpToCursor] = useState<string>('');
|
||||
const cachingKeys = useMemo(
|
||||
() => [QUERY_KEY_PROCESS_EVENTS, sessionEntityId, jumpToCursor],
|
||||
[sessionEntityId, jumpToCursor]
|
||||
() => [QUERY_KEY_PROCESS_EVENTS, index, sessionEntityId, jumpToCursor],
|
||||
[index, sessionEntityId, jumpToCursor]
|
||||
);
|
||||
|
||||
const query = useInfiniteQuery(
|
||||
|
@ -50,7 +52,9 @@ export const useFetchSessionViewProcessEvents = (
|
|||
|
||||
const res = await http.get<ProcessEventResults>(PROCESS_EVENTS_ROUTE, {
|
||||
query: {
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
cursor,
|
||||
forward,
|
||||
},
|
||||
|
@ -126,6 +130,7 @@ export const useFetchSessionViewProcessEvents = (
|
|||
|
||||
export const useFetchSessionViewAlerts = (
|
||||
sessionEntityId: string,
|
||||
sessionStartTime: string,
|
||||
investigatedAlertId?: string
|
||||
) => {
|
||||
const { http } = useKibana<CoreStart>().services;
|
||||
|
@ -139,6 +144,7 @@ export const useFetchSessionViewAlerts = (
|
|||
const res = await http.get<ProcessEventResults>(ALERTS_ROUTE, {
|
||||
query: {
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
investigatedAlertId,
|
||||
cursor,
|
||||
},
|
||||
|
@ -210,15 +216,21 @@ export const useFetchAlertStatus = (
|
|||
return query;
|
||||
};
|
||||
|
||||
export const useFetchGetTotalIOBytes = (sessionEntityId: string) => {
|
||||
export const useFetchGetTotalIOBytes = (
|
||||
index: string,
|
||||
sessionEntityId: string,
|
||||
sessionStartTime: string
|
||||
) => {
|
||||
const { http } = useKibana<CoreStart>().services;
|
||||
const cachingKeys = [QUERY_KEY_GET_TOTAL_IO_BYTES, sessionEntityId];
|
||||
const cachingKeys = [QUERY_KEY_GET_TOTAL_IO_BYTES, index, sessionEntityId];
|
||||
const query = useQuery<{ total: number }, Error>(
|
||||
cachingKeys,
|
||||
async () => {
|
||||
return http.get<{ total: number }>(GET_TOTAL_IO_BYTES_ROUTE, {
|
||||
query: {
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
import { waitFor, waitForElementToBeRemoved } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import {
|
||||
TEST_PROCESS_INDEX,
|
||||
TEST_SESSION_START_TIME,
|
||||
} from '../../../common/mocks/constants/session_view_process.mock';
|
||||
import { sessionViewProcessEventsMock } from '../../../common/mocks/responses/session_view_process_events.mock';
|
||||
import { sessionViewProcessEventsMergedMock } from '../../../common/mocks/responses/session_view_process_events_merged.mock';
|
||||
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
|
||||
|
@ -48,7 +52,13 @@ describe('SessionView component', () => {
|
|||
mockedContext = createAppRootMockRenderer();
|
||||
mockedApi = mockedContext.coreStart.http.get;
|
||||
render = () =>
|
||||
(renderResult = mockedContext.render(<SessionView sessionEntityId="test-entity-id" />));
|
||||
(renderResult = mockedContext.render(
|
||||
<SessionView
|
||||
processIndex={TEST_PROCESS_INDEX}
|
||||
sessionStartTime={TEST_SESSION_START_TIME}
|
||||
sessionEntityId="test-entity-id"
|
||||
/>
|
||||
));
|
||||
mockUseDateFormat.mockImplementation(() => 'MMM D, YYYY @ HH:mm:ss.SSS');
|
||||
});
|
||||
|
||||
|
|
|
@ -46,7 +46,9 @@ import { REFRESH_SESSION, TOGGLE_TTY_PLAYER, DETAIL_PANEL } from './translations
|
|||
* The main wrapper component for the session view.
|
||||
*/
|
||||
export const SessionView = ({
|
||||
processIndex,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
height,
|
||||
isFullScreen = false,
|
||||
jumpToEntityId,
|
||||
|
@ -129,7 +131,12 @@ export const SessionView = ({
|
|||
fetchPreviousPage,
|
||||
hasPreviousPage,
|
||||
refetch,
|
||||
} = useFetchSessionViewProcessEvents(sessionEntityId, currentJumpToCursor);
|
||||
} = useFetchSessionViewProcessEvents(
|
||||
processIndex,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
currentJumpToCursor
|
||||
);
|
||||
|
||||
const {
|
||||
data: alertsData,
|
||||
|
@ -138,10 +145,13 @@ export const SessionView = ({
|
|||
hasNextPage: hasNextPageAlerts,
|
||||
error: alertsError,
|
||||
refetch: refetchAlerts,
|
||||
} = useFetchSessionViewAlerts(sessionEntityId, investigatedAlertId);
|
||||
} = useFetchSessionViewAlerts(sessionEntityId, sessionStartTime, investigatedAlertId);
|
||||
|
||||
const { data: totalTTYOutputBytes, refetch: refetchTotalTTYOutput } =
|
||||
useFetchGetTotalIOBytes(sessionEntityId);
|
||||
const { data: totalTTYOutputBytes, refetch: refetchTotalTTYOutput } = useFetchGetTotalIOBytes(
|
||||
processIndex,
|
||||
sessionEntityId,
|
||||
sessionStartTime
|
||||
);
|
||||
const hasTTYOutput = !!totalTTYOutputBytes?.total;
|
||||
const bytesOfOutput = useMemo(() => {
|
||||
const { unit, value } = byteSize(totalTTYOutputBytes?.total || 0);
|
||||
|
@ -421,8 +431,10 @@ export const SessionView = ({
|
|||
}}
|
||||
</EuiResizableContainer>
|
||||
<TTYPlayer
|
||||
index={processIndex}
|
||||
show={showTTY}
|
||||
sessionEntityId={sessionEntityId}
|
||||
sessionStartTime={sessionStartTime}
|
||||
onClose={onToggleTTY}
|
||||
isFullscreen={isFullScreen}
|
||||
onJumpToEvent={onJumpToEvent}
|
||||
|
|
|
@ -33,7 +33,11 @@ import {
|
|||
TTY_LINES_PRE_SEEK,
|
||||
} from '../../../common/constants';
|
||||
|
||||
export const useFetchIOEvents = (sessionEntityId: string) => {
|
||||
export const useFetchIOEvents = (
|
||||
index: string,
|
||||
sessionEntityId: string,
|
||||
sessionStartTime: string
|
||||
) => {
|
||||
const { http } = useKibana<CoreStart>().services;
|
||||
const cachingKeys = useMemo(() => [QUERY_KEY_IO_EVENTS, sessionEntityId], [sessionEntityId]);
|
||||
|
||||
|
@ -43,7 +47,9 @@ export const useFetchIOEvents = (sessionEntityId: string) => {
|
|||
const { cursor } = pageParam;
|
||||
const res = await http.get<ProcessEventResults>(IO_EVENTS_ROUTE, {
|
||||
query: {
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
cursor,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import { waitFor, act } from '@testing-library/react';
|
||||
import {
|
||||
TEST_PROCESS_INDEX,
|
||||
TEST_SESSION_START_TIME,
|
||||
} from '../../../common/mocks/constants/session_view_process.mock';
|
||||
import { sessionViewIOEventsMock } from '../../../common/mocks/responses/session_view_io_events.mock';
|
||||
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
|
||||
import { TTYPlayerDeps, TTYPlayer } from '.';
|
||||
|
@ -51,7 +55,9 @@ describe('TTYPlayer component', () => {
|
|||
|
||||
props = {
|
||||
show: true,
|
||||
index: TEST_PROCESS_INDEX,
|
||||
sessionEntityId: mockSessionEntityId,
|
||||
sessionStartTime: TEST_SESSION_START_TIME,
|
||||
onClose: jest.fn(),
|
||||
onJumpToEvent: jest.fn(),
|
||||
isFullscreen: false,
|
||||
|
|
|
@ -33,8 +33,10 @@ import { TTYPlayerControls } from '../tty_player_controls';
|
|||
import { BETA, TOGGLE_TTY_PLAYER, DETAIL_PANEL } from '../session_view/translations';
|
||||
|
||||
export interface TTYPlayerDeps {
|
||||
show: boolean;
|
||||
index: string;
|
||||
sessionEntityId: string;
|
||||
sessionStartTime: string;
|
||||
show: boolean;
|
||||
onClose(): void;
|
||||
isFullscreen: boolean;
|
||||
onJumpToEvent(event: ProcessEvent): void;
|
||||
|
@ -43,8 +45,10 @@ export interface TTYPlayerDeps {
|
|||
}
|
||||
|
||||
export const TTYPlayer = ({
|
||||
show,
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
show,
|
||||
onClose,
|
||||
isFullscreen,
|
||||
onJumpToEvent,
|
||||
|
@ -54,8 +58,11 @@ export const TTYPlayer = ({
|
|||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { ref: scrollRef, height: containerHeight = 1 } = useResizeObserver<HTMLDivElement>({});
|
||||
|
||||
const { data, fetchNextPage, hasNextPage, isFetching, refetch } =
|
||||
useFetchIOEvents(sessionEntityId);
|
||||
const { data, fetchNextPage, hasNextPage, isFetching, refetch } = useFetchIOEvents(
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime
|
||||
);
|
||||
const { lines, processStartMarkers } = useIOLines(data?.pages);
|
||||
const [fontSize, setFontSize] = useState(DEFAULT_TTY_FONT_SIZE);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
@ -88,7 +95,7 @@ export const TTYPlayer = ({
|
|||
useEffect(() => {
|
||||
if (show) {
|
||||
// refetch the most recent page when tty player is loaded
|
||||
refetch({ refetchPage: (_page, index, allPages) => allPages.length - 1 === index });
|
||||
refetch({ refetchPage: (_page, i, allPages) => allPages.length - 1 === i });
|
||||
}
|
||||
}, [refetch, show]);
|
||||
|
||||
|
|
|
@ -10,8 +10,15 @@ import { CoreStart } from '@kbn/core/public';
|
|||
export type SessionViewServices = CoreStart;
|
||||
|
||||
export interface SessionViewDeps {
|
||||
// we pass in the index of the session leader that spawned session_view, this avoids having to query multiple cross cluster indices
|
||||
processIndex: string;
|
||||
|
||||
// the root node of the process tree to render. e.g process.entry.entity_id or process.session_leader.entity_id
|
||||
sessionEntityId: string;
|
||||
|
||||
// start time is passed in order to scope session_view queries to the appropriate time range, and avoid querying data across all time.
|
||||
sessionStartTime: string;
|
||||
|
||||
height?: number;
|
||||
isFullScreen?: boolean;
|
||||
// if provided, the session view will jump to and select the provided event if it belongs to the session leader
|
||||
|
@ -132,6 +139,7 @@ export interface DetailPanelCloud {
|
|||
};
|
||||
project: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
provider: string;
|
||||
region: string;
|
||||
|
|
|
@ -31,15 +31,15 @@ export const registerAlertsRoute = (
|
|||
validate: {
|
||||
query: schema.object({
|
||||
sessionEntityId: schema.string(),
|
||||
sessionStartTime: schema.string(),
|
||||
investigatedAlertId: schema.maybe(schema.string()),
|
||||
cursor: schema.maybe(schema.string()),
|
||||
range: schema.maybe(schema.arrayOf(schema.string())),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (_context, request, response) => {
|
||||
const client = await ruleRegistry.getRacClientWithRequest(request);
|
||||
const { sessionEntityId, investigatedAlertId, range, cursor } = request.query;
|
||||
const { sessionEntityId, sessionStartTime, investigatedAlertId, cursor } = request.query;
|
||||
|
||||
try {
|
||||
const body = await searchAlerts(
|
||||
|
@ -47,7 +47,7 @@ export const registerAlertsRoute = (
|
|||
sessionEntityId,
|
||||
ALERTS_PER_PAGE,
|
||||
investigatedAlertId,
|
||||
range,
|
||||
[sessionStartTime],
|
||||
cursor
|
||||
);
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ import { IRouter } from '@kbn/core/server';
|
|||
import { EVENT_ACTION } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
GET_TOTAL_IO_BYTES_ROUTE,
|
||||
PROCESS_EVENTS_INDEX,
|
||||
TOTAL_BYTES_CAPTURED_PROPERTY,
|
||||
ENTRY_SESSION_ENTITY_ID_PROPERTY,
|
||||
TIMESTAMP_PROPERTY,
|
||||
} from '../../common/constants';
|
||||
|
||||
export const registerGetTotalIOBytesRoute = (router: IRouter) => {
|
||||
|
@ -18,23 +18,33 @@ export const registerGetTotalIOBytesRoute = (router: IRouter) => {
|
|||
path: GET_TOTAL_IO_BYTES_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
sessionEntityId: schema.string(),
|
||||
sessionStartTime: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const client = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const { sessionEntityId } = request.query;
|
||||
const { index, sessionEntityId, sessionStartTime } = request.query;
|
||||
|
||||
try {
|
||||
const search = await client.search({
|
||||
index: [PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { [ENTRY_SESSION_ENTITY_ID_PROPERTY]: sessionEntityId } },
|
||||
{ term: { [EVENT_ACTION]: 'text_output' } },
|
||||
{
|
||||
range: {
|
||||
// optimization to prevent data before this session from being hit.
|
||||
[TIMESTAMP_PROPERTY]: {
|
||||
gte: sessionStartTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -8,6 +8,8 @@ import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
|
|||
import { EventAction, EventKind } from '../../common/types/process_tree';
|
||||
import { searchProcessWithIOEvents } from './io_events_route';
|
||||
|
||||
const TEST_PROCESS_INDEX = 'logs-endpoint.events.process*';
|
||||
|
||||
const getEmptyResponse = async () => {
|
||||
return {
|
||||
aggregations: {
|
||||
|
@ -45,7 +47,7 @@ describe('io_events_route.ts', () => {
|
|||
describe('searchProcessWithIOEvents(client, sessionEntityId, range)', () => {
|
||||
it('should return an empty events array for a non existant entity_id', async () => {
|
||||
const esClient = elasticsearchServiceMock.createElasticsearchClient(getEmptyResponse());
|
||||
const body = await searchProcessWithIOEvents(esClient, 'asdf');
|
||||
const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'asdf');
|
||||
|
||||
expect(body.length).toBe(0);
|
||||
});
|
||||
|
@ -53,7 +55,7 @@ describe('io_events_route.ts', () => {
|
|||
it('returns results for a particular session entity_id', async () => {
|
||||
const esClient = elasticsearchServiceMock.createElasticsearchClient(getResponse());
|
||||
|
||||
const body = await searchProcessWithIOEvents(esClient, 'mockId');
|
||||
const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'mockId');
|
||||
|
||||
expect(body.length).toBe(1);
|
||||
|
||||
|
@ -69,7 +71,10 @@ describe('io_events_route.ts', () => {
|
|||
|
||||
const start = '2021-11-23T15:25:04.210Z';
|
||||
const end = '2021-20-23T15:25:04.210Z';
|
||||
const body = await searchProcessWithIOEvents(esClient, 'mockId', [start, end]);
|
||||
const body = await searchProcessWithIOEvents(esClient, TEST_PROCESS_INDEX, 'mockId', [
|
||||
start,
|
||||
end,
|
||||
]);
|
||||
|
||||
expect(body.length).toBe(1);
|
||||
});
|
||||
|
|
|
@ -13,8 +13,8 @@ import { EventAction, EventKind } from '../../common/types/process_tree';
|
|||
import {
|
||||
IO_EVENTS_ROUTE,
|
||||
IO_EVENTS_PER_PAGE,
|
||||
PROCESS_EVENTS_INDEX,
|
||||
ENTRY_SESSION_ENTITY_ID_PROPERTY,
|
||||
TIMESTAMP_PROPERTY,
|
||||
PROCESS_ENTITY_ID_PROPERTY,
|
||||
PROCESS_EVENTS_PER_PAGE,
|
||||
} from '../../common/constants';
|
||||
|
@ -25,7 +25,9 @@ export const registerIOEventsRoute = (router: IRouter) => {
|
|||
path: IO_EVENTS_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
sessionEntityId: schema.string(),
|
||||
sessionStartTime: schema.string(),
|
||||
cursor: schema.maybe(schema.string()),
|
||||
pageSize: schema.maybe(schema.number()),
|
||||
}),
|
||||
|
@ -33,17 +35,31 @@ export const registerIOEventsRoute = (router: IRouter) => {
|
|||
},
|
||||
async (context, request, response) => {
|
||||
const client = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const { sessionEntityId, cursor, pageSize = IO_EVENTS_PER_PAGE } = request.query;
|
||||
const {
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
cursor,
|
||||
pageSize = IO_EVENTS_PER_PAGE,
|
||||
} = request.query;
|
||||
|
||||
try {
|
||||
const search = await client.search({
|
||||
index: [PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { [ENTRY_SESSION_ENTITY_ID_PROPERTY]: sessionEntityId } },
|
||||
{ term: { [EVENT_ACTION]: 'text_output' } },
|
||||
{
|
||||
range: {
|
||||
// optimization to prevent data before this session from being hit.
|
||||
[TIMESTAMP_PROPERTY]: {
|
||||
gte: sessionStartTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -72,6 +88,7 @@ export const registerIOEventsRoute = (router: IRouter) => {
|
|||
|
||||
export const searchProcessWithIOEvents = async (
|
||||
client: ElasticsearchClient,
|
||||
index: string,
|
||||
sessionEntityId: string,
|
||||
range?: string[]
|
||||
) => {
|
||||
|
@ -90,7 +107,7 @@ export const searchProcessWithIOEvents = async (
|
|||
|
||||
try {
|
||||
const search = await client.search({
|
||||
index: [PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
|
||||
import { fetchEventsAndScopedAlerts } from './process_events_route';
|
||||
import { mockEvents, mockAlerts } from '../../common/mocks/constants/session_view_process.mock';
|
||||
import {
|
||||
TEST_PROCESS_INDEX,
|
||||
TEST_SESSION_START_TIME,
|
||||
mockEvents,
|
||||
mockAlerts,
|
||||
} from '../../common/mocks/constants/session_view_process.mock';
|
||||
import { getAlertsClientMockInstance, resetAlertingAuthMock } from './alerts_client_mock.test';
|
||||
import { EventAction, EventKind, ProcessEvent } from '../../common/types/process_tree';
|
||||
|
||||
|
@ -42,7 +47,14 @@ describe('process_events_route.ts', () => {
|
|||
const client = elasticsearchServiceMock.createElasticsearchClient(getEmptyResponse());
|
||||
const alertsClient = getAlertsClientMockInstance(client);
|
||||
|
||||
const body = await fetchEventsAndScopedAlerts(client, alertsClient, 'asdf', undefined);
|
||||
const body = await fetchEventsAndScopedAlerts(
|
||||
client,
|
||||
alertsClient,
|
||||
TEST_PROCESS_INDEX,
|
||||
'asdf',
|
||||
'',
|
||||
undefined
|
||||
);
|
||||
|
||||
expect(body.events.length).toBe(0);
|
||||
expect(body.total).toBe(0);
|
||||
|
@ -52,7 +64,14 @@ describe('process_events_route.ts', () => {
|
|||
const client = elasticsearchServiceMock.createElasticsearchClient(getResponse());
|
||||
const alertsClient = getAlertsClientMockInstance();
|
||||
|
||||
const body = await fetchEventsAndScopedAlerts(client, alertsClient, 'mockId', undefined);
|
||||
const body = await fetchEventsAndScopedAlerts(
|
||||
client,
|
||||
alertsClient,
|
||||
TEST_PROCESS_INDEX,
|
||||
'mockId',
|
||||
TEST_SESSION_START_TIME,
|
||||
undefined
|
||||
);
|
||||
|
||||
expect(body.events.length).toBe(mockEvents.length + mockAlerts.length);
|
||||
|
||||
|
@ -74,7 +93,9 @@ describe('process_events_route.ts', () => {
|
|||
const body = await fetchEventsAndScopedAlerts(
|
||||
client,
|
||||
alertsClient,
|
||||
TEST_PROCESS_INDEX,
|
||||
'mockId',
|
||||
TEST_SESSION_START_TIME,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
|
|
@ -17,8 +17,8 @@ import {
|
|||
ALERTS_PER_PROCESS_EVENTS_PAGE,
|
||||
PROCESS_EVENTS_ROUTE,
|
||||
PROCESS_EVENTS_PER_PAGE,
|
||||
PROCESS_EVENTS_INDEX,
|
||||
ENTRY_SESSION_ENTITY_ID_PROPERTY,
|
||||
TIMESTAMP_PROPERTY,
|
||||
} from '../../common/constants';
|
||||
import { ProcessEvent } from '../../common/types/process_tree';
|
||||
import { searchAlerts } from './alerts_route';
|
||||
|
@ -33,7 +33,9 @@ export const registerProcessEventsRoute = (
|
|||
path: PROCESS_EVENTS_ROUTE,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
index: schema.string(),
|
||||
sessionEntityId: schema.string(),
|
||||
sessionStartTime: schema.string(),
|
||||
cursor: schema.maybe(schema.string()),
|
||||
forward: schema.maybe(schema.boolean()),
|
||||
pageSize: schema.maybe(schema.number()),
|
||||
|
@ -43,13 +45,15 @@ export const registerProcessEventsRoute = (
|
|||
async (context, request, response) => {
|
||||
const client = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
const alertsClient = await ruleRegistry.getRacClientWithRequest(request);
|
||||
const { sessionEntityId, cursor, forward, pageSize } = request.query;
|
||||
const { index, sessionEntityId, sessionStartTime, cursor, forward, pageSize } = request.query;
|
||||
|
||||
try {
|
||||
const body = await fetchEventsAndScopedAlerts(
|
||||
client,
|
||||
alertsClient,
|
||||
index,
|
||||
sessionEntityId,
|
||||
sessionStartTime,
|
||||
cursor,
|
||||
forward,
|
||||
pageSize
|
||||
|
@ -71,7 +75,9 @@ export const registerProcessEventsRoute = (
|
|||
export const fetchEventsAndScopedAlerts = async (
|
||||
client: ElasticsearchClient,
|
||||
alertsClient: AlertsClient,
|
||||
index: string,
|
||||
sessionEntityId: string,
|
||||
sessionStartTime: string,
|
||||
cursor?: string,
|
||||
forward = true,
|
||||
pageSize = PROCESS_EVENTS_PER_PAGE
|
||||
|
@ -79,7 +85,7 @@ export const fetchEventsAndScopedAlerts = async (
|
|||
const cursorMillis = cursor && new Date(cursor).getTime() + (forward ? -1 : 1);
|
||||
|
||||
const search = await client.search({
|
||||
index: [PROCESS_EVENTS_INDEX],
|
||||
index: [index],
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
|
@ -94,6 +100,14 @@ export const fetchEventsAndScopedAlerts = async (
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
// optimization to prevent data before this session from being hit.
|
||||
[TIMESTAMP_PROPERTY]: {
|
||||
gte: sessionStartTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -131,7 +145,12 @@ export const fetchEventsAndScopedAlerts = async (
|
|||
range
|
||||
);
|
||||
|
||||
const processesWithIOEvents = await searchProcessWithIOEvents(client, sessionEntityId, range);
|
||||
const processesWithIOEvents = await searchProcessWithIOEvents(
|
||||
client,
|
||||
index,
|
||||
sessionEntityId,
|
||||
range
|
||||
);
|
||||
|
||||
events = [...events, ...alertsBody.events, ...processesWithIOEvents];
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ export const CTI_ROW_RENDERER_FIELDS = [
|
|||
export const TIMELINE_EVENTS_FIELDS = [
|
||||
ALERT_RULE_CONSUMER,
|
||||
'@timestamp',
|
||||
'kibana.alert.ancestors.index',
|
||||
'kibana.alert.workflow_status',
|
||||
'kibana.alert.group.id',
|
||||
'kibana.alert.original_time',
|
||||
|
@ -230,6 +231,7 @@ export const TIMELINE_EVENTS_FIELDS = [
|
|||
'process.entry_leader.entity_id',
|
||||
'process.entry_leader.name',
|
||||
'process.entry_leader.pid',
|
||||
'process.entry_leader.start',
|
||||
'process.session_leader.entity_id',
|
||||
'process.session_leader.name',
|
||||
'process.session_leader.pid',
|
||||
|
|
|
@ -32288,7 +32288,6 @@
|
|||
"xpack.sessionView.metadataDetailsTab.cloud": "Cloud",
|
||||
"xpack.sessionView.metadataDetailsTab.container": "Conteneur",
|
||||
"xpack.sessionView.metadataDetailsTab.host": "Système d'exploitation de l'hôte",
|
||||
"xpack.sessionView.metadataDetailsTab.metadata": "Métadonnées",
|
||||
"xpack.sessionView.metadataDetailsTab.orchestrator": "Orchestrateur",
|
||||
"xpack.sessionView.networkTooltip": "Alerte réseau",
|
||||
"xpack.sessionView.output": "Sortie",
|
||||
|
|
|
@ -32267,7 +32267,6 @@
|
|||
"xpack.sessionView.metadataDetailsTab.cloud": "クラウド",
|
||||
"xpack.sessionView.metadataDetailsTab.container": "コンテナー",
|
||||
"xpack.sessionView.metadataDetailsTab.host": "ホストOS",
|
||||
"xpack.sessionView.metadataDetailsTab.metadata": "メタデータ",
|
||||
"xpack.sessionView.metadataDetailsTab.orchestrator": "オーケストレーター",
|
||||
"xpack.sessionView.networkTooltip": "ネットワークアラート",
|
||||
"xpack.sessionView.output": "アウトプット",
|
||||
|
|
|
@ -32284,7 +32284,6 @@
|
|||
"xpack.sessionView.metadataDetailsTab.cloud": "云",
|
||||
"xpack.sessionView.metadataDetailsTab.container": "容器",
|
||||
"xpack.sessionView.metadataDetailsTab.host": "主机 OS",
|
||||
"xpack.sessionView.metadataDetailsTab.metadata": "元数据",
|
||||
"xpack.sessionView.metadataDetailsTab.orchestrator": "Orchestrator",
|
||||
"xpack.sessionView.networkTooltip": "网络告警",
|
||||
"xpack.sessionView.output": "输出",
|
||||
|
|
|
@ -9,6 +9,8 @@ import expect from '@kbn/expect';
|
|||
import { GET_TOTAL_IO_BYTES_ROUTE } from '@kbn/session-view-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
const MOCK_INDEX = 'logs-endpoint.events.process*';
|
||||
const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z';
|
||||
const MOCK_SESSION_ENTITY_ID =
|
||||
'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA==';
|
||||
const MOCK_TOTAL_BYTES = 8192; // sum of total_captured_bytes field in io_events es archive
|
||||
|
@ -31,7 +33,9 @@ export default function getTotalIOBytesTests({ getService }: FtrProviderContext)
|
|||
|
||||
it(`${GET_TOTAL_IO_BYTES_ROUTE} returns a page of IO events`, async () => {
|
||||
const response = await supertest.get(GET_TOTAL_IO_BYTES_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.total).to.be(MOCK_TOTAL_BYTES);
|
||||
|
@ -39,6 +43,8 @@ export default function getTotalIOBytesTests({ getService }: FtrProviderContext)
|
|||
|
||||
it(`${GET_TOTAL_IO_BYTES_ROUTE} returns 0 for invalid query`, async () => {
|
||||
const response = await supertest.get(GET_TOTAL_IO_BYTES_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
sessionEntityId: 'xyz',
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
|
|
@ -9,6 +9,8 @@ import expect from '@kbn/expect';
|
|||
import { IO_EVENTS_ROUTE } from '@kbn/session-view-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
const MOCK_INDEX = 'logs-endpoint.events.process*';
|
||||
const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z';
|
||||
const MOCK_SESSION_ENTITY_ID =
|
||||
'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA==';
|
||||
const MOCK_IO_EVENT_TOTAL = 8;
|
||||
|
@ -33,7 +35,9 @@ export default function ioEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${IO_EVENTS_ROUTE} returns a page of IO events`, async () => {
|
||||
const response = await supertest.get(IO_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE,
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
@ -53,7 +57,9 @@ export default function ioEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${IO_EVENTS_ROUTE} returns a page of IO events (w cursor)`, async () => {
|
||||
const response = await supertest.get(IO_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE,
|
||||
cursor: MOCK_CURSOR,
|
||||
});
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
noKibanaPrivileges,
|
||||
} from '../../../rule_registry/common/lib/authentication/users';
|
||||
|
||||
const MOCK_INDEX = 'logs-endpoint.events.process*';
|
||||
const MOCK_SESSION_START_TIME = '2022-05-08T13:44:00.13Z';
|
||||
const MOCK_SESSION_ENTITY_ID =
|
||||
'MDEwMTAxMDEtMDEwMS0wMTAxLTAxMDEtMDEwMTAxMDEwMTAxLTUyMDU3LTEzMjk2NDkxMDQwLjEzMDAwMDAwMA==';
|
||||
|
||||
|
@ -58,7 +60,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${PROCESS_EVENTS_ROUTE} returns a page of process events`, async () => {
|
||||
const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
@ -68,7 +72,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${PROCESS_EVENTS_ROUTE} returns a page of process events (w alerts) (paging forward)`, async () => {
|
||||
const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data
|
||||
cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert.
|
||||
});
|
||||
|
@ -83,7 +89,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${PROCESS_EVENTS_ROUTE} returns a page of process events (w alerts) (paging backwards)`, async () => {
|
||||
const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data
|
||||
cursor: '2022-05-10T20:39:23.6817084Z',
|
||||
forward: false,
|
||||
|
@ -113,7 +121,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data
|
||||
cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert.
|
||||
});
|
||||
|
@ -134,7 +144,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
cursor: '2022-05-10T20:39:23.6817084Z', // paginating from the timestamp of the first alert.
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
@ -185,7 +197,9 @@ export default function processEventsTests({ getService }: FtrProviderContext) {
|
|||
|
||||
it(`${PROCESS_EVENTS_ROUTE} returns a page of process events`, async () => {
|
||||
const response = await supertest.get(PROCESS_EVENTS_ROUTE).set('kbn-xsrf', 'foo').query({
|
||||
index: MOCK_INDEX,
|
||||
sessionEntityId: MOCK_SESSION_ENTITY_ID,
|
||||
sessionStartTime: MOCK_SESSION_START_TIME,
|
||||
pageSize: MOCK_PAGE_SIZE, // overriding to test pagination, as we only have 419 records of mock data
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
|
|
@ -29590,10 +29590,10 @@ xtend@~2.1.1:
|
|||
dependencies:
|
||||
object-keys "~0.4.0"
|
||||
|
||||
xterm@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.0.0.tgz#0af50509b33d0dc62fde7a4ec17750b8e453cc5c"
|
||||
integrity sha512-tmVsKzZovAYNDIaUinfz+VDclraQpPUnAME+JawosgWRMphInDded/PuY0xmU5dOhyeYZsI0nz5yd8dPYsdLTA==
|
||||
xterm@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.1.0.tgz#3e160d60e6801c864b55adf19171c49d2ff2b4fc"
|
||||
integrity sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ==
|
||||
|
||||
y18n@^3.2.0:
|
||||
version "3.2.2"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue