Skip to content

Commit

Permalink
[7.x] [Security Solution][Resolver] Update @timestamp formatting (#78166
Browse files Browse the repository at this point in the history
) (#78533)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
michaelolo24 and elasticmachine authored Sep 29, 2020
1 parent c716d02 commit 9898a76
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export function getUiSettings(key: string): string | undefined {
if (key === 'dateFormat') {
return 'MMM D, YYYY @ HH:mm:ss.SSS';
}
if (key === 'dateFormat:tz') {
return 'America/New_York';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ export function mockTreeWith2AncestorsAndNoChildren({
entityID: secondAncestorID,
processName: 'a',
parentEntityID: 'none',
timestamp: 0,
timestamp: 1600863932316,
});
const firstAncestor: SafeResolverEvent = mockEndpointEvent({
entityID: firstAncestorID,
processName: 'b',
parentEntityID: secondAncestorID,
timestamp: 1,
timestamp: 1600863932317,
});
const originEvent: SafeResolverEvent = mockEndpointEvent({
entityID: originID,
processName: 'c',
parentEntityID: firstAncestorID,
timestamp: 2,
timestamp: 1600863932318,
});
return {
entityID: originID,
Expand Down Expand Up @@ -68,39 +68,39 @@ export function mockTreeWithAllProcessesTerminated({
entityID: secondAncestorID,
processName: 'a',
parentEntityID: 'none',
timestamp: 0,
timestamp: 1600863932316,
});
const firstAncestor: SafeResolverEvent = mockEndpointEvent({
entityID: firstAncestorID,
processName: 'b',
parentEntityID: secondAncestorID,
timestamp: 1,
timestamp: 1600863932317,
});
const originEvent: SafeResolverEvent = mockEndpointEvent({
entityID: originID,
processName: 'c',
parentEntityID: firstAncestorID,
timestamp: 2,
timestamp: 1600863932318,
});
const secondAncestorTermination: SafeResolverEvent = mockEndpointEvent({
entityID: secondAncestorID,
processName: 'a',
parentEntityID: 'none',
timestamp: 0,
timestamp: 1600863932316,
eventType: 'end',
});
const firstAncestorTermination: SafeResolverEvent = mockEndpointEvent({
entityID: firstAncestorID,
processName: 'b',
parentEntityID: secondAncestorID,
timestamp: 1,
timestamp: 1600863932317,
eventType: 'end',
});
const originEventTermination: SafeResolverEvent = mockEndpointEvent({
entityID: originID,
processName: 'c',
parentEntityID: firstAncestorID,
timestamp: 2,
timestamp: 1600863932318,
eventType: 'end',
});
return ({
Expand Down Expand Up @@ -162,21 +162,21 @@ export function mockTreeWithNoAncestorsAnd2Children({
entityID: originID,
processName: 'c.ext',
parentEntityID: 'none',
timestamp: 0,
timestamp: 1600863932316,
});
const firstChild: SafeResolverEvent = mockEndpointEvent({
pid: 1,
entityID: firstChildID,
processName: 'd',
parentEntityID: originID,
timestamp: 1,
timestamp: 1600863932317,
});
const secondChild: SafeResolverEvent = mockEndpointEvent({
pid: 2,
entityID: secondChildID,
processName: 'e',
parentEntityID: originID,
timestamp: 2,
timestamp: 1600863932318,
});

return {
Expand Down Expand Up @@ -216,50 +216,50 @@ export function mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents
const ancestor: SafeResolverEvent = mockEndpointEvent({
entityID: ancestorID,
processName: ancestorID,
timestamp: 1,
timestamp: 1600863932317,
parentEntityID: undefined,
});
const ancestorClone: SafeResolverEvent = mockEndpointEvent({
entityID: ancestorID,
processName: ancestorID,
timestamp: 1,
timestamp: 1600863932317,
parentEntityID: undefined,
});
const origin: SafeResolverEvent = mockEndpointEvent({
entityID: originID,
processName: originID,
parentEntityID: ancestorID,
timestamp: 0,
timestamp: 1600863932316,
});
const originClone: SafeResolverEvent = mockEndpointEvent({
entityID: originID,
processName: originID,
parentEntityID: ancestorID,
timestamp: 0,
timestamp: 1600863932316,
});
const firstChild: SafeResolverEvent = mockEndpointEvent({
entityID: firstChildID,
processName: firstChildID,
parentEntityID: originID,
timestamp: 1,
timestamp: 1600863932317,
});
const firstChildClone: SafeResolverEvent = mockEndpointEvent({
entityID: firstChildID,
processName: firstChildID,
parentEntityID: originID,
timestamp: 1,
timestamp: 1600863932317,
});
const secondChild: SafeResolverEvent = mockEndpointEvent({
entityID: secondChildID,
processName: secondChildID,
parentEntityID: originID,
timestamp: 2,
timestamp: 1600863932318,
});
const secondChildClone: SafeResolverEvent = mockEndpointEvent({
entityID: secondChildID,
processName: secondChildID,
parentEntityID: originID,
timestamp: 2,
timestamp: 1600863932318,
});

return ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import React from 'react';
import { Store, createStore, applyMiddleware } from 'redux';
import { mount, ReactWrapper } from 'enzyme';
import { History as HistoryPackageHistoryInterface, createMemoryHistory } from 'history';
import { CoreStart } from '../../../../../../../src/core/public';
import { coreMock } from '../../../../../../../src/core/public/mocks';
import { spyMiddlewareFactory } from '../spy_middleware_factory';
import { resolverMiddlewareFactory } from '../../store/middleware';
Expand All @@ -17,6 +16,7 @@ import { MockResolver } from './mock_resolver';
import { ResolverState, DataAccessLayer, SpyMiddleware, SideEffectSimulator } from '../../types';
import { ResolverAction } from '../../store/actions';
import { sideEffectSimulatorFactory } from '../../view/side_effect_simulator_factory';
import { getUiSettings } from '../../mocks/get_ui_settings';

/**
* Test a Resolver instance using jest, enzyme, and a mock data layer.
Expand Down Expand Up @@ -91,7 +91,9 @@ export class Simulator {
this.history = history ?? createMemoryHistory();

// Used for `KibanaContextProvider`
const coreStart: CoreStart = coreMock.createStart();
const coreStart = coreMock.createStart();

coreStart.uiSettings.get.mockImplementation(getUiSettings);

this.sideEffectSimulator = sideEffectSimulatorFactory();

Expand Down Expand Up @@ -296,10 +298,7 @@ export class Simulator {
const title = titles.at(index).text();
const description = descriptions.at(index).text();

// Exclude timestamp since we can't currently calculate the expected description for it from tests
if (title !== '@timestamp') {
entries.push([title, description]);
}
entries.push([title, description]);
}
return entries;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and
title: 'c.ext',
titleIcon: 'Running Process',
detailEntries: [
['@timestamp', 'Sep 23, 2020 @ 08:25:32.316'],
['process.executable', 'executable'],
['process.pid', '0'],
['user.name', 'user.name'],
Expand Down Expand Up @@ -128,6 +129,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and
await expect(
simulator().map(() => simulator().nodeDetailDescriptionListEntries())
).toYieldEqualTo([
['@timestamp', 'Sep 23, 2020 @ 08:25:32.317'],
['process.executable', 'executable'],
['process.pid', '1'],
['user.name', 'user.name'],
Expand Down Expand Up @@ -168,6 +170,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and
await expect(
simulator().map(() => simulator().nodeDetailDescriptionListEntries())
).toYieldEqualTo([
['@timestamp', 'Sep 23, 2020 @ 08:25:32.316'],
['process.executable', 'executable'],
['process.pid', '0'],
['user.name', 'user.name'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import styled from 'styled-components';
import { useSelector } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { StyledPanel } from '../styles';
import { BoldCode, StyledTime, GeneratedText, formatDate } from './panel_content_utilities';
import {
BoldCode,
StyledTime,
GeneratedText,
noTimestampRetrievedText,
} from './panel_content_utilities';
import { Breadcrumbs } from './breadcrumbs';
import * as eventModel from '../../../../common/endpoint/models/event';
import * as selectors from '../../store/selectors';
Expand All @@ -25,6 +30,7 @@ import { DescriptiveName } from './descriptive_name';
import { useLinkProps } from '../use_link_props';
import { SafeResolverEvent } from '../../../../common/endpoint/types';
import { deepObjectEntries } from './deep_object_entries';
import { useFormattedDate } from './use_formatted_date';

export const EventDetail = memo(function EventDetail({
nodeID,
Expand Down Expand Up @@ -78,12 +84,8 @@ const EventDetailContents = memo(function ({
eventType: string;
processEvent: SafeResolverEvent;
}) {
const formattedDate = useMemo(() => {
const timestamp = eventModel.timestampSafeVersion(event);
if (timestamp !== undefined) {
return formatDate(new Date(timestamp));
}
}, [event]);
const timestamp = eventModel.timestampSafeVersion(event);
const formattedDate = useFormattedDate(timestamp) || noTimestampRetrievedText;

return (
<StyledPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { EuiDescriptionListProps } from '@elastic/eui/src/components/description
import { StyledDescriptionList, StyledTitle } from './styles';
import * as selectors from '../../store/selectors';
import * as eventModel from '../../../../common/endpoint/models/event';
import { formatDate, GeneratedText } from './panel_content_utilities';
import { GeneratedText } from './panel_content_utilities';
import { Breadcrumbs } from './breadcrumbs';
import { processPath, processPID } from '../../models/process_event';
import { CubeForProcess } from './cube_for_process';
Expand All @@ -26,6 +26,7 @@ import { ResolverState } from '../../types';
import { PanelLoading } from './panel_loading';
import { StyledPanel } from '../styles';
import { useLinkProps } from '../use_link_props';
import { useFormattedDate } from './use_formatted_date';

const StyledCubeForProcess = styled(CubeForProcess)`
position: relative;
Expand Down Expand Up @@ -65,10 +66,10 @@ const NodeDetailView = memo(function ({
const relatedEventTotal = useSelector((state: ResolverState) => {
return selectors.relatedEventTotalCount(state)(nodeID);
});
const processInfoEntry: EuiDescriptionListProps['listItems'] = useMemo(() => {
const eventTime = eventModel.eventTimestamp(processEvent);
const dateTime = eventTime === undefined ? null : formatDate(eventTime);
const eventTime = eventModel.eventTimestamp(processEvent);
const dateTime = useFormattedDate(eventTime);

const processInfoEntry: EuiDescriptionListProps['listItems'] = useMemo(() => {
const createdEntry = {
title: '@timestamp',
description: dateTime,
Expand Down Expand Up @@ -131,7 +132,7 @@ const NodeDetailView = memo(function ({
});

return processDescriptionListData;
}, [processEvent]);
}, [dateTime, processEvent]);

const nodesLinkNavProps = useLinkProps({
panelView: 'nodes',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/
import { useSelector } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { StyledPanel } from '../styles';
import { formatDate, BoldCode, StyledTime } from './panel_content_utilities';
import { BoldCode, noTimestampRetrievedText, StyledTime } from './panel_content_utilities';
import { Breadcrumbs } from './breadcrumbs';
import * as eventModel from '../../../../common/endpoint/models/event';
import { SafeResolverEvent } from '../../../../common/endpoint/types';
Expand All @@ -19,6 +19,7 @@ import { ResolverState } from '../../types';
import { PanelLoading } from './panel_loading';
import { DescriptiveName } from './descriptive_name';
import { useLinkProps } from '../use_link_props';
import { useFormattedDate } from './use_formatted_date';

/**
* Render a list of events that are related to `nodeID` and that have a category of `eventType`.
Expand Down Expand Up @@ -83,7 +84,7 @@ const NodeEventsListItem = memo(function ({
eventType: string;
}) {
const timestamp = eventModel.eventTimestamp(event);
const date = timestamp !== undefined ? formatDate(timestamp) : timestamp;
const date = useFormattedDate(timestamp) || noTimestampRetrievedText;
const linkProps = useLinkProps({
panelView: 'eventDetail',
panelParameters: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
} from './styles';
import * as eventModel from '../../../../common/endpoint/models/event';
import * as selectors from '../../store/selectors';
import { formatter } from './panel_content_utilities';
import { Breadcrumbs } from './breadcrumbs';
import { CubeForProcess } from './cube_for_process';
import { LimitWarning } from '../limit_warnings';
Expand All @@ -41,6 +40,8 @@ import { useLinkProps } from '../use_link_props';
import { useColors } from '../use_colors';
import { SafeResolverEvent } from '../../../../common/endpoint/types';
import { ResolverAction } from '../../store/actions';
import { useFormattedDate } from './use_formatted_date';
import { getEmptyTagValue } from '../../../common/components/empty_value';

interface ProcessTableView {
name?: string;
Expand Down Expand Up @@ -80,18 +81,7 @@ export const NodeList = memo(() => {
dataType: 'date',
sortable: true,
render(eventDate?: Date) {
return eventDate ? (
formatter.format(eventDate)
) : (
<EuiBadge color="warning">
{i18n.translate(
'xpack.securitySolution.endpoint.resolver.panel.table.row.timestampInvalidLabel',
{
defaultMessage: 'invalid',
}
)}
</EuiBadge>
);
return <NodeDetailTimestamp eventDate={eventDate} />;
},
},
],
Expand Down Expand Up @@ -220,3 +210,9 @@ function NodeDetailLink({
</EuiButtonEmpty>
);
}

const NodeDetailTimestamp = memo(({ eventDate }: { eventDate: Date | undefined }) => {
const formattedDate = useFormattedDate(eventDate);

return formattedDate ? <>{formattedDate}</> : getEmptyTagValue();
});
Loading

0 comments on commit 9898a76

Please sign in to comment.