Skip to content

Commit

Permalink
[Cloud Posture] Findings - Group by resource - Fixed bug not showing …
Browse files Browse the repository at this point in the history
…results (elastic#132529)
  • Loading branch information
kfirpeled authored May 22, 2022
1 parent fb1eeb0 commit 383239e
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ import { TestProvider } from '../../../test/test_provider';

const chance = new Chance();

const getFakeFindingsByResource = (): CspFindingsByResource => ({
resource_id: chance.guid(),
cis_sections: [chance.word(), chance.word()],
failed_findings: {
total: chance.integer(),
normalized: chance.integer({ min: 0, max: 1 }),
},
});
const getFakeFindingsByResource = (): CspFindingsByResource => {
const count = chance.integer();
const total = chance.integer() + count + 1;
const normalized = count / total;

return {
resource_id: chance.guid(),
resource_name: chance.word(),
resource_subtype: chance.word(),
cis_sections: [chance.word(), chance.word()],
failed_findings: {
count,
normalized,
total_findings: total,
},
};
};

type TableProps = PropsOf<typeof FindingsByResourceTable>;

Expand Down Expand Up @@ -74,8 +83,11 @@ describe('<FindingsByResourceTable />', () => {
);
expect(row).toBeInTheDocument();
expect(within(row).getByText(item.resource_id)).toBeInTheDocument();
if (item.resource_name) expect(within(row).getByText(item.resource_name)).toBeInTheDocument();
if (item.resource_subtype)
expect(within(row).getByText(item.resource_subtype)).toBeInTheDocument();
expect(within(row).getByText(item.cis_sections.join(', '))).toBeInTheDocument();
expect(within(row).getByText(formatNumber(item.failed_findings.total))).toBeInTheDocument();
expect(within(row).getByText(formatNumber(item.failed_findings.count))).toBeInTheDocument();
expect(
within(row).getByText(new RegExp(numeral(item.failed_findings.normalized).format('0%')))
).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {
EuiEmptyPrompt,
EuiBasicTable,
EuiTextColor,
EuiFlexGroup,
EuiFlexItem,
type EuiTableFieldDataColumnType,
type CriteriaWithPagination,
type Pagination,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import numeral from '@elastic/numeral';
import { Link, generatePath } from 'react-router-dom';
Expand Down Expand Up @@ -81,6 +81,26 @@ const columns: Array<EuiTableFieldDataColumnType<CspFindingsByResource>> = [
</Link>
),
},
{
field: 'resource_subtype',
truncateText: true,
name: (
<FormattedMessage
id="xpack.csp.findings.groupByResourceTable.resourceTypeColumnLabel"
defaultMessage="Resource Type"
/>
),
},
{
field: 'resource_name',
truncateText: true,
name: (
<FormattedMessage
id="xpack.csp.findings.groupByResourceTable.resourceNameColumnLabel"
defaultMessage="Resource Name"
/>
),
},
{
field: 'cis_sections',
truncateText: true,
Expand All @@ -102,14 +122,22 @@ const columns: Array<EuiTableFieldDataColumnType<CspFindingsByResource>> = [
/>
),
render: (failedFindings: CspFindingsByResource['failed_findings']) => (
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiTextColor color="danger">{formatNumber(failedFindings.total)}</EuiTextColor>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<span>({numeral(failedFindings.normalized).format('0%')})</span>
</EuiFlexItem>
</EuiFlexGroup>
<EuiToolTip
content={i18n.translate('xpack.csp.findings.groupByResourceTable.failedFindingsToolTip', {
defaultMessage: '{failed} out of {total}',
values: {
failed: failedFindings.count,
total: failedFindings.total_findings,
},
})}
>
<>
<EuiTextColor color={failedFindings.count === 0 ? '' : 'danger'}>
{formatNumber(failedFindings.count)}
</EuiTextColor>
<span> ({numeral(failedFindings.normalized).format('0%')})</span>
</>
</EuiToolTip>
),
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { showErrorToast } from '../latest_findings/use_latest_findings';
import type { FindingsBaseEsQuery, FindingsQueryResult } from '../types';

// a large number to probably get all the buckets
const MAX_BUCKETS = 60 * 1000;
const MAX_BUCKETS = 1000 * 1000;

interface UseResourceFindingsOptions extends FindingsBaseEsQuery {
from: NonNullable<estypes.SearchRequest['from']>;
Expand Down Expand Up @@ -43,6 +43,8 @@ interface FindingsByResourceAggs {

interface FindingsAggBucket extends estypes.AggregationsStringRareTermsBucketKeys {
failed_findings: estypes.AggregationsMultiBucketBase;
name: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringTermsBucketKeys>;
subtype: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringTermsBucketKeys>;
cis_sections: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringRareTermsBucketKeys>;
}

Expand All @@ -57,10 +59,16 @@ export const getFindingsByResourceAggQuery = ({
query,
size: 0,
aggs: {
resource_total: { cardinality: { field: 'resource.id.keyword' } },
resource_total: { cardinality: { field: 'resource.id' } },
resources: {
terms: { field: 'resource.id.keyword', size: MAX_BUCKETS },
terms: { field: 'resource.id', size: MAX_BUCKETS },
aggs: {
name: {
terms: { field: 'resource.name', size: 1 },
},
subtype: {
terms: { field: 'resource.sub_type', size: 1 },
},
cis_sections: {
terms: { field: 'rule.section.keyword' },
},
Expand Down Expand Up @@ -117,16 +125,24 @@ export const useFindingsByResource = ({ index, query, from, size }: UseResourceF
);
};

const createFindingsByResource = (bucket: FindingsAggBucket) => {
if (!Array.isArray(bucket.cis_sections.buckets))
const createFindingsByResource = (resource: FindingsAggBucket) => {
if (
!Array.isArray(resource.cis_sections.buckets) ||
!Array.isArray(resource.name.buckets) ||
!Array.isArray(resource.subtype.buckets)
)
throw new Error('expected buckets to be an array');

return {
resource_id: bucket.key,
cis_sections: bucket.cis_sections.buckets.map((v) => v.key),
resource_id: resource.key,
resource_name: resource.name.buckets.map((v) => v.key).at(0),
resource_subtype: resource.subtype.buckets.map((v) => v.key).at(0),
cis_sections: resource.cis_sections.buckets.map((v) => v.key),
failed_findings: {
total: bucket.failed_findings.doc_count,
normalized: bucket.doc_count > 0 ? bucket.failed_findings.doc_count / bucket.doc_count : 0,
count: resource.failed_findings.doc_count,
normalized:
resource.doc_count > 0 ? resource.failed_findings.doc_count / resource.doc_count : 0,
total_findings: resource.doc_count,
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,32 @@ export const latestFindingsMapping: MappingTypeMapping = {
properties: {
type: {
type: 'keyword',
ignore_above: 256,
ignore_above: 1024,
},
id: {
type: 'text',
type: 'keyword',
ignore_above: 1024,
fields: {
text: {
type: 'text',
},
},
},
name: {
type: 'text',
type: 'keyword',
ignore_above: 1024,
fields: {
text: {
type: 'text',
},
},
},
sub_type: {
type: 'text',
ignore_above: 1024,
type: 'keyword',
fields: {
keyword: {
ignore_above: 1024,
type: 'keyword',
text: {
type: 'text',
},
},
},
Expand Down

0 comments on commit 383239e

Please sign in to comment.