diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/README.md b/x-pack/solutions/security/plugins/cloud_security_posture/README.md index aaa1b3f2377a9..c39796fa8562c 100755 --- a/x-pack/solutions/security/plugins/cloud_security_posture/README.md +++ b/x-pack/solutions/security/plugins/cloud_security_posture/README.md @@ -144,4 +144,21 @@ run serverless Cloud Security Posture e2e tests: yarn cypress:cloud_security_posture:run:serverless ``` -Unlike FTR where we have to set server and runner separately, Cypress handles everything in 1 go, so just running the above the script is enough to get it running \ No newline at end of file +Unlike FTR where we have to set server and runner separately, Cypress handles everything in 1 go, so just running the above the script is enough to get it running + +### Troubleshooting + +If you encounter an error related to running machine learning code, you should add the following string `'xpack.ml.enabled=false'` under the `esTestCluster` property in the `x-pack/test/functional/config.base.js` file. + +Example: +```javascript +module.exports = { + esTestCluster: { + // ...existing configuration... + serverArgs: [ + // ...existing arguments... + 'xpack.ml.enabled=false', // Add this line to disable ML + ], + }, + // ...other configurations... +}; \ No newline at end of file diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/constants.ts index 2039506c8cdde..52a4f5a8a2b3c 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/common/constants.ts @@ -176,7 +176,9 @@ export const FINDINGS_GROUPING_OPTIONS = { RULE_NAME: 'rule.name', RULE_SECTION: 'rule.section', CLOUD_ACCOUNT_NAME: 'cloud.account.name', + CLOUD_ACCOUNT_ID: 'cloud.account.id', ORCHESTRATOR_CLUSTER_NAME: 'orchestrator.cluster.name', + ORCHESTRATOR_CLUSTER_ID: 'orchestrator.cluster.id', }; export const VULNERABILITY_FIELDS = { @@ -224,6 +226,6 @@ the fields from the runtime mappings if they are removed from the Data Table. */ export const CDR_VULNERABILITY_GROUPING_RUNTIME_MAPPING_FIELDS: Record = {}; export const CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS: Record = { - [FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME]: ['orchestrator.cluster.name'], - [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]: ['cloud.account.name'], + [FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID]: ['orchestrator.cluster.id'], + [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID]: ['cloud.account.id'], }; diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx index 9f5f2ec85d021..470a04e402489 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.test.tsx @@ -47,7 +47,7 @@ describe('AccountsEvaluatedWidget', () => { 'cloud.provider': 'aws', 'rule.benchmark.posture_type': 'cspm', }, - ['cloud.account.name'] + ['cloud.account.id'] ); }); @@ -64,7 +64,7 @@ describe('AccountsEvaluatedWidget', () => { { 'rule.benchmark.id': 'cis_k8s', }, - ['orchestrator.cluster.name'] + ['orchestrator.cluster.id'] ); }); }); diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index 5ae8a47a93e71..80716efd98690 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -64,13 +64,13 @@ export const AccountsEvaluatedWidget = ({ const navToFindingsByCloudProvider = (provider: string) => { navToFindings( { 'cloud.provider': provider, 'rule.benchmark.posture_type': CSPM_POLICY_TEMPLATE }, - [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME] + [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID] ); }; const navToFindingsByCisBenchmark = (cisBenchmark: string) => { navToFindings({ 'rule.benchmark.id': cisBenchmark }, [ - FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID, ]); }; diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx index 27b32345d85ca..5c10703bc5adc 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx @@ -145,14 +145,12 @@ const getBenchmarkTableColumns = ( 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.APPLICABLE_TO, render: (benchmarkId: BenchmarksCisId) => { return ( - <> - - - - - {getBenchmarkApplicableTo(benchmarkId)} - - + + + + + {getBenchmarkApplicableTo(benchmarkId)} + ); }, }, @@ -189,8 +187,8 @@ const getBenchmarkTableColumns = ( const isKspmBenchmark = ['cis_k8s', 'cis_eks'].includes(benchmark.id); const groupByField = isKspmBenchmark - ? FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME - : FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME; + ? FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID + : FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID; return ( - navToFindings(getBenchmarkIdQuery(benchmark), [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME]); + navToFindings(getBenchmarkIdQuery(benchmark), [FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID]); const handleClickCluster = () => navToFindings(getBenchmarkIdQuery(benchmark), [ - FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID, ]); const getBenchmarkInfo = (benchmarkId: string, cloudAssetCount: number): BenchmarkInfo => { diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts index 4ccf6caf68ea6..8612c754ac35c 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts @@ -35,12 +35,12 @@ export const MISCONFIGURATIONS_GROUPS_UNIT = ( values: { groupCount }, defaultMessage: `{groupCount} {groupCount, plural, =1 {rule} other {rules}}`, }); - case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: + case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID: return i18n.translate('xpack.csp.findings.groupUnit.cloudAccount', { values: { groupCount }, defaultMessage: `{groupCount} {groupCount, plural, =1 {cloud account} other {cloud accounts}}`, }); - case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: + case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID: return i18n.translate('xpack.csp.findings.groupUnit.kubernetes', { values: { groupCount }, defaultMessage: `{groupCount} {groupCount, plural, =1 {kubernetes cluster} other {kubernetes clusters}}`, @@ -67,10 +67,9 @@ export const NULL_GROUPING_MESSAGES = { CLOUD_ACCOUNT_NAME: i18n.translate('xpack.csp.findings.grouping.cloudAccount.nullGroupTitle', { defaultMessage: 'No cloud account', }), - ORCHESTRATOR_CLUSTER_NAME: i18n.translate( - 'xpack.csp.findings.grouping.kubernetes.nullGroupTitle', - { defaultMessage: 'No Kubernetes cluster' } - ), + ORCHESTRATOR_CLUSTER_ID: i18n.translate('xpack.csp.findings.grouping.kubernetes.nullGroupTitle', { + defaultMessage: 'No Kubernetes cluster', + }), DEFAULT: i18n.translate('xpack.csp.findings.grouping.default.nullGroupTitle', { defaultMessage: 'No grouping', }), @@ -91,15 +90,15 @@ export const defaultGroupingOptions: GroupOption[] = [ }, { label: i18n.translate('xpack.csp.findings.latestFindings.groupByCloudAccount', { - defaultMessage: 'Cloud account', + defaultMessage: 'Cloud account ID', }), - key: FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME, + key: FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID, }, { label: i18n.translate('xpack.csp.findings.latestFindings.groupByKubernetesCluster', { - defaultMessage: 'Kubernetes cluster', + defaultMessage: 'Kubernetes cluster ID', }), - key: FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME, + key: FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID, }, ]; diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index 743b0bc95cb43..2fbc09d3f2494 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { GroupPanelRenderer, GroupStatsItem, RawBucket } from '@kbn/grouping/src'; +import { GenericBuckets, GroupPanelRenderer, GroupStatsItem, RawBucket } from '@kbn/grouping/src'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; @@ -45,13 +45,15 @@ export const groupPanelRenderer: GroupPanelRenderer ); - const getGroupPanelTitle = () => { - const resourceId = bucket.resourceName?.buckets?.[0]?.key; + const getGroupPanelTitle = (aggregationField?: keyof FindingsGroupingAggregation) => { + const aggregationFieldValue = aggregationField + ? (bucket[aggregationField] as { buckets?: GenericBuckets[] })?.buckets?.[0]?.key + : null; - if (resourceId) { + if (aggregationFieldValue) { return ( <> - {resourceId} - {bucket.key_as_string} + {aggregationFieldValue} - {bucket.key_as_string} ); } @@ -80,7 +82,7 @@ export const groupPanelRenderer: GroupPanelRenderer `} title={bucket.resourceName?.buckets?.[0]?.key as string} > - {getGroupPanelTitle()} + {getGroupPanelTitle('resourceName')} @@ -101,9 +103,7 @@ export const groupPanelRenderer: GroupPanelRenderer - - {bucket.key_as_string} - + {getGroupPanelTitle()} @@ -115,7 +115,7 @@ export const groupPanelRenderer: GroupPanelRenderer ); - case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: + case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID: return nullGroupMessage ? ( renderNullGroup(NULL_GROUPING_MESSAGES.CLOUD_ACCOUNT_NAME) ) : ( @@ -131,9 +131,7 @@ export const groupPanelRenderer: GroupPanelRenderer - - {bucket.key_as_string} - + {getGroupPanelTitle('accountName')} @@ -144,9 +142,9 @@ export const groupPanelRenderer: GroupPanelRenderer ); - case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: + case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID: return nullGroupMessage ? ( - renderNullGroup(NULL_GROUPING_MESSAGES.ORCHESTRATOR_CLUSTER_NAME) + renderNullGroup(NULL_GROUPING_MESSAGES.ORCHESTRATOR_CLUSTER_ID) ) : ( {benchmarkId && ( @@ -160,9 +158,7 @@ export const groupPanelRenderer: GroupPanelRenderer - - {bucket.key_as_string} - + {getGroupPanelTitle('clusterName')} @@ -181,9 +177,7 @@ export const groupPanelRenderer: GroupPanelRenderer - - {bucket.key_as_string} - + {getGroupPanelTitle()} diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx index 842e21c58a971..438e6b0778e2b 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx @@ -61,6 +61,12 @@ export interface FindingsGroupingAggregation { benchmarkId?: { buckets?: GenericBuckets[]; }; + accountName?: { + buckets?: GenericBuckets[]; + }; + clusterName?: { + buckets?: GenericBuckets[]; + }; isLoading?: boolean; } diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx index 87e0969a5966c..f591115792e08 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -98,17 +98,19 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { getTermAggregation('benchmarkName', 'rule.benchmark.name'), getTermAggregation('benchmarkVersion', 'rule.benchmark.version'), ]; - case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME: + case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID: return [ ...aggMetrics, getTermAggregation('benchmarkName', 'rule.benchmark.name'), getTermAggregation('benchmarkId', 'rule.benchmark.id'), + getTermAggregation('accountName', 'cloud.account.name'), ]; - case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_NAME: + case FINDINGS_GROUPING_OPTIONS.ORCHESTRATOR_CLUSTER_ID: return [ ...aggMetrics, getTermAggregation('benchmarkName', 'rule.benchmark.name'), getTermAggregation('benchmarkId', 'rule.benchmark.id'), + getTermAggregation('clusterName', 'orchestrator.cluster.name'), ]; } return aggMetrics; diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts index c918fb275d968..c904e1b45f2c1 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_grouping.ts @@ -296,14 +296,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } ); }); - it('groups findings by cloud account and sort by compliance score desc', async () => { + it('groups findings by cloud account id and sort by compliance score desc', async () => { const groupSelector = await findings.groupSelector(); await groupSelector.openDropDown(); await groupSelector.setValue('None'); await groupSelector.openDropDown(); - await groupSelector.setValue('Cloud account'); - + await groupSelector.setValue('Cloud account ID'); const grouping = await findings.findingsGrouping(); + const noCloudAccountGroupTitle = 'No cloud account'; const groupCount = await grouping.getGroupCount(); expect(groupCount).to.be('2 cloud accounts'); @@ -314,12 +314,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const cloudNameOrder = [ { cloudName: 'Account 2', + cloudId: '2', findingsCount: '1', complianceScore: '0%', benchmarkName: data[3].rule.benchmark.name, }, { cloudName: 'Account 1', + cloudId: '1', findingsCount: '1', complianceScore: '100%', benchmarkName: data[2].rule.benchmark.name, @@ -334,11 +336,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await asyncForEach( cloudNameOrder, - async ({ cloudName, complianceScore, findingsCount, benchmarkName }, index) => { + async ({ cloudName, complianceScore, findingsCount, benchmarkName, cloudId }, index) => { const groupRow = await grouping.getRowAtIndex(index); - expect(await groupRow.getVisibleText()).to.contain(cloudName); + const groupTitle = + cloudName === noCloudAccountGroupTitle + ? noCloudAccountGroupTitle + : `${cloudName} - ${cloudId}`; + expect(await groupRow.getVisibleText()).to.contain(groupTitle); - if (cloudName !== 'No cloud account') { + if (cloudName !== noCloudAccountGroupTitle) { expect(await groupRow.getVisibleText()).to.contain(benchmarkName); } @@ -353,36 +359,33 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } ); }); - it('groups findings by Kubernetes cluster and sort by compliance score desc', async () => { + it('groups findings by Kubernetes cluster id and sort by compliance score desc', async () => { const groupSelector = await findings.groupSelector(); await groupSelector.openDropDown(); await groupSelector.setValue('None'); await groupSelector.openDropDown(); - await groupSelector.setValue('Kubernetes cluster'); + await groupSelector.setValue('Kubernetes cluster ID'); + const noKuberenetsClusterGroupTitle = 'No Kubernetes cluster'; const grouping = await findings.findingsGrouping(); const groupCount = await grouping.getGroupCount(); - expect(groupCount).to.be('2 kubernetes clusters'); + expect(groupCount).to.be('1 kubernetes cluster'); const unitCount = await grouping.getUnitCount(); + expect(unitCount).to.be('4 findings'); const kubernetesOrder = [ { clusterName: 'Cluster 1', - findingsCount: '1', - complianceScore: '0%', + clusterId: '1', + findingsCount: '2', + complianceScore: '50%', benchmarkName: data[0].rule.benchmark.name, }, { - clusterName: 'Cluster 2', - findingsCount: '1', - complianceScore: '100%', - benchmarkName: data[1].rule.benchmark.name, - }, - { - clusterName: 'No Kubernetes cluster', + clusterName: noKuberenetsClusterGroupTitle, findingsCount: '2', complianceScore: '50%', }, @@ -390,12 +393,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await asyncForEach( kubernetesOrder, - async ({ clusterName, complianceScore, findingsCount, benchmarkName }, index) => { + async ( + { clusterName, complianceScore, findingsCount, benchmarkName, clusterId }, + index + ) => { const groupRow = await grouping.getRowAtIndex(index); - expect(await groupRow.getVisibleText()).to.contain(clusterName); - if (clusterName !== 'No Kubernetes cluster') { + const groupTitle = + clusterName === noKuberenetsClusterGroupTitle + ? noKuberenetsClusterGroupTitle + : `${clusterName} - ${clusterId}`; + + expect(await groupRow.getVisibleText()).to.contain(groupTitle); + if (clusterName !== noKuberenetsClusterGroupTitle) { expect(await groupRow.getVisibleText()).to.contain(benchmarkName); } + expect( await ( await groupRow.findByTestSubject('cloudSecurityFindingsComplianceScore')