Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,21 @@ export const EntityInsight = <T,>({
const insightContent: React.ReactElement[] = [];

const cspPreviewEntityType = inferEntityTypeFromIdentityFields(identityFields);
const { hasMisconfigurationFindings: showMisconfigurationsPreview } = useHasMisconfigurations(
buildEuidCspPreviewOptions(cspPreviewEntityType, identityFields, euidApi, {
const {
hasMisconfigurationFindings: showMisconfigurationsPreview,
passedFindings,
failedFindings,
} = useHasMisconfigurations(
buildEuidCspPreviewOptions(cspPreviewEntityType, entityRecord, euidApi, {
entityStoreV2Enabled,
legacyIdentityFields: identityFields,
})
);

const { hasVulnerabilitiesFindings } = useHasVulnerabilities(
buildEuidCspPreviewOptions(cspPreviewEntityType, identityFields, euidApi, {
buildEuidCspPreviewOptions(cspPreviewEntityType, entityRecord, euidApi, {
entityStoreV2Enabled,
legacyIdentityFields: identityFields,
})
);

Expand Down Expand Up @@ -99,8 +105,9 @@ export const EntityInsight = <T,>({
insightContent.push(
<>
<MisconfigurationsPreview
identityFields={identityFields}
isPreviewMode={isPreviewMode}
passedFindings={passedFindings}
failedFindings={failedFindings}
openDetailsPanel={openDetailsPanel}
/>
<EuiSpacer size="s" />
Expand All @@ -111,6 +118,7 @@ export const EntityInsight = <T,>({
<>
<VulnerabilitiesPreview
identityFields={identityFields}
entityRecord={entityRecord}
isPreviewMode={isPreviewMode}
openDetailsPanel={openDetailsPanel}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,19 @@
import React from 'react';
import { render } from '@testing-library/react';
import { MisconfigurationsPreview } from './misconfiguration_preview';
import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview';
import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview';
import { TestProviders } from '../../../common/mock/test_providers';

// Mock hooks
jest.mock('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview');
jest.mock('@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview');

describe('MisconfigurationsPreview', () => {
const mockOpenLeftPanel = jest.fn();

beforeEach(() => {
(useVulnerabilitiesPreview as jest.Mock).mockReturnValue({
data: { count: { CRITICAL: 0, HIGH: 1, MEDIUM: 1, LOW: 0, UNKNOWN: 0 } },
});
(useMisconfigurationPreview as jest.Mock).mockReturnValue({
data: { count: { passed: 1, failed: 1 } },
});
});
const mockOpenDetailsPanel = jest.fn();

it('renders', () => {
const { getByTestId } = render(
<TestProviders>
<MisconfigurationsPreview
identityFields={{ 'host.name': 'host1' }}
isPreviewMode={false}
openDetailsPanel={mockOpenLeftPanel}
passedFindings={1}
failedFindings={1}
openDetailsPanel={mockOpenDetailsPanel}
/>
</TestProviders>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { css } from '@emotion/react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { DistributionBar } from '@kbn/security-solution-distribution-bar';
import { useHasMisconfigurations } from '@kbn/cloud-security-posture/src/hooks/use_has_misconfigurations';
import { i18n } from '@kbn/i18n';
import { useGetMisconfigurationStatusColor } from '@kbn/cloud-security-posture';
import { MISCONFIGURATION_STATUS } from '@kbn/cloud-security-posture-common';
Expand All @@ -19,19 +18,12 @@ import {
ENTITY_FLYOUT_WITH_MISCONFIGURATION_VISIT,
uiMetricService,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { FF_ENABLE_ENTITY_STORE_V2, useEntityStoreEuidApi } from '@kbn/entity-store/public';
import {
buildEuidCspPreviewOptions,
inferEntityTypeFromIdentityFields,
} from '../../utils/build_euid_csp_preview_options';
import { ExpandablePanel } from '../../../flyout_v2/shared/components/expandable_panel';
import type { EntityDetailsPath } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header';
import {
CspInsightLeftPanelSubTab,
EntityDetailsLeftPanelTab,
} from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header';
import type { IdentityFields } from '../../../flyout/document_details/shared/utils';
import { useUiSetting } from '../../../common/lib/kibana';

interface MisconfigurationPreviewDistributionBarProps {
key: string;
Expand Down Expand Up @@ -111,24 +103,16 @@ const MisconfigurationPreviewScore = ({
};

export const MisconfigurationsPreview = ({
identityFields,
isPreviewMode,
openDetailsPanel,
passedFindings,
failedFindings,
}: {
identityFields: IdentityFields;
isPreviewMode: boolean;
passedFindings: number;
failedFindings: number;
openDetailsPanel: (path: EntityDetailsPath) => void;
}) => {
const euidApi = useEntityStoreEuidApi();
const entityStoreV2Enabled = useUiSetting<boolean>(FF_ENABLE_ENTITY_STORE_V2);
const { hasMisconfigurationFindings, passedFindings, failedFindings } = useHasMisconfigurations(
buildEuidCspPreviewOptions(
inferEntityTypeFromIdentityFields(identityFields),
identityFields,
euidApi,
{ entityStoreV2Enabled }
)
);
const findingsStats = useGetFindingsStats(passedFindings, failedFindings);

useEffect(() => {
Expand Down Expand Up @@ -160,7 +144,7 @@ export const MisconfigurationsPreview = ({
return (
<ExpandablePanel
header={{
iconType: !isPreviewMode && hasMisconfigurationFindings ? 'chevronLimitLeft' : '',
iconType: !isPreviewMode ? 'chevronLimitLeft' : '',
title: (
<EuiTitle
css={css`
Expand All @@ -174,7 +158,7 @@ export const MisconfigurationsPreview = ({
/>
</EuiTitle>
),
link: hasMisconfigurationFindings ? link : undefined,
link,
}}
data-test-subj={'securitySolutionFlyoutInsightsMisconfigurations'}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { METRIC_TYPE } from '@kbn/analytics';
import { FF_ENABLE_ENTITY_STORE_V2, useEntityStoreEuidApi } from '@kbn/entity-store/public';
import type { EntityStoreRecord } from '../../../flyout/entity_details/shared/hooks/use_entity_from_store';
import {
buildEuidCspPreviewOptions,
inferEntityTypeFromIdentityFields,
Expand Down Expand Up @@ -68,10 +69,12 @@ const VulnerabilitiesCount = ({

export const VulnerabilitiesPreview = ({
identityFields,
entityRecord,
isPreviewMode,
openDetailsPanel,
}: {
identityFields: IdentityFields;
entityRecord?: EntityStoreRecord | null;
isPreviewMode: boolean;
openDetailsPanel: (path: EntityDetailsPath) => void;
}) => {
Expand All @@ -83,8 +86,12 @@ export const VulnerabilitiesPreview = ({
const entityStoreV2Enabled = useUiSetting<boolean>(FF_ENABLE_ENTITY_STORE_V2);
const entityType = inferEntityTypeFromIdentityFields(identityFields);
const cspPreviewOptions = useMemo(
() => buildEuidCspPreviewOptions(entityType, identityFields, euidApi, { entityStoreV2Enabled }),
[euidApi, entityStoreV2Enabled, entityType, identityFields]
() =>
buildEuidCspPreviewOptions(entityType, entityRecord, euidApi, {
entityStoreV2Enabled,
legacyIdentityFields: identityFields,
}),
[entityType, entityRecord, euidApi, entityStoreV2Enabled, identityFields]
);
const { data } = useVulnerabilitiesPreview(cspPreviewOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { EntityStoreEuid } from '@kbn/entity-store/public';

import type { EntityStoreRecord } from '../../../../flyout/entity_details/shared/hooks/use_entity_from_store';
import type { Anomaly } from '../types';
import {
anomalyMatchesMlEntityField,
Expand Down Expand Up @@ -81,6 +82,28 @@ describe('anomaly_table_euid', () => {
});
expect(q).toEqual(scopedDsl);
});

test('scoped path passes entityRecord directly when provided', () => {
const scopedDsl = { bool: { filter: [{ term: { 'user.name': 'alice' } }] } };
const getEuidFilterBasedOnDocument = jest.fn().mockReturnValue(scopedDsl);
const euid = {
dsl: { getEuidFilterBasedOnDocument },
getEuidSourceFields: () => ({
requiresOneOf: ['user.name'],
identitySourceFields: ['user.name'],
}),
} as unknown as EntityStoreEuid;
const entityRecord = { 'user.name': 'alice' } as unknown as EntityStoreRecord;

const q = buildAnomaliesTableInfluencersFilterQuery({
euid,
entityType: 'user',
entityRecord,
isScopedToEntity: true,
});
expect(getEuidFilterBasedOnDocument).toHaveBeenCalledWith('user', entityRecord);
expect(q).toEqual(scopedDsl);
});
});

describe('getCriteriaFieldsForAnomaliesTable', () => {
Expand Down Expand Up @@ -125,6 +148,27 @@ describe('anomaly_table_euid', () => {
{ fieldName: 'user.name', fieldValue: 'bob' },
]);
});

test('passes entityRecord directly when provided', () => {
const getEuidFilterBasedOnDocument = jest.fn().mockReturnValue(undefined);
const getEntityIdentifiersFromDocument = jest.fn().mockReturnValue({ 'user.name': 'carol' });
const euid = {
dsl: { getEuidFilterBasedOnDocument },
getEntityIdentifiersFromDocument,
} as unknown as EntityStoreEuid;
const entityRecord = { 'user.name': 'carol' } as unknown as EntityStoreRecord;

const result = getCriteriaFieldsForAnomaliesTable({
euid,
entityType: 'user',
entityRecord,
isScopedToEntity: true,
fallbackDisplayName: 'carol',
});
expect(getEuidFilterBasedOnDocument).toHaveBeenCalledWith('user', entityRecord);
expect(getEntityIdentifiersFromDocument).toHaveBeenCalledWith('user', entityRecord);
expect(result).toEqual([{ fieldName: 'user.name', fieldValue: 'carol' }]);
});
});

describe('anomalyRowMatchesIdentityIdentifiers', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { estypes } from '@elastic/elasticsearch';
import type { EntityStoreEuid } from '@kbn/entity-store/public';

import type { EntityStoreRecord } from '../../../../flyout/entity_details/shared/hooks/use_entity_from_store';
import type { Anomaly, CriteriaFields } from '../types';

/** Entity kinds that have Security Solution host/user anomaly tables. */
Expand Down Expand Up @@ -71,24 +72,24 @@ export const buildBroadMlIdentityFieldsExistFilter = (
export const buildAnomaliesTableInfluencersFilterQuery = ({
euid,
entityType,
entityRecord,
isScopedToEntity,
identityFields,
fallbackDisplayName,
}: {
euid: EntityStoreEuid | undefined;
entityType: AnomaliesTableEntityType;
entityRecord?: EntityStoreRecord | null;
isScopedToEntity: boolean;
identityFields?: Record<string, string>;
fallbackDisplayName?: string;
}): estypes.QueryDslQueryContainer => {
if (euid) {
if (isScopedToEntity) {
const doc = buildEuidSampleDocumentForAnomaliesTable(
entityType,
identityFields,
fallbackDisplayName
);
const scoped = euid.dsl.getEuidFilterBasedOnDocument(entityType, doc);
const inputDoc = entityRecord
? entityRecord
: buildEuidSampleDocumentForAnomaliesTable(entityType, identityFields, fallbackDisplayName);
const scoped = euid.dsl.getEuidFilterBasedOnDocument(entityType, inputDoc);
if (scoped != null) {
return scoped as estypes.QueryDslQueryContainer;
}
Expand All @@ -103,12 +104,14 @@ export const buildAnomaliesTableInfluencersFilterQuery = ({
export const getCriteriaFieldsForAnomaliesTable = ({
euid,
entityType,
entityRecord,
isScopedToEntity,
identityFields,
fallbackDisplayName,
}: {
euid: EntityStoreEuid | undefined;
entityType: AnomaliesTableEntityType;
entityRecord?: EntityStoreRecord | null;
isScopedToEntity: boolean;
identityFields?: Record<string, string>;
fallbackDisplayName?: string;
Expand All @@ -117,16 +120,14 @@ export const getCriteriaFieldsForAnomaliesTable = ({
return [];
}
if (euid) {
const doc = buildEuidSampleDocumentForAnomaliesTable(
entityType,
identityFields,
fallbackDisplayName
);
const scopedDsl = euid.dsl.getEuidFilterBasedOnDocument(entityType, doc);
const inputDoc = entityRecord
? entityRecord
: buildEuidSampleDocumentForAnomaliesTable(entityType, identityFields, fallbackDisplayName);
const scopedDsl = euid.dsl.getEuidFilterBasedOnDocument(entityType, inputDoc);
if (scopedDsl != null) {
return [];
}
const identifiers = euid.getEntityIdentifiersFromDocument(entityType, doc);
const identifiers = euid.getEntityIdentifiersFromDocument(entityType, inputDoc);
if (identifiers != null && Object.keys(identifiers).length > 0) {
return Object.entries(identifiers).map(([fieldName, fieldValue]) => ({
fieldName,
Expand Down
Loading
Loading