diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.test.tsx
index 81d971fa98af8..e056eeae67085 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.test.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.test.tsx
@@ -8,13 +8,19 @@
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../common/mock';
import React from 'react';
-import { EnrichmentAccordion } from './threat_details_view_enrichment_accordion';
+import {
+ ENRICHMENT_ACCORDION_LINK_TEST_ID,
+ EnrichmentAccordion,
+ THREAT_DETAILS_ROW_FIELD_TEST_ID,
+ THREAT_DETAILS_ROW_LINK_VALUE_TEST_ID,
+ THREAT_DETAILS_ROW_STRING_VALUE_TEST_ID,
+} from './threat_details_view_enrichment_accordion';
import { indicatorWithNestedObjects } from '../mocks/indicator_with_nested_objects';
import type { CtiEnrichment } from '../../../../../common/search_strategy';
describe('EnrichmentAccordion', () => {
- it('should render', () => {
- render(
+ it('should render the top level accordion', () => {
+ const { getByTestId } = render(
{
);
- expect(true).toBeTruthy();
+ expect(getByTestId(ENRICHMENT_ACCORDION_LINK_TEST_ID)).toBeInTheDocument();
+ });
+
+ it('should render rows with EuiLink', () => {
+ const { getAllByTestId, getAllByText, getByText } = render(
+
+
+
+ );
+
+ expect(getAllByTestId(THREAT_DETAILS_ROW_FIELD_TEST_ID).length).toBeGreaterThan(0);
+ expect(getAllByTestId(THREAT_DETAILS_ROW_STRING_VALUE_TEST_ID).length).toBeGreaterThan(0);
+ expect(getAllByTestId(THREAT_DETAILS_ROW_LINK_VALUE_TEST_ID).length).toEqual(1);
+
+ expect(getByText('@timestamp')).toBeInTheDocument();
+ expect(getAllByText('2024-02-24T17:32:37.813Z').length).toBeGreaterThan(0);
+
+ expect(getByText('agent.name')).toBeInTheDocument();
+ expect(getByText('win-10')).toBeInTheDocument();
+
+ expect(getByText('data_stream.namespace')).toBeInTheDocument();
+ expect(getByText('default')).toBeInTheDocument();
+
+ expect(getByText('indicator.reference')).toBeInTheDocument();
+ expect(getByText('indicatorReferenceValue')).toBeInTheDocument();
});
});
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.tsx
index 85b443682d34e..1bb366432c34c 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/left/components/threat_details_view_enrichment_accordion.tsx
@@ -11,8 +11,8 @@ import {
EuiAccordion,
EuiFlexGroup,
EuiFlexItem,
- EuiLink,
EuiInMemoryTable,
+ EuiLink,
EuiTitle,
EuiToolTip,
useEuiTheme,
@@ -24,8 +24,8 @@ import type { CtiEnrichment } from '../../../../../common/search_strategy';
import { QUERY_ID } from '../../shared/hooks/use_investigation_enrichment';
import { InspectButton } from '../../../../common/components/inspect';
import {
- getEnrichmentIdentifiers,
buildThreatDetailsItems,
+ getEnrichmentIdentifiers,
isInvestigationTimeEnrichment,
} from '../../shared/utils/threat_intelligence';
import { EnrichmentButtonContent } from './threat_details_view_enrichment_button_content';
@@ -35,6 +35,11 @@ const StyledH5 = styled.h5`
line-height: 1.7rem;
`;
+export const ENRICHMENT_ACCORDION_LINK_TEST_ID = 'enrichementAccordion';
+export const THREAT_DETAILS_ROW_FIELD_TEST_ID = 'threatDetailsRowField';
+export const THREAT_DETAILS_ROW_LINK_VALUE_TEST_ID = 'threatDetailsRowLinkValue';
+export const THREAT_DETAILS_ROW_STRING_VALUE_TEST_ID = 'threatDetailsRowStringValue';
+
const INVESTIGATION_QUERY_TITLE = i18n.translate(
'xpack.securitySolution.flyout.threatIntelligence.investigationTimeQueryTitle',
{
@@ -61,7 +66,7 @@ export interface ThreatDetailsRow {
/**
* Value of the enrichment
*/
- value: string;
+ value: string[];
};
}
@@ -70,7 +75,7 @@ const columns: Array> = [
field: 'title',
truncateText: false,
render: (title: string) => (
-
+
{title}
),
@@ -82,13 +87,27 @@ const columns: Array> = [
truncateText: false,
render: (description: ThreatDetailsRow['description']) => {
const { fieldName, value } = description;
- const tooltipChild = fieldName.match(REFERENCE) ? (
-
- {value}
-
+
+ const renderedValue = fieldName.match(REFERENCE) ? (
+
+ {value.map((val, key) => (
+
+
+ {val}
+
+
+ ))}
+
) : (
- {value}
+
+ {value.map((val, key) => (
+
+ {val}
+
+ ))}
+
);
+
return (
> = [
}
>
- {tooltipChild}
+ {renderedValue}
);
},
@@ -137,6 +156,7 @@ export const EnrichmentAccordion = memo(({ enrichment, index }: EnrichmentAccord
return (
{
@@ -510,28 +510,28 @@ describe('buildThreatDetailsItems', () => {
{
description: {
fieldName: 'feed.name',
- value: 'feed name',
+ value: ['feed name'],
},
title: 'feed.name',
},
{
description: {
fieldName: 'matched.atomic',
- value: 'matched atomic',
+ value: ['matched atomic'],
},
title: 'matched.atomic',
},
{
description: {
fieldName: 'matched.field',
- value: 'matched field',
+ value: ['matched field'],
},
title: 'matched.field',
},
{
description: {
fieldName: 'matched.type',
- value: 'matched type',
+ value: ['matched type'],
},
title: 'matched.type',
},
@@ -548,7 +548,7 @@ describe('buildThreatDetailsItems', () => {
title: 'array_values',
description: {
fieldName: 'array_values',
- value: 'first value',
+ value: ['first value', 'second value'],
},
},
]);
@@ -563,7 +563,7 @@ describe('buildThreatDetailsItems', () => {
title: 'indicator.ip',
description: {
fieldName: 'threat.indicator.ip',
- value: '127.0.0.1',
+ value: ['127.0.0.1'],
},
},
]);
@@ -579,7 +579,7 @@ describe('buildThreatDetailsItems', () => {
title: 'object_field.foo',
description: {
fieldName: 'object_field.foo',
- value: 'bar',
+ value: ['bar'],
},
},
]);
@@ -597,8 +597,9 @@ describe('buildThreatDetailsItems', () => {
title: 'flattened_object',
description: {
fieldName: 'flattened_object',
- value:
+ value: [
'This field contains nested object values, which are not rendered here. See the full document for all fields/values',
+ ],
},
},
]);
@@ -614,8 +615,9 @@ describe('buildThreatDetailsItems', () => {
title: 'array_field',
description: {
fieldName: 'array_field',
- value:
+ value: [
'This field contains nested object values, which are not rendered here. See the full document for all fields/values',
+ ],
},
},
]);
diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/utils/threat_intelligence.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/utils/threat_intelligence.tsx
index fefacf3e7fc14..8047d988577e8 100644
--- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/utils/threat_intelligence.tsx
+++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/utils/threat_intelligence.tsx
@@ -18,12 +18,12 @@ import {
} from '../../../../../common/constants';
import {
ENRICHMENT_TYPES,
+ FEED_NAME,
FIRST_SEEN,
MATCHED_ATOMIC,
MATCHED_FIELD,
MATCHED_ID,
MATCHED_TYPE,
- FEED_NAME,
} from '../../../../../common/cti/constants';
const NESTED_OBJECT_VALUES_NOT_RENDERED = i18n.translate(
@@ -189,10 +189,23 @@ export const buildThreatDetailsItems = (enrichment: CtiEnrichment): ThreatDetail
? field.replace(`${DEFAULT_INDICATOR_SOURCE_PATH}`, 'indicator')
: field;
- let value = getFirstElement(enrichment[field]);
- if (isObject(value)) {
- value = NESTED_OBJECT_VALUES_NOT_RENDERED;
- }
-
- return { title, description: { fieldName: field, value: value as string } };
+ // Gather string values, ignoring nested objects/arrays
+ // TODO We should probably enhance this in the future to show all values, but
+ // this will require more involved UI changes (like show a table or json view similar to the flyout)
+ const values: string[] = [];
+ const enrichmentValues = enrichment[field];
+ enrichmentValues.forEach((enrichmentValue) => {
+ // We show the string values as is, but for nested objects we show a message indicating
+ // that those values are not rendered here
+ if (typeof enrichmentValue === 'string') {
+ values.push(enrichmentValue);
+ } else if (isObject(enrichmentValue)) {
+ // We don't need to add the message more than once
+ if (!values.includes(NESTED_OBJECT_VALUES_NOT_RENDERED)) {
+ values.push(NESTED_OBJECT_VALUES_NOT_RENDERED);
+ }
+ }
+ });
+
+ return { title, description: { fieldName: field, value: values as string[] } };
});