diff --git a/x-pack/plugins/security_solution/public/resolver/mocks/get_ui_settings.ts b/x-pack/plugins/security_solution/public/resolver/mocks/get_ui_settings.ts new file mode 100644 index 00000000000000..ab1a5c86859ac5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/mocks/get_ui_settings.ts @@ -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'; + } +} diff --git a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts index 8691ecac4d1ccf..3f7c58efc762b9 100644 --- a/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts @@ -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, @@ -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 ({ @@ -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 { @@ -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 ({ diff --git a/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx b/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx index 9d10d1c2b64a77..ea603f25834313 100644 --- a/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx +++ b/x-pack/plugins/security_solution/public/resolver/test_utilities/simulator/index.tsx @@ -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'; @@ -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. @@ -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(); @@ -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; } diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx index 2f23469606acab..63a70716b2d417 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx @@ -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'], @@ -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'], @@ -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'], diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx index 72f0d54d51fa3b..168752c507d5a9 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx @@ -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'; @@ -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, @@ -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 ( diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx index 04e9de61f62568..181c9ac8ab8a08 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx @@ -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'; @@ -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; @@ -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, @@ -131,7 +132,7 @@ const NodeDetailView = memo(function ({ }); return processDescriptionListData; - }, [processEvent]); + }, [dateTime, processEvent]); const nodesLinkNavProps = useLinkProps({ panelView: 'nodes', diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx index 281794ac24d244..771a143a9c0cde 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx @@ -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'; @@ -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`. @@ -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: { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_list.tsx index 8fc6e7cc66c790..78d3477301539f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_list.tsx @@ -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'; @@ -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; @@ -80,18 +81,7 @@ export const NodeList = memo(() => { dataType: 'date', sortable: true, render(eventDate?: Date) { - return eventDate ? ( - formatter.format(eventDate) - ) : ( - - {i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.table.row.timestampInvalidLabel', - { - defaultMessage: 'invalid', - } - )} - - ); + return ; }, }, ], @@ -220,3 +210,9 @@ function NodeDetailLink({ ); } + +const NodeDetailTimestamp = memo(({ eventDate }: { eventDate: Date | undefined }) => { + const formattedDate = useFormattedDate(eventDate); + + return formattedDate ? <>{formattedDate} : getEmptyTagValue(); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 5ca34b33b2396e..a20498cbfb67b2 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -6,11 +6,22 @@ /* eslint-disable react/display-name */ -import { i18n } from '@kbn/i18n'; import { EuiCode } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import React, { memo } from 'react'; +/** + * Text to use in place of an undefined timestamp value + */ + +export const noTimestampRetrievedText = i18n.translate( + 'xpack.securitySolution.enpdoint.resolver.panelutils.noTimestampRetrieved', + { + defaultMessage: 'No timestamp retrieved', + } +); + /** * A bold version of EuiCode to display certain titles with */ @@ -59,33 +70,3 @@ export const StyledTime = memo(styled('time')` display: inline-block; text-align: start; `); - -/** - * Long formatter (to second) for DateTime - */ -export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', -}); - -/** - * @returns {string} A nicely formatted string for a date - */ -export function formatDate( - /** To be passed through Date->Intl.DateTimeFormat */ timestamp: ConstructorParameters< - typeof Date - >[0] -): string { - const date = new Date(timestamp); - if (isFinite(date.getTime())) { - return formatter.format(date); - } else { - return i18n.translate('xpack.securitySolution.enpdoint.resolver.panelutils.invaliddate', { - defaultMessage: 'Invalid Date', - }); - } -} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.test.tsx new file mode 100644 index 00000000000000..9e9ae26900efaf --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.test.tsx @@ -0,0 +1,106 @@ +/* + * 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. + */ + +import React from 'react'; +import { render, RenderResult } from '@testing-library/react'; +import { useFormattedDate } from './use_formatted_date'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; +import { getUiSettings } from '../../mocks/get_ui_settings'; + +describe('useFormattedDate', () => { + let element: HTMLElement; + const testID = 'formattedDate'; + let reactRenderResult: ( + date: ConstructorParameters[0] | Date | undefined + ) => RenderResult; + + beforeEach(async () => { + const mockCoreStart = coreMock.createStart(); + mockCoreStart.uiSettings.get.mockImplementation(getUiSettings); + + function Test({ date }: { date: ConstructorParameters[0] | Date | undefined }) { + const formattedDate = useFormattedDate(date); + return
{formattedDate}
; + } + + reactRenderResult = ( + date: ConstructorParameters[0] | Date | undefined + ): RenderResult => + render( + + + + ); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('when the provided date is undefined', () => { + it('should return undefined', async () => { + const { findByTestId } = reactRenderResult(undefined); + element = await findByTestId(testID); + + expect(element).toBeEmpty(); + }); + }); + + describe('when the provided date is empty', () => { + it('should return undefined', async () => { + const { findByTestId } = reactRenderResult(''); + element = await findByTestId(testID); + + expect(element).toBeEmpty(); + }); + }); + + describe('when the provided date is an invalid date', () => { + it('should return the string invalid date', async () => { + const { findByTestId } = reactRenderResult('randomString'); + element = await findByTestId(testID); + + expect(element).toHaveTextContent('Invalid Date'); + }); + }); + + describe('when the provided date is a stringified unix timestamp', () => { + it('should return the string invalid date', async () => { + const { findByTestId } = reactRenderResult('1600863932316'); + element = await findByTestId(testID); + + expect(element).toHaveTextContent('Invalid Date'); + }); + }); + + describe('when the provided date is a valid numerical timestamp', () => { + it('should return the string invalid date', async () => { + const { findByTestId } = reactRenderResult(1600863932316); + element = await findByTestId(testID); + + expect(element).toHaveTextContent('Sep 23, 2020 @ 08:25:32.316'); + }); + }); + + describe('when the provided date is a date string', () => { + it('should return the string invalid date', async () => { + const { findByTestId } = reactRenderResult('2020-09-23T12:25:32Z'); + element = await findByTestId(testID); + + expect(element).toHaveTextContent('Sep 23, 2020 @ 08:25:32.000'); + }); + }); + + describe('when the provided date is a valid date', () => { + it('should return the string invalid date', async () => { + const validDate = new Date(1600863932316); + const { findByTestId } = reactRenderResult(validDate); + element = await findByTestId(testID); + + expect(element).toHaveTextContent('Sep 23, 2020 @ 08:25:32.316'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.ts b/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.ts new file mode 100644 index 00000000000000..05e7154dd6fdd3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/use_formatted_date.ts @@ -0,0 +1,53 @@ +/* + * 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. + */ +import { i18n } from '@kbn/i18n'; +import moment from 'moment-timezone'; +import { useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; + +const invalidDateText = i18n.translate( + 'xpack.securitySolution.enpdoint.resolver.panelutils.invaliddate', + { + defaultMessage: 'Invalid Date', + } +); + +/** + * Long formatter (to second) for DateTime + */ +const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', +}); + +/** + * + * @description formats a given time based on the user defined format in the advanced settings section of kibana under dateFormat + * @export + * @param {(ConstructorParameters[0] | undefined)} timestamp + * @returns {(string | null)} - Either a formatted date or the text 'Invalid Date' + */ +export function useFormattedDate( + timestamp: ConstructorParameters[0] | Date | undefined +): string | undefined { + const dateFormatSetting: string = useUiSetting('dateFormat'); + const timezoneSetting: string = useUiSetting('dateFormat:tz'); + const usableTimezoneSetting = timezoneSetting === 'Browser' ? moment.tz.guess() : timezoneSetting; + + if (!timestamp) return undefined; + + const date = new Date(timestamp); + if (date && Number.isFinite(date.getTime())) { + return dateFormatSetting + ? moment.tz(date, usableTimezoneSetting).format(dateFormatSetting) + : formatter.format(date); + } + + return invalidDateText; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f0540ac33edf05..ba46c216d3361f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -15887,7 +15887,6 @@ "xpack.securitySolution.endpoint.resolver.panel.table.row.count": "カウント", "xpack.securitySolution.endpoint.resolver.panel.table.row.eventType": "イベントタイプ", "xpack.securitySolution.endpoint.resolver.panel.table.row.processNameTitle": "プロセス名", - "xpack.securitySolution.endpoint.resolver.panel.table.row.timestampInvalidLabel": "無効", "xpack.securitySolution.endpoint.resolver.panel.table.row.timestampTitle": "タイムスタンプ", "xpack.securitySolution.endpoint.resolver.panel.table.row.valueMissingDescription": "値が見つかりません", "xpack.securitySolution.endpoint.resolver.relatedEventLimitExceeded": "{numberOfEventsMissing} {category}件のイベントを表示できませんでした。データの上限に達しました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 277205aac774dc..ceefe8a00eea0d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -15897,7 +15897,6 @@ "xpack.securitySolution.endpoint.resolver.panel.table.row.count": "计数", "xpack.securitySolution.endpoint.resolver.panel.table.row.eventType": "事件类型", "xpack.securitySolution.endpoint.resolver.panel.table.row.processNameTitle": "进程名称", - "xpack.securitySolution.endpoint.resolver.panel.table.row.timestampInvalidLabel": "无效", "xpack.securitySolution.endpoint.resolver.panel.table.row.timestampTitle": "时间戳", "xpack.securitySolution.endpoint.resolver.panel.table.row.valueMissingDescription": "值缺失", "xpack.securitySolution.endpoint.resolver.relatedEventLimitExceeded": "{numberOfEventsMissing} 个{category}事件无法显示,因为已达到数据限制。",