Skip to content

Commit

Permalink
[Security Solution] 10751 app findings group by (#207155)
Browse files Browse the repository at this point in the history
## Summary

This PR updates the group by mechanism with the following logic:
cloud.account.id and orchestrator.cluster.id instead of
cloud.account.name and orchestrator.cluster.name for grouping on the
Findings page.

### Screenshots

![image](/~https://github.com/user-attachments/assets/b5760b62-4afb-433f-b962-40695711ac52)

![image](/~https://github.com/user-attachments/assets/d6a968b0-bb17-49a4-b47e-c9c8fda2495e)


### Closes
elastic/security-team#10751

### DOD
- [ ] Group By Cloud account should use cloud.account.id for grouping.
cloud.account.name should still be displayed, for user's convenience,
consult with the design
- [ ] Group By Kubernetes cluster should use orchestrator.cluster.id for
grouping. orchestrator.cluster.name should still be displayed, for
user's convenience, consult with the design
  • Loading branch information
alexreal1314 authored Jan 20, 2025
1 parent 30bb71a commit 453ebf1
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
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...
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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<string, string[]> = {};
export const CDR_MISCONFIGURATION_GROUPING_RUNTIME_MAPPING_FIELDS: Record<string, string[]> = {
[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'],
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('AccountsEvaluatedWidget', () => {
'cloud.provider': 'aws',
'rule.benchmark.posture_type': 'cspm',
},
['cloud.account.name']
['cloud.account.id']
);
});

Expand All @@ -64,7 +64,7 @@ describe('AccountsEvaluatedWidget', () => {
{
'rule.benchmark.id': 'cis_k8s',
},
['orchestrator.cluster.name']
['orchestrator.cluster.id']
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,12 @@ const getBenchmarkTableColumns = (
'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.APPLICABLE_TO,
render: (benchmarkId: BenchmarksCisId) => {
return (
<>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<CISBenchmarkIcon type={benchmarkId} size={'l'} />
</EuiFlexItem>
<EuiFlexItem grow={false}>{getBenchmarkApplicableTo(benchmarkId)}</EuiFlexItem>
</EuiFlexGroup>
</>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<CISBenchmarkIcon type={benchmarkId} size={'l'} />
</EuiFlexItem>
<EuiFlexItem grow={false}>{getBenchmarkApplicableTo(benchmarkId)}</EuiFlexItem>
</EuiFlexGroup>
);
},
},
Expand Down Expand Up @@ -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 (
<EuiButtonEmpty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export const BenchmarkDetailsBox = ({ benchmark }: { benchmark: BenchmarkData })
const navToFindings = useNavigateFindings();

const handleClickCloudProvider = () =>
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 => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}`,
Expand All @@ -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',
}),
Expand All @@ -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,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -45,13 +45,15 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
<NullGroup title={title} field={selectedGroup} unit={NULL_GROUPING_UNIT} />
);

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 (
<>
<strong>{resourceId}</strong> - {bucket.key_as_string}
<strong>{aggregationFieldValue}</strong> - {bucket.key_as_string}
</>
);
}
Expand Down Expand Up @@ -80,7 +82,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
`}
title={bucket.resourceName?.buckets?.[0]?.key as string}
>
{getGroupPanelTitle()}
{getGroupPanelTitle('resourceName')}
</EuiTextBlockTruncate>
</EuiText>
</EuiFlexItem>
Expand All @@ -101,9 +103,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiText size="s">
<strong>{bucket.key_as_string}</strong>
</EuiText>
<EuiText size="s"> {getGroupPanelTitle()}</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs" color="subdued">
Expand All @@ -115,7 +115,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
</EuiFlexItem>
</EuiFlexGroup>
);
case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_NAME:
case FINDINGS_GROUPING_OPTIONS.CLOUD_ACCOUNT_ID:
return nullGroupMessage ? (
renderNullGroup(NULL_GROUPING_MESSAGES.CLOUD_ACCOUNT_NAME)
) : (
Expand All @@ -131,9 +131,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiText size="s">
<strong>{bucket.key_as_string}</strong>
</EuiText>
<EuiText size="s">{getGroupPanelTitle('accountName')}</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs" color="subdued">
Expand All @@ -144,9 +142,9 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
</EuiFlexItem>
</EuiFlexGroup>
);
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)
) : (
<EuiFlexGroup alignItems="center" gutterSize="m">
{benchmarkId && (
Expand All @@ -160,9 +158,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiText size="s">
<strong>{bucket.key_as_string}</strong>
</EuiText>
<EuiText size="s">{getGroupPanelTitle('clusterName')}</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs" color="subdued">
Expand All @@ -181,9 +177,7 @@ export const groupPanelRenderer: GroupPanelRenderer<FindingsGroupingAggregation>
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiText size="s">
<strong>{bucket.key_as_string}</strong>
</EuiText>
<EuiText size="s">{getGroupPanelTitle()}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export interface FindingsGroupingAggregation {
benchmarkId?: {
buckets?: GenericBuckets[];
};
accountName?: {
buckets?: GenericBuckets[];
};
clusterName?: {
buckets?: GenericBuckets[];
};
isLoading?: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 453ebf1

Please sign in to comment.