From 10428fd51ded4d27d51b83287efeea99fab2edd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Wed, 14 Oct 2020 23:29:20 +0200 Subject: [PATCH] [Security Solution] Fix networkTopNFlow search strategy response (#80362) (#80581) --- .../security_solution/common/ecs/geo/index.ts | 7 - .../common/ecs/source/index.ts | 5 - .../__snapshots__/helpers.test.tsx.snap | 4 - .../common/components/tables/helpers.tsx | 8 +- .../anomalies_query_tab_body/index.tsx | 2 + .../anomalies_query_tab_body/types.ts | 1 + .../authentications_table/index.tsx | 28 +--- .../uncommon_process_table/index.test.tsx | 4 +- .../components/embeddables/embedded_map.tsx | 9 +- .../__snapshots__/map_tool_tip.test.tsx.snap | 44 +++--- .../point_tool_tip_content.test.tsx.snap | 1 - .../embeddables/map_tool_tip/map_tool_tip.tsx | 140 ++++++++++-------- .../point_tool_tip_content.test.tsx | 8 +- .../map_tool_tip/point_tool_tip_content.tsx | 50 ++++--- .../source_destination/country_flag.tsx | 20 ++- .../network/containers/network_http/index.tsx | 3 +- .../network_top_countries/index.tsx | 4 + .../containers/network_top_n_flow/index.tsx | 9 +- .../field_renderers/field_renderers.tsx | 34 +++-- .../helpers/format_response_object_values.ts | 26 ++++ .../search_strategy/helpers/to_array.ts | 5 +- .../factory/hosts/all/helpers.ts | 4 +- .../factory/hosts/authentications/helpers.ts | 4 +- .../factory/hosts/details/helpers.ts | 4 +- .../hosts/uncommon_processes/helpers.ts | 4 +- .../network/top_n_flow/__mocks__/index.ts | 132 ++++++++++------- .../factory/network/top_n_flow/helpers.ts | 15 +- .../timeline/factory/events/all/helpers.ts | 8 +- .../apis/security_solution/authentications.ts | 4 + 29 files changed, 327 insertions(+), 260 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/helpers/format_response_object_values.ts diff --git a/x-pack/plugins/security_solution/common/ecs/geo/index.ts b/x-pack/plugins/security_solution/common/ecs/geo/index.ts index 409b5bbdc17a4..4a4c76adb097b 100644 --- a/x-pack/plugins/security_solution/common/ecs/geo/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/geo/index.ts @@ -6,22 +6,15 @@ export interface GeoEcs { city_name?: string[]; - continent_name?: string[]; - country_iso_code?: string[]; - country_name?: string[]; - location?: Location; - region_iso_code?: string[]; - region_name?: string[]; } export interface Location { lon?: number[]; - lat?: number[]; } diff --git a/x-pack/plugins/security_solution/common/ecs/source/index.ts b/x-pack/plugins/security_solution/common/ecs/source/index.ts index 9e6b6563cec68..2c8618f4edcd0 100644 --- a/x-pack/plugins/security_solution/common/ecs/source/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/source/index.ts @@ -8,14 +8,9 @@ import { GeoEcs } from '../geo'; export interface SourceEcs { bytes?: number[]; - ip?: string[]; - port?: number[]; - domain?: string[]; - geo?: GeoEcs; - packets?: number[]; } diff --git a/x-pack/plugins/security_solution/public/common/components/tables/__snapshots__/helpers.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/tables/__snapshots__/helpers.test.tsx.snap index 5372ccfcd1188..b585bfc613315 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/__snapshots__/helpers.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/tables/__snapshots__/helpers.test.tsx.snap @@ -47,8 +47,6 @@ exports[`Table Helpers #getRowItemDraggables it returns correctly against snapsh key="idPrefix-attrName-item1-0" render={[Function]} /> - , - - , - - {index !== 0 && ( - <> - {','} - - - )} = ({ AnomaliesTableComponent, flowTarget, ip, + hostName, indexNames, }) => { const { jobs } = useInstalledSecurityJobs(); @@ -71,6 +72,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ narrowDateRange={narrowDateRange} flowTarget={flowTarget} ip={ip} + hostName={hostName} /> ); diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts index 3ce4b8b6d4494..7621749348a90 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts @@ -32,4 +32,5 @@ export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & { updateDateRange?: UpdateDateRange; hideHistogramIfEmpty?: boolean; ip?: string; + hostName?: string; }; diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx index 3d291d9bf7b28..88fd1ad5f98b0 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx @@ -256,12 +256,7 @@ const getAuthenticationColumns = (): AuthTableColumns => [ hideForMobile: false, render: ({ node }) => getRowItemDraggables({ - rowItems: - node.lastSuccess != null && - node.lastSuccess.source != null && - node.lastSuccess.source.ip != null - ? node.lastSuccess.source.ip - : null, + rowItems: node.lastSuccess?.source?.ip || null, attrName: 'source.ip', idPrefix: `authentications-table-${node._id}-lastSuccessSource`, render: (item) => , @@ -273,12 +268,7 @@ const getAuthenticationColumns = (): AuthTableColumns => [ hideForMobile: false, render: ({ node }) => getRowItemDraggables({ - rowItems: - node.lastSuccess != null && - node.lastSuccess.host != null && - node.lastSuccess.host.name != null - ? node.lastSuccess.host.name - : null, + rowItems: node.lastSuccess?.host?.name ?? null, attrName: 'host.name', idPrefix: `authentications-table-${node._id}-lastSuccessfulDestination`, render: (item) => , @@ -301,12 +291,7 @@ const getAuthenticationColumns = (): AuthTableColumns => [ hideForMobile: false, render: ({ node }) => getRowItemDraggables({ - rowItems: - node.lastFailure != null && - node.lastFailure.source != null && - node.lastFailure.source.ip != null - ? node.lastFailure.source.ip - : null, + rowItems: node.lastFailure?.source?.ip || null, attrName: 'source.ip', idPrefix: `authentications-table-${node._id}-lastFailureSource`, render: (item) => , @@ -318,12 +303,7 @@ const getAuthenticationColumns = (): AuthTableColumns => [ hideForMobile: false, render: ({ node }) => getRowItemDraggables({ - rowItems: - node.lastFailure != null && - node.lastFailure.host != null && - node.lastFailure.host.name != null - ? node.lastFailure.host.name - : null, + rowItems: node.lastFailure?.host?.name || null, attrName: 'host.name', idPrefix: `authentications-table-${node._id}-lastFailureDestination`, render: (item) => , diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx index 41f443f14cafe..54cb0c0883e14 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx @@ -129,7 +129,7 @@ describe('Uncommon Process Table Component', () => { ); expect(wrapper.find('.euiTableRow').at(2).find('.euiTableRowCell').at(3).text()).toBe( - 'Host nameshello-world,hello-world-2 ' + 'Host nameshello-worldhello-world-2 ' ); }); @@ -214,7 +214,7 @@ describe('Uncommon Process Table Component', () => { ); expect(wrapper.find('.euiTableRow').at(4).find('.euiTableRowCell').at(3).text()).toBe( - 'Host nameshello-world,hello-world-2 ' + 'Host nameshello-worldhello-world-2 ' ); }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx index 7ae8aecdab606..ac7c5078e4ba0 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx @@ -198,15 +198,13 @@ export const EmbeddedMapComponent = ({ if (embeddable != null) { embeddable.updateInput({ query }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query]); + }, [embeddable, query]); useEffect(() => { if (embeddable != null) { embeddable.updateInput({ filters }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filters]); + }, [embeddable, filters]); // DateRange updated useEffect useEffect(() => { @@ -217,8 +215,7 @@ export const EmbeddedMapComponent = ({ }; embeddable.updateInput({ timeRange }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [startDate, endDate]); + }, [embeddable, startDate, endDate]); return isError ? null : ( diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/map_tool_tip.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/map_tool_tip.test.tsx.snap index 775329553cbeb..dc94b1039dfc5 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/map_tool_tip.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/map_tool_tip.test.tsx.snap @@ -1,29 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MapToolTip full component renders correctly against snapshot 1`] = ` - - - - - + + + + + `; exports[`MapToolTip placeholder component renders correctly against snapshot 1`] = ` - - - - - + + + + + `; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap index 8927e492993d0..8801e455c95b6 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap @@ -2,7 +2,6 @@ exports[`PointToolTipContent renders correctly against snapshot 1`] = ` (null); const [, setLayerName] = useState(''); + const handleCloseTooltip = useCallback(() => { + if (closeTooltip != null) { + closeTooltip(); + setFeatureIndex(0); + } + }, [closeTooltip]); + + const handlePreviousFeature = useCallback(() => { + setFeatureIndex((prevFeatureIndex) => prevFeatureIndex - 1); + setIsLoadingNextFeature(true); + }, []); + + const handleNextFeature = useCallback(() => { + setFeatureIndex((prevFeatureIndex) => prevFeatureIndex + 1); + setIsLoadingNextFeature(true); + }, []); + + const content = useMemo(() => { + if (isError) { + return ( + + {i18n.MAP_TOOL_TIP_ERROR} + + ); + } + + if (isLoading && !isLoadingNextFeature) { + return ( + + + + + + ); + } + + return ( +
+ {featureGeometry != null && featureGeometry.type === 'LineString' ? ( + + ) : ( + + )} + {features.length > 1 && ( + + )} + {isLoadingNextFeature && } +
+ ); + }, [ + featureGeometry, + featureIndex, + featureProps, + features, + handleNextFeature, + handlePreviousFeature, + isError, + isLoading, + isLoadingNextFeature, + ]); + useEffect(() => { // Early return if component doesn't yet have props -- result of mounting in portal before actual rendering if ( @@ -77,69 +149,17 @@ export const MapToolTipComponent = ({ }; fetchFeatureProps(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ featureIndex, - // eslint-disable-next-line react-hooks/exhaustive-deps - features - .map((f) => `${f.id}-${f.layerId}`) - .sort() - .join(), + features, + getLayerName, + isLoadingNextFeature, + loadFeatureGeometry, + loadFeatureProperties, ]); - if (isError) { - return ( - - {i18n.MAP_TOOL_TIP_ERROR} - - ); - } - - return isLoading && !isLoadingNextFeature ? ( - - - - - - ) : ( - { - if (closeTooltip != null) { - closeTooltip(); - setFeatureIndex(0); - } - }} - > -
- {featureGeometry != null && featureGeometry.type === 'LineString' ? ( - - ) : ( - - )} - {features.length > 1 && ( - { - setFeatureIndex(featureIndex - 1); - setIsLoadingNextFeature(true); - }} - nextFeature={() => { - setFeatureIndex(featureIndex + 1); - setIsLoadingNextFeature(true); - }} - /> - )} - {isLoadingNextFeature && } -
-
+ return ( + {content} ); }; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx index 27fe27adc99c2..87b972e9d7053 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx @@ -24,15 +24,9 @@ describe('PointToolTipContent', () => { ]; test('renders correctly against snapshot', () => { - const closeTooltip = jest.fn(); - const wrapper = shallow( - + ); expect(wrapper.find('PointToolTipContentComponent')).toMatchSnapshot(); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx index 57113a1395778..a3a5ddf4d53b3 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { sourceDestinationFieldMappings } from '../map_config'; import { getEmptyTagValue, @@ -20,36 +20,38 @@ import { ITooltipProperty } from '../../../../../../maps/public/classes/tooltips interface PointToolTipContentProps { contextId: string; featureProps: ITooltipProperty[]; - closeTooltip?(): void; } export const PointToolTipContentComponent = ({ contextId, featureProps, - closeTooltip, }: PointToolTipContentProps) => { - const featureDescriptionListItems = featureProps.map((featureProp) => { - const key = featureProp.getPropertyKey(); - const value = featureProp.getRawValue() ?? []; + const featureDescriptionListItems = useMemo( + () => + featureProps.map((featureProp) => { + const key = featureProp.getPropertyKey(); + const value = featureProp.getRawValue() ?? []; - return { - title: sourceDestinationFieldMappings[key], - description: ( - <> - {value != null ? ( - getRenderedFieldValue(key, item)} - /> - ) : ( - getEmptyTagValue() - )} - - ), - }; - }); + return { + title: sourceDestinationFieldMappings[key], + description: ( + <> + {value != null ? ( + getRenderedFieldValue(key, item)} + /> + ) : ( + getEmptyTagValue() + )} + + ), + }; + }), + [contextId, featureProps] + ); return ; }; diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx index cb1af5513c846..31ad679ce41bf 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/country_flag.tsx @@ -9,6 +9,13 @@ import { isEmpty } from 'lodash/fp'; import { EuiToolTip } from '@elastic/eui'; import countries from 'i18n-iso-countries'; import countryJson from 'i18n-iso-countries/langs/en.json'; +import styled from 'styled-components'; + +// Fixes vertical alignment of the flag +const FlagWrapper = styled.span` + position: relative; + top: 1px; +`; /** * Returns the flag for the specified country code, or null if the specified @@ -38,10 +45,10 @@ export const CountryFlag = memo<{ if (flag !== null) { return displayCountryNameOnHover ? ( - {flag} + {flag} ) : ( - {flag} + {flag} ); } return null; @@ -49,7 +56,7 @@ export const CountryFlag = memo<{ CountryFlag.displayName = 'CountryFlag'; -/** Renders an emjoi flag with country name for the specified country code */ +/** Renders an emoji flag with country name for the specified country code */ export const CountryFlagAndName = memo<{ countryCode: string; displayCountryNameOnHover?: boolean; @@ -67,10 +74,13 @@ export const CountryFlagAndName = memo<{ if (flag !== null && localesLoaded) { return displayCountryNameOnHover ? ( - {flag} + {flag} ) : ( - {`${flag} ${countries.getName(countryCode, 'en')}`} + <> + {flag} + {` ${countries.getName(countryCode, 'en')}`} + ); } return null; diff --git a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx index 12c3cc481cfc1..356173fa2ac71 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx @@ -198,6 +198,7 @@ export const useNetworkHttp = ({ factoryQueryType: NetworkQueries.http, filterQuery: createFilter(filterQuery), id: ID, + ip, pagination: generateTablePaginationOptions(activePage, limit), sort: sort as SortField, timerange: { @@ -211,7 +212,7 @@ export const useNetworkHttp = ({ } return prevRequest; }); - }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip]); + }, [activePage, indexNames, endDate, filterQuery, ip, limit, startDate, sort, skip]); useEffect(() => { networkHttpSearch(networkHttpRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx index 0b864d66842d1..c2dc638fa719f 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx @@ -61,6 +61,7 @@ export const useNetworkTopCountries = ({ filterQuery, flowTarget, indexNames, + ip, skip, startDate, type, @@ -86,6 +87,7 @@ export const useNetworkTopCountries = ({ filterQuery: createFilter(filterQuery), flowTarget, id: queryId, + ip, pagination: generateTablePaginationOptions(activePage, limit), sort, timerange: { @@ -203,6 +205,7 @@ export const useNetworkTopCountries = ({ filterQuery: createFilter(filterQuery), flowTarget, id: queryId, + ip, pagination: generateTablePaginationOptions(activePage, limit), sort, timerange: { @@ -221,6 +224,7 @@ export const useNetworkTopCountries = ({ indexNames, endDate, filterQuery, + ip, limit, startDate, sort, diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx index c68ad2422c514..87968e7a03522 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx @@ -61,6 +61,7 @@ export const useNetworkTopNFlow = ({ filterQuery, flowTarget, indexNames, + ip, skip, startDate, type, @@ -84,7 +85,8 @@ export const useNetworkTopNFlow = ({ factoryQueryType: NetworkQueries.topNFlow, filterQuery: createFilter(filterQuery), flowTarget, - id: ID, + id: `${ID}-${flowTarget}`, + ip, pagination: generateTablePaginationOptions(activePage, limit), sort, timerange: { @@ -199,7 +201,8 @@ export const useNetworkTopNFlow = ({ factoryQueryType: NetworkQueries.topNFlow, filterQuery: createFilter(filterQuery), flowTarget, - id: ID, + id: `${ID}-${flowTarget}`, + ip, pagination: generateTablePaginationOptions(activePage, limit), timerange: { interval: '12h', @@ -213,7 +216,7 @@ export const useNetworkTopNFlow = ({ } return prevRequest; }); - }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip, flowTarget]); + }, [activePage, endDate, filterQuery, indexNames, ip, limit, startDate, sort, skip, flowTarget]); useEffect(() => { networkTopNFlowSearch(networkTopNFlowRequest); diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx index 1f76c2840e8b7..cb913287b24d8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx @@ -7,7 +7,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { getOr } from 'lodash/fp'; -import React, { Fragment, useState } from 'react'; +import React, { useCallback, Fragment, useMemo, useState } from 'react'; import styled from 'styled-components'; import { HostEcs } from '../../../../common/ecs/host'; @@ -260,25 +260,31 @@ MoreContainer.displayName = 'MoreContainer'; export const DefaultFieldRendererOverflow = React.memo( ({ idPrefix, moreMaxHeight, overflowIndexStart = 5, render, rowItems }) => { const [isOpen, setIsOpen] = useState(false); + const handleClose = useCallback(() => setIsOpen(false), []); + const button = useMemo( + () => ( + <> + {' ,'} + + {`+${rowItems.length - overflowIndexStart} `} + + + + ), + [handleClose, overflowIndexStart, rowItems.length] + ); + return ( {rowItems.length > overflowIndexStart && ( - {' ,'} - setIsOpen(!isOpen)}> - {`+${rowItems.length - overflowIndexStart} `} - - - - } + button={button} isOpen={isOpen} - closePopover={() => setIsOpen(!isOpen)} + closePopover={handleClose} repositionOnScroll > + mapValues((o) => { + if (isObject(o) && !isArray(o)) { + return mapObjectValuesToStringArray(o); + } + + return toArray(o); + }, object); + +export const formatResponseObjectValues = (object: T | T[] | null) => { + if (object && typeof object === 'object') { + return mapObjectValuesToStringArray(object as object); + } + + return object; +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/helpers/to_array.ts b/x-pack/plugins/security_solution/server/search_strategy/helpers/to_array.ts index f7d9f408c5e2d..1aba6660677cd 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/helpers/to_array.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/helpers/to_array.ts @@ -4,5 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export const toArray = (value: T | T[] | null) => +export const toArray = (value: T | T[] | null): T[] => + Array.isArray(value) ? value : value == null ? [] : [value]; + +export const toStringArray = (value: T | T[] | null): T[] | string[] => Array.isArray(value) ? value : value == null ? [] : [`${value}`]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/helpers.ts index b06c36fd24e1a..55b54c8975214 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/helpers.ts @@ -9,7 +9,7 @@ import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { HostsEdges } from '../../../../../../common/search_strategy/security_solution/hosts'; import { HostAggEsItem, HostBuckets, HostValue } from '../../../../../lib/hosts/types'; -import { toArray } from '../../../../helpers/to_array'; +import { toStringArray } from '../../../../helpers/to_array'; export const HOSTS_FIELDS: readonly string[] = [ '_id', @@ -31,7 +31,7 @@ export const formatHostEdgesData = ( flattenedFields.cursor.value = hostId || ''; const fieldValue = getHostFieldValue(fieldName, bucket); if (fieldValue != null) { - return set(`node.${fieldName}`, toArray(fieldValue), flattenedFields); + return set(`node.${fieldName}`, toStringArray(fieldValue), flattenedFields); } return flattenedFields; }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/helpers.ts index ce8900a578102..e1924d6c27940 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/helpers.ts @@ -6,7 +6,7 @@ import { get, getOr, isEmpty } from 'lodash/fp'; import { set } from '@elastic/safer-lodash-set/fp'; import { mergeFieldsWithHit } from '../../../../../utils/build_query'; -import { toArray } from '../../../../helpers/to_array'; +import { toStringArray } from '../../../../helpers/to_array'; import { AuthenticationsEdges, AuthenticationHit, @@ -53,7 +53,7 @@ export const formatAuthenticationData = ( const fieldPath = `node.${fieldName}`; const fieldValue = get(fieldPath, mergedResult); if (!isEmpty(fieldValue)) { - return set(fieldPath, toArray(fieldValue), mergedResult); + return set(fieldPath, toStringArray(fieldValue), mergedResult); } else { return mergedResult; } diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index 644278963742d..36cf025304e76 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -7,7 +7,7 @@ import { set } from '@elastic/safer-lodash-set/fp'; import { get, has, head } from 'lodash/fp'; import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { HostItem } from '../../../../../../common/search_strategy/security_solution/hosts'; -import { toArray } from '../../../../helpers/to_array'; +import { toStringArray } from '../../../../helpers/to_array'; import { HostAggEsItem, HostBuckets, HostValue } from '../../../../../lib/hosts/types'; @@ -40,7 +40,7 @@ export const formatHostItem = (bucket: HostAggEsItem): HostItem => if (fieldName === '_id') { return set('_id', fieldValue, flattenedFields); } - return set(fieldName, toArray(fieldValue), flattenedFields); + return set(fieldName, toStringArray(fieldValue), flattenedFields); } return flattenedFields; }, {}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts index 20b3f5b05bc87..7d9351993bc85 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts @@ -12,7 +12,7 @@ import { HostsUncommonProcessesEdges, HostsUncommonProcessHit, } from '../../../../../../common/search_strategy/security_solution/hosts/uncommon_processes'; -import { toArray } from '../../../../helpers/to_array'; +import { toStringArray } from '../../../../helpers/to_array'; import { HostHits } from '../../../../../../common/search_strategy'; export const uncommonProcessesFields = [ @@ -79,7 +79,7 @@ export const formatUncommonProcessesData = ( fieldPath = `node.hosts.0.name`; fieldValue = get(fieldPath, mergedResult); } - return set(fieldPath, toArray(fieldValue), mergedResult); + return set(fieldPath, toStringArray(fieldValue), mergedResult); }, { node: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts index b1470b17eea5d..3e4070a28a9f8 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts @@ -11,6 +11,7 @@ import { FlowTargetSourceDest, NetworkQueries, NetworkTopNFlowRequestOptions, + NetworkTopNFlowStrategyResponse, NetworkTopTablesFields, } from '../../../../../../../common/search_strategy'; @@ -554,7 +555,7 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { loaded: 21, }; -export const formattedSearchStrategyResponse = { +export const formattedSearchStrategyResponse: NetworkTopNFlowStrategyResponse = { edges: [ { node: { @@ -579,13 +580,16 @@ export const formattedSearchStrategyResponse = { ip: '35.232.239.42', location: { geo: { - continent_name: 'North America', - region_iso_code: 'US-VA', - country_iso_code: 'US', - region_name: 'Virginia', - location: { lon: -77.2481, lat: 38.6583 }, + continent_name: ['North America'], + region_iso_code: ['US-VA'], + country_iso_code: ['US'], + region_name: ['Virginia'], + location: { + lon: [-77.2481], + lat: [38.6583], + }, }, - flowTarget: 'source', + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 15169, name: 'Google LLC' }, flows: 922, @@ -603,14 +607,17 @@ export const formattedSearchStrategyResponse = { ip: '151.101.200.204', location: { geo: { - continent_name: 'North America', - region_iso_code: 'US-VA', - city_name: 'Ashburn', - country_iso_code: 'US', - region_name: 'Virginia', - location: { lon: -77.4728, lat: 39.0481 }, - }, - flowTarget: 'source', + continent_name: ['North America'], + region_iso_code: ['US-VA'], + city_name: ['Ashburn'], + country_iso_code: ['US'], + region_name: ['Virginia'], + location: { + lon: [-77.4728], + lat: [39.0481], + }, + }, + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 54113, name: 'Fastly' }, flows: 2, @@ -628,14 +635,17 @@ export const formattedSearchStrategyResponse = { ip: '91.189.92.39', location: { geo: { - continent_name: 'Europe', - region_iso_code: 'GB-ENG', - city_name: 'London', - country_iso_code: 'GB', - region_name: 'England', - location: { lon: -0.0961, lat: 51.5132 }, - }, - flowTarget: 'source', + continent_name: ['Europe'], + region_iso_code: ['GB-ENG'], + city_name: ['London'], + country_iso_code: ['GB'], + region_name: ['England'], + location: { + lon: [-0.0961], + lat: [51.5132], + }, + }, + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 41231, name: 'Canonical Group Limited' }, flows: 1, @@ -668,14 +678,17 @@ export const formattedSearchStrategyResponse = { ip: '151.101.248.204', location: { geo: { - continent_name: 'North America', - region_iso_code: 'US-VA', - city_name: 'Ashburn', - country_iso_code: 'US', - region_name: 'Virginia', - location: { lon: -77.539, lat: 39.018 }, - }, - flowTarget: 'source', + continent_name: ['North America'], + region_iso_code: ['US-VA'], + city_name: ['Ashburn'], + country_iso_code: ['US'], + region_name: ['Virginia'], + location: { + lon: [-77.539], + lat: [39.018], + }, + }, + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 54113, name: 'Fastly' }, flows: 6, @@ -693,13 +706,16 @@ export const formattedSearchStrategyResponse = { ip: '35.196.129.83', location: { geo: { - continent_name: 'North America', - region_iso_code: 'US-VA', - country_iso_code: 'US', - region_name: 'Virginia', - location: { lon: -77.2481, lat: 38.6583 }, + continent_name: ['North America'], + region_iso_code: ['US-VA'], + country_iso_code: ['US'], + region_name: ['Virginia'], + location: { + lon: [-77.2481], + lat: [38.6583], + }, }, - flowTarget: 'source', + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 15169, name: 'Google LLC' }, flows: 1, @@ -717,11 +733,14 @@ export const formattedSearchStrategyResponse = { ip: '151.101.2.217', location: { geo: { - continent_name: 'North America', - country_iso_code: 'US', - location: { lon: -97.822, lat: 37.751 }, + continent_name: ['North America'], + country_iso_code: ['US'], + location: { + lon: [-97.822], + lat: [37.751], + }, }, - flowTarget: 'source', + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 54113, name: 'Fastly' }, flows: 24, @@ -739,14 +758,17 @@ export const formattedSearchStrategyResponse = { ip: '91.189.91.38', location: { geo: { - continent_name: 'North America', - region_iso_code: 'US-MA', - city_name: 'Boston', - country_iso_code: 'US', - region_name: 'Massachusetts', - location: { lon: -71.0631, lat: 42.3562 }, - }, - flowTarget: 'source', + continent_name: ['North America'], + region_iso_code: ['US-MA'], + city_name: ['Boston'], + country_iso_code: ['US'], + region_name: ['Massachusetts'], + location: { + lon: [-71.0631], + lat: [42.3562], + }, + }, + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 41231, name: 'Canonical Group Limited' }, flows: 1, @@ -764,11 +786,14 @@ export const formattedSearchStrategyResponse = { ip: '193.228.91.123', location: { geo: { - continent_name: 'North America', - country_iso_code: 'US', - location: { lon: -97.822, lat: 37.751 }, + continent_name: ['North America'], + country_iso_code: ['US'], + location: { + lon: [-97.822], + lat: [37.751], + }, }, - flowTarget: 'source', + flowTarget: FlowTargetSourceDest.source, }, autonomous_system: { number: 133766, name: 'YHSRV.LLC' }, flows: 33, @@ -846,6 +871,7 @@ export const formattedSearchStrategyResponse = { }, pageInfo: { activePage: 0, fakeTotalCount: 50, showMorePagesIndicator: true }, totalCount: 738, + rawResponse: {} as NetworkTopNFlowStrategyResponse['rawResponse'], }; export const expectedDsl = { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/helpers.ts index 720661e12bd96..0bf99aeea8a2d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/helpers.ts @@ -20,6 +20,7 @@ import { AutonomousSystemItem, } from '../../../../../../common/search_strategy'; import { getOppositeField } from '../helpers'; +import { formatResponseObjectValues } from '../../../../helpers/format_response_object_values'; export const getTopNFlowEdges = ( response: IEsSearchResponse, @@ -66,12 +67,14 @@ const getFlowTargetFromString = (flowAsString: string) => const getGeoItem = (result: NetworkTopNFlowBuckets): GeoItem | null => result.location.top_geo.hits.hits.length > 0 && result.location.top_geo.hits.hits[0]._source ? { - geo: getOr( - '', - `location.top_geo.hits.hits[0]._source.${ - Object.keys(result.location.top_geo.hits.hits[0]._source)[0] - }.geo`, - result + geo: formatResponseObjectValues( + getOr( + '', + `location.top_geo.hits.hits[0]._source.${ + Object.keys(result.location.top_geo.hits.hits[0]._source)[0] + }.geo`, + result + ) ), flowTarget: getFlowTargetFromString( Object.keys(result.location.top_geo.hits.hits[0]._source)[0] diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts index b2e3989f99d4f..8e2bfb5426610 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts @@ -6,7 +6,7 @@ import { get, has, merge, uniq } from 'lodash/fp'; import { EventHit, TimelineEdges } from '../../../../../../common/search_strategy'; -import { toArray } from '../../../../helpers/to_array'; +import { toStringArray } from '../../../../helpers/to_array'; export const formatTimelineData = ( dataFields: readonly string[], @@ -56,8 +56,8 @@ const mergeTimelineFieldsWithHit = ( { field: fieldName, value: specialFields.includes(esField) - ? toArray(get(esField, hit)) - : toArray(get(esField, hit._source)), + ? toStringArray(get(esField, hit)) + : toStringArray(get(esField, hit._source)), }, ] : get('node.data', flattenedFields), @@ -68,7 +68,7 @@ const mergeTimelineFieldsWithHit = ( ...fieldName.split('.').reduceRight( // @ts-expect-error (obj, next) => ({ [next]: obj }), - toArray(get(esField, hit._source)) + toStringArray(get(esField, hit._source)) ), } : get('node.ecs', flattenedFields), diff --git a/x-pack/test/api_integration/apis/security_solution/authentications.ts b/x-pack/test/api_integration/apis/security_solution/authentications.ts index c0a3570c9d8e2..7073658ab3ccc 100644 --- a/x-pack/test/api_integration/apis/security_solution/authentications.ts +++ b/x-pack/test/api_integration/apis/security_solution/authentications.ts @@ -14,6 +14,7 @@ const TO = '3000-01-01T00:00:00.000Z'; // typical values that have to change after an update from "scripts/es_archiver" const HOST_NAME = 'zeek-newyork-sha-aa8df15'; +const LAST_SUCCESS_SOURCE_IP = '8.42.77.171'; const TOTAL_COUNT = 3; const EDGE_LENGTH = 1; @@ -78,6 +79,9 @@ export default function ({ getService }: FtrProviderContext) { expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); + expect(authentications.edges[0]!.node.lastSuccess!.source!.ip).to.eql([ + LAST_SUCCESS_SOURCE_IP, + ]); expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); }); });