[Cloud Posture] CIS AWS support - changes to findings tables (#148945)

This commit is contained in:
Jordan 2023-01-24 12:54:15 +02:00 committed by GitHub
parent 06cec01479
commit 48eb4d51d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 206 additions and 175 deletions

View file

@ -12,6 +12,7 @@ export const cspRuleMetadataSchema = rt.object({
name: rt.string(),
id: rt.string(),
version: rt.string(),
rule_number: rt.maybe(rt.string()),
}),
default_value: rt.maybe(rt.string()),
description: rt.string(),

View file

@ -18,7 +18,7 @@ import {
CLOUDBEAT_VANILLA,
CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE,
} from '../constants';
import { BenchmarkId } from '../types';
import type { BenchmarkId, Score } from '../types';
/**
* @example
@ -72,3 +72,15 @@ export function assert(condition: any, msg?: string): asserts condition {
throw new Error(msg);
}
}
/**
* @param value value is [0, 1] range
*/
export const roundScore = (value: number): Score => Number((value * 100).toFixed(1));
export const calculatePostureScore = (passed: number, failed: number): Score => {
const total = passed + failed;
if (total === 0) return total;
return roundScore(passed / (passed + failed));
};

View file

@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { calculatePostureScore } from '../../common/utils/helpers';
import { statusColors } from '../common/constants';
export const ComplianceScoreBar = ({
totalPassed,
totalFailed,
}: {
totalPassed: number;
totalFailed: number;
}) => {
const { euiTheme } = useEuiTheme();
const complianceScore = calculatePostureScore(totalPassed, totalFailed);
return (
<EuiFlexGroup
gutterSize="none"
alignItems="center"
justifyContent="flexEnd"
style={{ gap: euiTheme.size.s }}
>
<EuiFlexItem>
<EuiToolTip
content={i18n.translate('xpack.csp.complianceScoreBar.tooltipTitle', {
defaultMessage: '{failed} failed and {passed} passed findings',
values: {
passed: totalPassed,
failed: totalFailed,
},
})}
>
<EuiFlexGroup
gutterSize="none"
style={{
height: euiTheme.size.xs,
borderRadius: euiTheme.border.radius.medium,
overflow: 'hidden',
gap: 1,
}}
>
{!!totalFailed && (
<EuiFlexItem
style={{
flex: totalFailed,
background: statusColors.failed,
}}
/>
)}
{!!totalPassed && (
<EuiFlexItem
style={{
flex: totalPassed,
background: statusColors.passed,
}}
/>
)}
</EuiFlexGroup>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText
size="xs"
style={{ fontWeight: euiTheme.font.weight.bold }}
>{`${complianceScore.toFixed(0)}%`}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -13,12 +13,9 @@ import {
EuiFlexItem,
EuiInMemoryTable,
EuiLink,
EuiText,
EuiToolTip,
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { statusColors } from '../../../common/constants';
import { ComplianceScoreBar } from '../../../components/compliance_score_bar';
import { ComplianceDashboardData, GroupedFindingsEvaluation } from '../../../../common/types';
export interface RisksTableProps {
@ -49,8 +46,6 @@ export const RisksTable = ({
viewAllButtonTitle,
compact,
}: RisksTableProps) => {
const { euiTheme } = useEuiTheme();
const columns: Array<EuiBasicTableColumn<GroupedFindingsEvaluation>> = useMemo(
() => [
{
@ -76,63 +71,11 @@ export const RisksTable = ({
defaultMessage: 'Compliance',
}),
render: (postureScore: GroupedFindingsEvaluation['postureScore'], data) => (
<EuiFlexGroup
gutterSize="none"
alignItems="center"
justifyContent="flexEnd"
style={{ gap: euiTheme.size.s }}
>
<EuiFlexItem>
<EuiToolTip
content={i18n.translate(
'xpack.csp.complianceDashboard.complianceByCisSection.complianceColumnTooltip',
{
defaultMessage: '{passed}/{total}',
values: { passed: data.totalPassed, total: data.totalFindings },
}
)}
>
<EuiFlexGroup
gutterSize="none"
style={{
height: euiTheme.size.xs,
borderRadius: euiTheme.border.radius.medium,
overflow: 'hidden',
gap: 1,
}}
>
<EuiFlexItem
style={{
flex: data.totalFailed,
background: statusColors.failed,
}}
/>
<EuiFlexItem
style={{
flex: data.totalPassed,
background: statusColors.passed,
}}
/>
</EuiFlexGroup>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="xs" style={{ fontWeight: euiTheme.font.weight.bold }}>{`${
postureScore?.toFixed(0) || 0
}%`}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<ComplianceScoreBar totalPassed={data.totalPassed} totalFailed={data.totalFailed} />
),
},
],
[
compact,
euiTheme.border.radius.medium,
euiTheme.font.weight.bold,
euiTheme.size.s,
euiTheme.size.xs,
onCellClick,
]
[compact, onCellClick]
);
const sortedByComplianceScore = getTopRisks(cisSectionsEvaluations, maxItems);

View file

@ -89,6 +89,7 @@ export const mockFindingsHit: CspFinding = {
'Kubernetes provides a `default` service account which is used by cluster workloads where no specific service account is assigned to the pod. Where access to the Kubernetes API from a pod is required, a specific service account should be created for that pod, and rights granted to that service account. The default service account should be configured such that it does not provide a service account token and does not have any explicit rights assignments.\n',
version: '1.0',
benchmark: {
rule_number: '1.1.1',
name: 'CIS Kubernetes V1.23',
id: 'cis_k8s',
version: 'v1.0.0',

View file

@ -32,6 +32,7 @@ const getFakeFindings = (name: string): CspFinding & { id: string } => ({
rule: {
audit: chance.paragraph(),
benchmark: {
rule_number: '1.1.1',
name: 'CIS Kubernetes',
version: '1.6.0',
id: 'cis_k8s',
@ -140,12 +141,11 @@ describe('<FindingsTable />', () => {
const row = data[0];
const columns = [
'resource.id',
'result.evaluation',
'resource.sub_type',
'resource.id',
'resource.name',
'resource.sub_type',
'rule.name',
'cluster_id',
];
columns.forEach((field) => {

View file

@ -64,14 +64,13 @@ const FindingsTableComponent = ({
] = useMemo(
() => [
getExpandColumn<CspFinding>({ onClick: setSelectedFinding }),
createColumnWithFilters(baseFindingsColumns['resource.id'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['result.evaluation'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['resource.sub_type'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['resource.id'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['resource.name'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['resource.sub_type'], { onAddFilter }),
baseFindingsColumns['rule.benchmark.rule_number'],
createColumnWithFilters(baseFindingsColumns['rule.name'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['rule.benchmark.name'], { onAddFilter }),
baseFindingsColumns['rule.section'],
createColumnWithFilters(baseFindingsColumns.cluster_id, { onAddFilter }),
baseFindingsColumns['@timestamp'],
],
[onAddFilter]

View file

@ -42,7 +42,7 @@ const getDefaultQuery = ({
query,
filters,
pageIndex: 0,
sortDirection: 'desc',
sortDirection: 'asc',
});
export const FindingsByResourceContainer = ({ dataView }: FindingsBaseProps) => (
@ -179,7 +179,7 @@ const LatestFindingsByResource = ({ dataView }: FindingsBaseProps) => {
});
}}
sorting={{
sort: { field: 'failed_findings', direction: urlQuery.sortDirection },
sort: { field: 'compliance_score', direction: urlQuery.sortDirection },
}}
onAddFilter={(field, value, negate) =>
setUrlQuery({

View file

@ -7,20 +7,19 @@
import React from 'react';
import { render, screen, within } from '@testing-library/react';
import * as TEST_SUBJECTS from '../test_subjects';
import { FindingsByResourceTable, formatNumber, getResourceId } from './findings_by_resource_table';
import { FindingsByResourceTable, getResourceId } from './findings_by_resource_table';
import type { PropsOf } from '@elastic/eui';
import Chance from 'chance';
import numeral from '@elastic/numeral';
import { TestProvider } from '../../../test/test_provider';
import type { FindingsByResourcePage } from './use_findings_by_resource';
import { calculatePostureScore } from '../../../../common/utils/helpers';
const chance = new Chance();
const getFakeFindingsByResource = (): FindingsByResourcePage => {
const count = chance.integer();
const total = chance.integer() + count + 1;
const normalized = count / total;
const failed = chance.natural();
const passed = chance.natural();
const total = failed + passed;
const [resourceName, resourceSubtype, ruleBenchmarkName, ...cisSections] = chance.unique(
chance.word,
5
@ -33,9 +32,11 @@ const getFakeFindingsByResource = (): FindingsByResourcePage => {
'resource.sub_type': resourceSubtype,
'rule.section': cisSections,
'rule.benchmark.name': ruleBenchmarkName,
failed_findings: {
count,
normalized,
compliance_score: passed / total,
findings: {
failed_findings: failed,
passed_findings: passed,
normalized: passed / total,
total_findings: total,
},
};
@ -50,7 +51,7 @@ describe('<FindingsByResourceTable />', () => {
items: [],
pagination: { pageIndex: 0, pageSize: 10, totalItemCount: 0 },
sorting: {
sort: { field: 'failed_findings', direction: 'desc' },
sort: { field: 'compliance_score', direction: 'desc' },
},
setTableOptions: jest.fn(),
onAddFilter: jest.fn(),
@ -75,7 +76,7 @@ describe('<FindingsByResourceTable />', () => {
items: data,
pagination: { pageIndex: 0, pageSize: 10, totalItemCount: 0 },
sorting: {
sort: { field: 'failed_findings', direction: 'desc' },
sort: { field: 'compliance_score', direction: 'desc' },
},
setTableOptions: jest.fn(),
onAddFilter: jest.fn(),
@ -97,10 +98,13 @@ describe('<FindingsByResourceTable />', () => {
expect(within(row).getByText(item['resource.name'])).toBeInTheDocument();
if (item['resource.sub_type'])
expect(within(row).getByText(item['resource.sub_type'])).toBeInTheDocument();
expect(within(row).getByText(item['rule.section'].join(', '))).toBeInTheDocument();
expect(within(row).getByText(formatNumber(item.failed_findings.count))).toBeInTheDocument();
expect(
within(row).getByText(new RegExp(numeral(item.failed_findings.normalized).format('0%')))
within(row).getByText(
`${calculatePostureScore(
item.findings.passed_findings,
item.findings.failed_findings
).toFixed(0)}%`
)
).toBeInTheDocument();
});
});

View file

@ -8,17 +8,16 @@ import React, { useMemo } from 'react';
import {
EuiEmptyPrompt,
EuiBasicTable,
EuiTextColor,
type EuiTableFieldDataColumnType,
type CriteriaWithPagination,
type Pagination,
EuiToolTip,
EuiBasicTableProps,
} 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';
import { ComplianceScoreBar } from '../../../components/compliance_score_bar';
import * as TEST_SUBJECTS from '../test_subjects';
import type { FindingsByResourcePage } from './use_findings_by_resource';
import { findingsNavigation } from '../../../common/navigation/constants';
@ -31,9 +30,7 @@ import {
export const formatNumber = (value: number) =>
value < 1000 ? value : numeral(value).format('0.0a');
type Sorting = Required<
EuiBasicTableProps<Pick<FindingsByResourcePage, 'failed_findings'>>
>['sorting'];
type Sorting = Required<EuiBasicTableProps<FindingsByResourcePage>>['sorting'];
interface Props {
items: FindingsByResourcePage[];
@ -66,9 +63,8 @@ const FindingsByResourceTableComponent = ({
createColumnWithFilters(findingsByResourceColumns['resource.sub_type'], { onAddFilter }),
createColumnWithFilters(findingsByResourceColumns['resource.name'], { onAddFilter }),
createColumnWithFilters(findingsByResourceColumns['rule.benchmark.name'], { onAddFilter }),
findingsByResourceColumns['rule.section'],
createColumnWithFilters(findingsByResourceColumns.cluster_id, { onAddFilter }),
findingsByResourceColumns.failed_findings,
findingsByResourceColumns.compliance_score,
],
[onAddFilter]
);
@ -106,6 +102,7 @@ const baseColumns: Array<EuiTableFieldDataColumnType<FindingsByResourcePage>> =
{
...baseFindingsColumns['resource.id'],
field: 'resource_id',
width: '15%',
render: (resourceId: FindingsByResourcePage['resource_id']) => (
<Link
to={generatePath(findingsNavigation.resource_findings.path, { resourceId })}
@ -139,36 +136,21 @@ const baseColumns: Array<EuiTableFieldDataColumnType<FindingsByResourcePage>> =
},
baseFindingsColumns.cluster_id,
{
field: 'failed_findings',
field: 'compliance_score',
width: '150px',
truncateText: true,
sortable: true,
name: (
<FormattedMessage
id="xpack.csp.findings.findingsByResourceTable.failedFindingsColumnLabel"
defaultMessage="Failed Findings"
id="xpack.csp.findings.findingsByResourceTable.complianceScoreColumnLabel"
defaultMessage="Compliance Score"
/>
),
render: (failedFindings: FindingsByResourcePage['failed_findings']) => (
<EuiToolTip
content={i18n.translate(
'xpack.csp.findings.findingsByResourceTable.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>
render: (complianceScore: FindingsByResourcePage['compliance_score'], data) => (
<ComplianceScoreBar
totalPassed={data.findings.passed_findings}
totalFailed={data.findings.failed_findings}
/>
),
dataType: 'number',
},

View file

@ -57,8 +57,8 @@ const ResourceFindingsTableComponent = ({
() => [
getExpandColumn<CspFinding>({ onClick: setSelectedFinding }),
createColumnWithFilters(baseFindingsColumns['result.evaluation'], { onAddFilter }),
baseFindingsColumns['rule.benchmark.rule_number'],
createColumnWithFilters(baseFindingsColumns['rule.name'], { onAddFilter }),
createColumnWithFilters(baseFindingsColumns['rule.benchmark.name'], { onAddFilter }),
baseFindingsColumns['rule.section'],
baseFindingsColumns['@timestamp'],
],

View file

@ -9,12 +9,12 @@ import { lastValueFrom } from 'rxjs';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { Pagination } from '@elastic/eui';
import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants';
import { useKibana } from '../../../common/hooks/use_kibana';
import { showErrorToast } from '../latest_findings/use_latest_findings';
import type { FindingsBaseEsQuery, Sort } from '../types';
import { getAggregationCount, getFindingsCountAggQuery } from '../utils/utils';
import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants';
import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants';
interface UseFindingsByResourceOptions extends FindingsBaseEsQuery {
enabled: boolean;
@ -35,11 +35,13 @@ type FindingsAggResponse = IKibanaSearchResponse<
>;
export interface FindingsByResourcePage {
failed_findings: {
count: number;
findings: {
failed_findings: number;
passed_findings: number;
normalized: number;
total_findings: number;
};
compliance_score: number;
resource_id: string;
cluster_id: string;
'resource.name': string;
@ -56,6 +58,8 @@ interface FindingsByResourceAggs {
interface FindingsAggBucket extends estypes.AggregationsStringRareTermsBucketKeys {
failed_findings: estypes.AggregationsMultiBucketBase;
compliance_score: estypes.AggregationsScriptedMetricAggregate;
passed_findings: estypes.AggregationsMultiBucketBase;
name: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringTermsBucketKeys>;
subtype: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringTermsBucketKeys>;
cluster_id: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringTermsBucketKeys>;
@ -92,15 +96,27 @@ export const getFindingsByResourceAggQuery = ({
failed_findings: {
filter: { term: { 'result.evaluation': 'failed' } },
},
passed_findings: {
filter: { term: { 'result.evaluation': 'passed' } },
},
cluster_id: {
terms: { field: 'cluster_id', size: 1 },
},
sort_failed_findings: {
compliance_score: {
bucket_script: {
buckets_path: {
passed: 'passed_findings>_count',
failed: 'failed_findings>_count',
},
script: 'params.passed / (params.passed + params.failed)',
},
},
sort_by_compliance_score: {
bucket_sort: {
size: MAX_FINDINGS_TO_LOAD,
sort: [
{
'failed_findings>_count': { order: sortDirection },
compliance_score: { order: sortDirection },
_count: { order: 'desc' },
_key: { order: 'asc' },
},
@ -177,11 +193,13 @@ const createFindingsByResource = (resource: FindingsAggBucket): FindingsByResour
cluster_id: resource.cluster_id.buckets[0]?.key,
['rule.section']: resource.cis_sections.buckets.map((v) => v.key),
['rule.benchmark.name']: resource.benchmarkName.buckets[0]?.key,
failed_findings: {
count: resource.failed_findings.doc_count,
compliance_score: resource.compliance_score.value,
findings: {
failed_findings: resource.failed_findings.doc_count,
normalized:
resource.doc_count > 0 ? resource.failed_findings.doc_count / resource.doc_count : 0,
total_findings: resource.doc_count,
passed_findings: resource.passed_findings.doc_count,
},
};
};

View file

@ -123,9 +123,10 @@ const baseColumns = [
},
{
field: 'rule.name',
name: i18n.translate('xpack.csp.findings.findingsTable.findingsTableColumn.ruleColumnLabel', {
defaultMessage: 'Rule',
}),
name: i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleNameColumnLabel',
{ defaultMessage: 'Rule Name' }
),
sortable: true,
render: (name: string) => (
<EuiToolTip content={name} position="left" anchorClassName="eui-textTruncate">
@ -134,12 +135,29 @@ const baseColumns = [
),
},
{
field: 'rule.benchmark.name',
field: 'rule.benchmark.rule_number',
name: i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnLabel',
{ defaultMessage: 'Benchmark' }
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleNumberColumnLabel',
{
defaultMessage: 'Rule Number',
}
),
width: '120px',
},
{
field: 'rule.benchmark.name',
name: (
<ColumnNameWithTooltip
columnName={i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnLabel',
{ defaultMessage: 'Applicable Benchmark' }
)}
tooltipContent={i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnTooltipLabel',
{ defaultMessage: 'The benchmark(s) rules used to evaluate this resource came from' }
)}
/>
),
width: '10%',
sortable: true,
truncateText: true,
},
@ -149,7 +167,6 @@ const baseColumns = [
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel',
{ defaultMessage: 'CIS Section' }
),
width: '7%',
sortable: true,
truncateText: true,
render: (section: string) => (
@ -164,15 +181,14 @@ const baseColumns = [
<ColumnNameWithTooltip
columnName={i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.clusterIdColumnLabel',
{ defaultMessage: 'Cluster ID' }
{ defaultMessage: 'Belongs To' }
)}
tooltipContent={i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.clusterIdColumnTooltipLabel',
{ defaultMessage: 'Kube-System Namespace ID' }
{ defaultMessage: 'Kubernetes Cluster ID or Cloud Account Name' }
)}
/>
),
width: '150px',
sortable: true,
truncateText: true,
render: (section: string) => (
@ -183,6 +199,7 @@ const baseColumns = [
},
{
field: '@timestamp',
align: 'right',
width: '10%',
name: i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.lastCheckedColumnLabel',

View file

@ -7,7 +7,7 @@
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { Logger } from '@kbn/core/server';
import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
import { calculatePostureScore } from '../../../routes/compliance_dashboard/get_stats';
import { calculatePostureScore } from '../../../../common/utils/helpers';
import type { CspmAccountsStats } from './types';
import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../../../common/constants';

View file

@ -11,7 +11,7 @@ import type {
QueryDslQueryContainer,
SearchRequest,
} from '@elastic/elasticsearch/lib/api/types';
import { calculatePostureScore } from './get_stats';
import { calculatePostureScore } from '../../../common/utils/helpers';
import type { ComplianceDashboardData } from '../../../common/types';
import { KeyDocCount } from './compliance_dashboard';

View file

@ -5,12 +5,8 @@
* 2.0.
*/
import {
calculatePostureScore,
FindingsEvaluationsQueryResult,
getStatsFromFindingsEvaluationsAggs,
roundScore,
} from './get_stats';
import { FindingsEvaluationsQueryResult, getStatsFromFindingsEvaluationsAggs } from './get_stats';
import { calculatePostureScore, roundScore } from '../../../common/utils/helpers';
const standardQueryResult: FindingsEvaluationsQueryResult = {
resources_evaluated: {

View file

@ -7,15 +7,8 @@
import { ElasticsearchClient } from '@kbn/core/server';
import type { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types';
import type { ComplianceDashboardData, Score } from '../../../common/types';
/**
* @param value value is [0, 1] range
*/
export const roundScore = (value: number): Score => Number((value * 100).toFixed(1));
export const calculatePostureScore = (passed: number, failed: number): Score =>
roundScore(passed / (passed + failed));
import { calculatePostureScore } from '../../../common/utils/helpers';
import type { ComplianceDashboardData } from '../../../common/types';
export interface FindingsEvaluationsQueryResult {
failed_findings: {

View file

@ -6,9 +6,9 @@
*/
import { ElasticsearchClient } from '@kbn/core/server';
import { calculatePostureScore } from '../../../common/utils/helpers';
import { BENCHMARK_SCORE_INDEX_DEFAULT_NS } from '../../../common/constants';
import type { PosturePolicyTemplate, Stats } from '../../../common/types';
import { calculatePostureScore } from './get_stats';
export interface ScoreTrendDoc {
'@timestamp': string;

View file

@ -28,7 +28,7 @@ function migrateCspRuleMetadata(
policy_id,
metadata: {
...metadata,
benchmark: { ...benchmark, id: 'cis_k8s' },
benchmark: { ...benchmark, id: 'cis_k8s', rule_number: '' },
impact: metadata.impact || undefined,
default_value: metadata.default_value || undefined,
references: metadata.references || undefined,

View file

@ -28,7 +28,7 @@ function migrateCspRuleMetadata(
muted,
metadata: {
...metadata,
benchmark: { ...benchmark, id: 'cis_k8s' },
benchmark: { ...benchmark, id: 'cis_k8s', rule_number: '' },
impact: metadata.impact || undefined,
default_value: metadata.default_value || undefined,
references: metadata.references || undefined,

View file

@ -10135,12 +10135,10 @@
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " pour \"{name}\"",
"xpack.csp.benchmarks.totalIntegrationsCountMessage": "Affichage de {pageCount} sur {totalCount, plural, one {# intégration} other {# intégrations}}",
"xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode} : {body}",
"xpack.csp.complianceDashboard.complianceByCisSection.complianceColumnTooltip": "{passed}/{total}",
"xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "Dernière évaluation {dateFromNow}",
"xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "Affichage de {pageStart}-{pageEnd} sur {total} {type}",
"xpack.csp.findings.findingsByResourceTable.failedFindingsToolTip": "{failed} sur {total}",
"xpack.csp.findings.findingsTableCell.addFilterButton": "Ajouter un filtre {field}",
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "Ajouter un filtre {field} négatif",
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceName} - Résultats",
@ -10198,7 +10196,6 @@
"xpack.csp.findings.findingsByResource.noFindingsTitle": "Il n'y a aucun résultat",
"xpack.csp.findings.findingsByResource.tableRowTypeLabel": "Ressources",
"xpack.csp.findings.findingsByResourceTable.cisSectionsColumnLabel": "Sections CIS",
"xpack.csp.findings.findingsByResourceTable.failedFindingsColumnLabel": "Échec des résultats",
"xpack.csp.findings.findingsErrorToast.searchFailedTitle": "Échec de la recherche",
"xpack.csp.findings.findingsFlyout.jsonTabTitle": "JSON",
"xpack.csp.findings.findingsFlyout.overviewTab.actualTitle": "Réel",
@ -10240,7 +10237,6 @@
"xpack.csp.findings.findingsTable.findingsTableColumn.resourceTypeColumnLabel": "Type de ressource",
"xpack.csp.findings.findingsTable.findingsTableColumn.resultColumnLabel": "Résultat",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnLabel": "Benchmark",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleColumnLabel": "Règle",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel": "Section CIS",
"xpack.csp.findings.groupBySelector.groupByLabel": "Regrouper par",
"xpack.csp.findings.groupBySelector.groupByNoneLabel": "Aucun",

View file

@ -10124,12 +10124,10 @@
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " \"{name}\"",
"xpack.csp.benchmarks.totalIntegrationsCountMessage": "{pageCount}/{totalCount, plural, other {#個の統合}}を表示しています",
"xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode}: {body}",
"xpack.csp.complianceDashboard.complianceByCisSection.complianceColumnTooltip": "{passed}/{total}",
"xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "前回の評価{dateFromNow}",
"xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "{total}件中{pageStart}-{pageEnd}件の{type}を表示しています",
"xpack.csp.findings.findingsByResourceTable.failedFindingsToolTip": "{total}件中{failed}件",
"xpack.csp.findings.findingsTableCell.addFilterButton": "{field}フィルターを追加",
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "{field}否定フィルターを追加",
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceName} - 調査結果",
@ -10187,7 +10185,6 @@
"xpack.csp.findings.findingsByResource.noFindingsTitle": "調査結果はありません",
"xpack.csp.findings.findingsByResource.tableRowTypeLabel": "リソース",
"xpack.csp.findings.findingsByResourceTable.cisSectionsColumnLabel": "CISセクション",
"xpack.csp.findings.findingsByResourceTable.failedFindingsColumnLabel": "失敗した調査結果",
"xpack.csp.findings.findingsErrorToast.searchFailedTitle": "検索失敗",
"xpack.csp.findings.findingsFlyout.jsonTabTitle": "JSON",
"xpack.csp.findings.findingsFlyout.overviewTab.actualTitle": "実際",
@ -10229,7 +10226,6 @@
"xpack.csp.findings.findingsTable.findingsTableColumn.resourceTypeColumnLabel": "リソースタイプ",
"xpack.csp.findings.findingsTable.findingsTableColumn.resultColumnLabel": "結果",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnLabel": "ベンチマーク",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleColumnLabel": "ルール",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel": "CISセクション",
"xpack.csp.findings.groupBySelector.groupByLabel": "グループ分けの条件",
"xpack.csp.findings.groupBySelector.groupByNoneLabel": "なし",

View file

@ -10139,12 +10139,10 @@
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundForNameTitle": " 对于“{name}”",
"xpack.csp.benchmarks.totalIntegrationsCountMessage": "正在显示 {pageCount}/{totalCount, plural, other {# 个集成}}",
"xpack.csp.cloudPosturePage.errorRenderer.errorDescription": "{error} {statusCode}{body}",
"xpack.csp.complianceDashboard.complianceByCisSection.complianceColumnTooltip": "{passed}/{total}",
"xpack.csp.dashboard.benchmarkSection.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.clusterTitleTooltip.clusterTitle": "{title} - {shortId}",
"xpack.csp.dashboard.benchmarkSection.lastEvaluatedTitle": "上次评估于 {dateFromNow}",
"xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "正在显示第 {pageStart}-{pageEnd} 个(共 {total} 个){type}",
"xpack.csp.findings.findingsByResourceTable.failedFindingsToolTip": "{failed} 个(共 {total} 个)",
"xpack.csp.findings.findingsTableCell.addFilterButton": "添加 {field} 筛选",
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "添加 {field} 作废筛选",
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceName} - 结果",
@ -10202,7 +10200,6 @@
"xpack.csp.findings.findingsByResource.noFindingsTitle": "无结果",
"xpack.csp.findings.findingsByResource.tableRowTypeLabel": "资源",
"xpack.csp.findings.findingsByResourceTable.cisSectionsColumnLabel": "CIS 部分",
"xpack.csp.findings.findingsByResourceTable.failedFindingsColumnLabel": "失败的结果",
"xpack.csp.findings.findingsErrorToast.searchFailedTitle": "搜索失败",
"xpack.csp.findings.findingsFlyout.jsonTabTitle": "JSON",
"xpack.csp.findings.findingsFlyout.overviewTab.actualTitle": "实际",
@ -10244,7 +10241,6 @@
"xpack.csp.findings.findingsTable.findingsTableColumn.resourceTypeColumnLabel": "资源类型",
"xpack.csp.findings.findingsTable.findingsTableColumn.resultColumnLabel": "结果",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleBenchmarkColumnLabel": "基准",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleColumnLabel": "规则",
"xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel": "CIS 部分",
"xpack.csp.findings.groupBySelector.groupByLabel": "分组依据",
"xpack.csp.findings.groupBySelector.groupByNoneLabel": "无",

View file

@ -88,7 +88,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await filterBar.addFilter({ field: 'rule.name', operation: 'is', value: ruleName1 });
expect(await filterBar.hasFilter('rule.name', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule Name', ruleName1)).to.be(true);
});
it('remove filter', async () => {
@ -102,8 +102,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await queryBar.setQuery(ruleName1);
await queryBar.submitQuery();
expect(await table.hasColumnValue('Rule', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule', ruleName2)).to.be(false);
expect(await table.hasColumnValue('Rule Name', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule Name', ruleName2)).to.be(false);
await queryBar.setQuery('');
await queryBar.submitQuery();
@ -114,18 +114,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('Table Filters', () => {
it('add cell value filter', async () => {
await table.addCellFilter('Rule', ruleName1, false);
await table.addCellFilter('Rule Name', ruleName1, false);
expect(await filterBar.hasFilter('rule.name', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule', ruleName1)).to.be(true);
expect(await table.hasColumnValue('Rule Name', ruleName1)).to.be(true);
});
it('add negated cell value filter', async () => {
await table.addCellFilter('Rule', ruleName1, true);
await table.addCellFilter('Rule Name', ruleName1, true);
expect(await filterBar.hasFilter('rule.name', ruleName1, true, false, true)).to.be(true);
expect(await table.hasColumnValue('Rule', ruleName1)).to.be(false);
expect(await table.hasColumnValue('Rule', ruleName2)).to.be(true);
expect(await table.hasColumnValue('Rule Name', ruleName1)).to.be(false);
expect(await table.hasColumnValue('Rule Name', ruleName2)).to.be(true);
await filterBar.removeFilter('rule.name');
});
@ -147,8 +147,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const testCases: TestCase[] = [
['CIS Section', 'asc', sortByAlphabeticalOrder],
['CIS Section', 'desc', sortByAlphabeticalOrder],
['Cluster ID', 'asc', compareStringByLexicographicOrder],
['Cluster ID', 'desc', compareStringByLexicographicOrder],
['Resource ID', 'asc', compareStringByLexicographicOrder],
['Resource ID', 'desc', compareStringByLexicographicOrder],
['Resource Name', 'asc', sortByAlphabeticalOrder],
['Resource Name', 'desc', sortByAlphabeticalOrder],
['Resource Type', 'asc', sortByAlphabeticalOrder],