mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cloud Posture] Tables shared values (#140295)
This commit is contained in:
parent
b5a35d74e7
commit
49b39dff2a
13 changed files with 249 additions and 61 deletions
|
@ -6,8 +6,44 @@
|
|||
*/
|
||||
|
||||
import { euiPaletteForStatus } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA } from '../../common/constants';
|
||||
|
||||
const [success, warning, danger] = euiPaletteForStatus(3);
|
||||
|
||||
export const statusColors = { success, warning, danger };
|
||||
export const CSP_MOMENT_FORMAT = 'MMMM D, YYYY @ HH:mm:ss.SSS';
|
||||
|
||||
export type CloudPostureIntegrations = typeof cloudPostureIntegrations;
|
||||
|
||||
export const cloudPostureIntegrations = {
|
||||
kspm: {
|
||||
policyTemplate: 'kspm',
|
||||
name: i18n.translate('xpack.csp.kspmIntegration.integration.nameTitle', {
|
||||
defaultMessage: 'Kubernetes Security Posture Management',
|
||||
}),
|
||||
shortName: i18n.translate('xpack.csp.kspmIntegration.integration.shortNameTitle', {
|
||||
defaultMessage: 'KSPM',
|
||||
}),
|
||||
options: [
|
||||
{
|
||||
type: CLOUDBEAT_VANILLA,
|
||||
name: i18n.translate('xpack.csp.kspmIntegration.vanillaOption.nameTitle', {
|
||||
defaultMessage: 'Unmanaged Kubernetes',
|
||||
}),
|
||||
benchmark: i18n.translate('xpack.csp.kspmIntegration.vanillaOption.benchmarkTitle', {
|
||||
defaultMessage: 'CIS Kubernetes',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: CLOUDBEAT_EKS,
|
||||
name: i18n.translate('xpack.csp.kspmIntegration.eksOption.nameTitle', {
|
||||
defaultMessage: 'EKS (Elastic Kubernetes Service)',
|
||||
}),
|
||||
benchmark: i18n.translate('xpack.csp.kspmIntegration.eksOption.benchmarkTitle', {
|
||||
defaultMessage: 'CIS EKS',
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
} as const;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiDescriptionList, useEuiTheme, type EuiDescriptionListProps } from '@elastic/eui';
|
||||
|
||||
const getModifiedTitlesListItems = (listItems: EuiDescriptionListProps['listItems']) =>
|
||||
listItems
|
||||
?.filter((item) => !!item?.title && !!item?.description)
|
||||
.map((item) => ({ ...item, title: `${item.title}:` }));
|
||||
|
||||
// eui size m is 12px which is too small, and next after it is base which is 16px which is too big
|
||||
const fontSize = '1rem';
|
||||
|
||||
export const CspInlineDescriptionList = ({
|
||||
listItems,
|
||||
}: {
|
||||
listItems: EuiDescriptionListProps['listItems'];
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const modifiedTitlesListItems = getModifiedTitlesListItems(listItems);
|
||||
|
||||
return (
|
||||
<EuiDescriptionList
|
||||
type="inline"
|
||||
titleProps={{
|
||||
style: {
|
||||
background: 'initial',
|
||||
color: euiTheme.colors.subduedText,
|
||||
fontSize,
|
||||
paddingRight: 0,
|
||||
},
|
||||
}}
|
||||
descriptionProps={{
|
||||
style: {
|
||||
color: euiTheme.colors.subduedText,
|
||||
marginRight: euiTheme.size.xs,
|
||||
fontSize,
|
||||
},
|
||||
}}
|
||||
listItems={modifiedTitlesListItems}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -16,7 +16,7 @@ import {
|
|||
EuiDescribedFormGroup,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { cloudPostureIntegrations } from '../../common/constants';
|
||||
import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA } from '../../../common/constants';
|
||||
|
||||
export type InputType = typeof CLOUDBEAT_EKS | typeof CLOUDBEAT_VANILLA;
|
||||
|
@ -27,22 +27,8 @@ interface Props {
|
|||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
const kubeDeployOptions: Array<EuiComboBoxOptionOption<InputType>> = [
|
||||
{
|
||||
value: CLOUDBEAT_VANILLA,
|
||||
label: i18n.translate(
|
||||
'xpack.csp.createPackagePolicy.stepConfigure.integrationSettingsSection.vanillaKubernetesDeploymentOption',
|
||||
{ defaultMessage: 'Unmanaged Kubernetes' }
|
||||
),
|
||||
},
|
||||
{
|
||||
value: CLOUDBEAT_EKS,
|
||||
label: i18n.translate(
|
||||
'xpack.csp.createPackagePolicy.stepConfigure.integrationSettingsSection.eksKubernetesDeploymentOption',
|
||||
{ defaultMessage: 'EKS (Elastic Kubernetes Service)' }
|
||||
),
|
||||
},
|
||||
];
|
||||
const kubeDeployOptions: Array<EuiComboBoxOptionOption<InputType>> =
|
||||
cloudPostureIntegrations.kspm.options.map((o) => ({ value: o.type, label: o.name }));
|
||||
|
||||
const KubernetesDeploymentFieldLabel = () => (
|
||||
<EuiToolTip
|
||||
|
|
|
@ -18,8 +18,8 @@ import { CspFinding } from '../../../../common/schemas/csp_finding';
|
|||
const chance = new Chance();
|
||||
|
||||
const getFakeFindings = (name: string): CspFinding & { id: string } => ({
|
||||
id: chance.word(),
|
||||
cluster_id: chance.guid(),
|
||||
id: chance.word(),
|
||||
result: {
|
||||
expected: {
|
||||
source: {},
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiSpacer, EuiButtonEmpty } from '@elastic/eui';
|
||||
import {
|
||||
EuiSpacer,
|
||||
EuiButtonEmpty,
|
||||
EuiPageHeader,
|
||||
type EuiDescriptionListProps,
|
||||
} from '@elastic/eui';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { CspInlineDescriptionList } from '../../../../components/csp_inline_description_list';
|
||||
import type { Evaluation } from '../../../../../common/types';
|
||||
import { CspFinding } from '../../../../../common/schemas/csp_finding';
|
||||
import { CloudPosturePageTitle } from '../../../../components/cloud_posture_page_title';
|
||||
|
@ -54,6 +60,32 @@ const BackToResourcesButton = () => (
|
|||
</Link>
|
||||
);
|
||||
|
||||
const getResourceFindingSharedValues = (sharedValues: {
|
||||
resourceId: string;
|
||||
resourceSubType: string;
|
||||
resourceName: string;
|
||||
clusterId: string;
|
||||
}): EuiDescriptionListProps['listItems'] => [
|
||||
{
|
||||
title: i18n.translate('xpack.csp.findings.resourceFindingsSharedValues.resourceTypeTitle', {
|
||||
defaultMessage: 'Resource Type',
|
||||
}),
|
||||
description: sharedValues.resourceSubType,
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.csp.findings.resourceFindingsSharedValues.resourceIdTitle', {
|
||||
defaultMessage: 'Resource ID',
|
||||
}),
|
||||
description: sharedValues.resourceId,
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.csp.findings.resourceFindingsSharedValues.clusterIdTitle', {
|
||||
defaultMessage: 'Cluster ID',
|
||||
}),
|
||||
description: sharedValues.clusterId,
|
||||
},
|
||||
];
|
||||
|
||||
export const ResourceFindings = ({ dataView }: FindingsBaseProps) => {
|
||||
const params = useParams<{ resourceId: string }>();
|
||||
const getPersistedDefaultQuery = usePersistedQuery(getDefaultQuery);
|
||||
|
@ -114,14 +146,28 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => {
|
|||
title={i18n.translate(
|
||||
'xpack.csp.findings.resourceFindings.resourceFindingsPageTitle',
|
||||
{
|
||||
defaultMessage: '{resourceId} - Findings',
|
||||
values: { resourceId: params.resourceId },
|
||||
defaultMessage: '{resourceName} - Findings',
|
||||
values: { resourceName: resourceFindings.data?.resourceName },
|
||||
}
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</PageTitle>
|
||||
<EuiPageHeader
|
||||
description={
|
||||
resourceFindings.data && (
|
||||
<CspInlineDescriptionList
|
||||
listItems={getResourceFindingSharedValues({
|
||||
resourceId: params.resourceId,
|
||||
resourceName: resourceFindings.data.resourceName,
|
||||
resourceSubType: resourceFindings.data.resourceSubType,
|
||||
clusterId: resourceFindings.data.clusterId,
|
||||
})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
{error && <ErrorCallout error={error} />}
|
||||
{!error && (
|
||||
|
|
|
@ -49,21 +49,11 @@ const ResourceFindingsTableComponent = ({
|
|||
] = useMemo(
|
||||
() => [
|
||||
getExpandColumn<CspFinding>({ onClick: setSelectedFinding }),
|
||||
baseFindingsColumns['resource.id'],
|
||||
createColumnWithFilters(baseFindingsColumns['result.evaluation'], { onAddFilter }),
|
||||
createColumnWithFilters(
|
||||
{ ...baseFindingsColumns['resource.sub_type'], sortable: false },
|
||||
{ onAddFilter }
|
||||
),
|
||||
createColumnWithFilters(
|
||||
{ ...baseFindingsColumns['resource.name'], sortable: false },
|
||||
{ onAddFilter }
|
||||
),
|
||||
createColumnWithFilters(baseFindingsColumns['rule.name'], { onAddFilter }),
|
||||
createColumnWithFilters(baseFindingsColumns['rule.benchmark.name'], { onAddFilter }),
|
||||
baseFindingsColumns['rule.section'],
|
||||
baseFindingsColumns['rule.tags'],
|
||||
createColumnWithFilters(baseFindingsColumns.cluster_id, { onAddFilter }),
|
||||
baseFindingsColumns['@timestamp'],
|
||||
],
|
||||
[onAddFilter]
|
||||
|
|
|
@ -34,11 +34,14 @@ export interface ResourceFindingsQuery {
|
|||
}
|
||||
|
||||
type ResourceFindingsRequest = IKibanaSearchRequest<estypes.SearchRequest>;
|
||||
type ResourceFindingsResponse = IKibanaSearchResponse<estypes.SearchResponse<CspFinding, Aggs>>;
|
||||
type ResourceFindingsResponse = IKibanaSearchResponse<
|
||||
estypes.SearchResponse<CspFinding, ResourceFindingsResponseAggs>
|
||||
>;
|
||||
|
||||
interface Aggs {
|
||||
count: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringRareTermsBucketKeys>;
|
||||
}
|
||||
export type ResourceFindingsResponseAggs = Record<
|
||||
'count' | 'clusterId' | 'resourceSubType' | 'resourceName',
|
||||
estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringRareTermsBucketKeys>
|
||||
>;
|
||||
|
||||
const getResourceFindingsQuery = ({
|
||||
query,
|
||||
|
@ -60,7 +63,18 @@ const getResourceFindingsQuery = ({
|
|||
},
|
||||
sort: [{ [sort.field]: sort.direction }],
|
||||
pit: { id: pitId },
|
||||
aggs: getFindingsCountAggQuery(),
|
||||
aggs: {
|
||||
...getFindingsCountAggQuery(),
|
||||
clusterId: {
|
||||
terms: { field: 'cluster_id' },
|
||||
},
|
||||
resourceSubType: {
|
||||
terms: { field: 'resource.sub_type' },
|
||||
},
|
||||
resourceName: {
|
||||
terms: { field: 'resource.name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
ignore_unavailable: false,
|
||||
});
|
||||
|
@ -90,13 +104,18 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => {
|
|||
}: ResourceFindingsResponse) => {
|
||||
if (!aggregations) throw new Error('expected aggregations to exists');
|
||||
|
||||
if (!Array.isArray(aggregations?.count.buckets))
|
||||
throw new Error('expected buckets to be an array');
|
||||
assertNonEmptyArray(aggregations.count.buckets);
|
||||
assertNonEmptyArray(aggregations.clusterId.buckets);
|
||||
assertNonEmptyArray(aggregations.resourceSubType.buckets);
|
||||
assertNonEmptyArray(aggregations.resourceName.buckets);
|
||||
|
||||
return {
|
||||
page: hits.hits.map((hit) => hit._source!),
|
||||
total: number.is(hits.total) ? hits.total : 0,
|
||||
count: getAggregationCount(aggregations.count.buckets),
|
||||
clusterId: getFirstBucketKey(aggregations.clusterId.buckets),
|
||||
resourceSubType: getFirstBucketKey(aggregations.resourceSubType.buckets),
|
||||
resourceName: getFirstBucketKey(aggregations.resourceName.buckets),
|
||||
newPitId: newPitId!,
|
||||
};
|
||||
},
|
||||
|
@ -110,3 +129,12 @@ export const useResourceFindings = (options: UseResourceFindingsOptions) => {
|
|||
}
|
||||
);
|
||||
};
|
||||
|
||||
function assertNonEmptyArray<T>(arr: unknown): asserts arr is T[] {
|
||||
if (!Array.isArray(arr) || arr.length === 0) {
|
||||
throw new Error('expected a non empty array');
|
||||
}
|
||||
}
|
||||
|
||||
const getFirstBucketKey = (buckets: estypes.AggregationsStringRareTermsBucketKeys[]) =>
|
||||
buckets[0].key;
|
||||
|
|
|
@ -7,10 +7,18 @@
|
|||
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import { generatePath, Link, type RouteComponentProps } from 'react-router-dom';
|
||||
import { EuiTextColor, EuiButtonEmpty, EuiFlexGroup, EuiPageHeader, EuiSpacer } from '@elastic/eui';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
type EuiDescriptionListProps,
|
||||
EuiFlexGroup,
|
||||
EuiPageHeader,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { pagePathGetters } from '@kbn/fleet-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { CspInlineDescriptionList } from '../../components/csp_inline_description_list';
|
||||
import { CloudPosturePageTitle } from '../../components/cloud_posture_page_title';
|
||||
import type { BreadcrumbEntry } from '../../common/navigation/types';
|
||||
import { RulesContainer, type PageUrlParams } from './rules_container';
|
||||
|
@ -20,6 +28,8 @@ import { useCspIntegrationInfo } from './use_csp_integration';
|
|||
import { useKibana } from '../../common/hooks/use_kibana';
|
||||
import { CloudPosturePage } from '../../components/cloud_posture_page';
|
||||
import { SecuritySolutionContext } from '../../application/security_solution_context';
|
||||
import { CloudPostureIntegrations, cloudPostureIntegrations } from '../../common/constants';
|
||||
import * as TEST_SUBJECTS from './test_subjects';
|
||||
|
||||
const getRulesBreadcrumbs = (
|
||||
name?: string,
|
||||
|
@ -41,12 +51,55 @@ const getRulesBreadcrumbs = (
|
|||
return breadCrumbs;
|
||||
};
|
||||
|
||||
const isPolicyTemplate = (name: unknown): name is keyof CloudPostureIntegrations =>
|
||||
typeof name === 'string' && name in cloudPostureIntegrations;
|
||||
|
||||
const getRulesSharedValues = (
|
||||
packageInfo?: PackagePolicy
|
||||
): NonNullable<EuiDescriptionListProps['listItems']> => {
|
||||
const enabledInput = packageInfo?.inputs.find((input) => input.enabled);
|
||||
if (!enabledInput || !isPolicyTemplate(enabledInput.policy_template)) return [];
|
||||
|
||||
const integration = cloudPostureIntegrations[enabledInput.policy_template];
|
||||
const enabledIntegrationOption = integration.options.find(
|
||||
(option) => option.type === enabledInput.type
|
||||
);
|
||||
|
||||
const values = [
|
||||
{
|
||||
title: i18n.translate('xpack.csp.rules.rulesPageSharedValues.integrationTitle', {
|
||||
defaultMessage: 'Integration',
|
||||
}),
|
||||
description: integration.shortName,
|
||||
},
|
||||
];
|
||||
|
||||
if (!enabledIntegrationOption) return values;
|
||||
|
||||
values.push(
|
||||
{
|
||||
title: i18n.translate('xpack.csp.rules.rulesPageSharedValues.deploymentTypeTitle', {
|
||||
defaultMessage: 'Deployment Type',
|
||||
}),
|
||||
description: enabledIntegrationOption.name,
|
||||
},
|
||||
{
|
||||
title: i18n.translate('xpack.csp.rules.rulesPageSharedValues.benchmarkTitle', {
|
||||
defaultMessage: 'Benchmark',
|
||||
}),
|
||||
description: enabledIntegrationOption.benchmark,
|
||||
}
|
||||
);
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
export const Rules = ({ match: { params } }: RouteComponentProps<PageUrlParams>) => {
|
||||
const { http } = useKibana().services;
|
||||
const integrationInfo = useCspIntegrationInfo(params);
|
||||
const securitySolutionContext = useContext(SecuritySolutionContext);
|
||||
|
||||
const [packageInfo, agentInfo] = integrationInfo.data || [];
|
||||
const [packageInfo] = integrationInfo.data || [];
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
() =>
|
||||
|
@ -56,6 +109,8 @@ export const Rules = ({ match: { params } }: RouteComponentProps<PageUrlParams>)
|
|||
|
||||
useCspBreadcrumbs(breadcrumbs);
|
||||
|
||||
const sharedValues = getRulesSharedValues(packageInfo);
|
||||
|
||||
return (
|
||||
<CloudPosturePage query={integrationInfo}>
|
||||
<EuiPageHeader
|
||||
|
@ -93,18 +148,10 @@ export const Rules = ({ match: { params } }: RouteComponentProps<PageUrlParams>)
|
|||
</EuiFlexGroup>
|
||||
}
|
||||
description={
|
||||
packageInfo?.package &&
|
||||
agentInfo?.name && (
|
||||
<EuiTextColor color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.csp.rules.rulePageHeader.pageDescriptionTitle"
|
||||
defaultMessage="{integrationType}, {agentPolicyName}"
|
||||
values={{
|
||||
integrationType: packageInfo.package.title,
|
||||
agentPolicyName: agentInfo.name,
|
||||
}}
|
||||
/>
|
||||
</EuiTextColor>
|
||||
sharedValues.length && (
|
||||
<div data-test-subj={TEST_SUBJECTS.CSP_RULES_SHARED_VALUES}>
|
||||
<CspInlineDescriptionList listItems={sharedValues} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
bottomBorder
|
||||
|
|
|
@ -105,6 +105,18 @@ describe('<Rules />', () => {
|
|||
package: {
|
||||
title: 'my package',
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
enabled: true,
|
||||
policy_template: 'kspm',
|
||||
type: 'cloudbeat/cis_k8s',
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
policy_template: 'kspm',
|
||||
type: 'cloudbeat/cis_eks',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'my agent' },
|
||||
],
|
||||
|
@ -114,9 +126,7 @@ describe('<Rules />', () => {
|
|||
|
||||
render(<Component />);
|
||||
|
||||
expect(
|
||||
await screen.findByText(`${response.data?.[0]?.package?.title}, ${response.data?.[1].name}`)
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(TEST_SUBJECTS.CSP_RULES_CONTAINER)).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(TEST_SUBJECTS.CSP_RULES_SHARED_VALUES)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
*/
|
||||
|
||||
export const CSP_RULES_CONTAINER = 'csp_rules_container';
|
||||
export const CSP_RULES_SHARED_VALUES = 'csp_rules_shared_values';
|
||||
export const CSP_RULES_TABLE_ITEM_SWITCH = 'csp_rules_table_item_switch';
|
||||
export const CSP_RULES_SAVE_BUTTON = 'csp_rules_table_save_button';
|
||||
export const CSP_RULES_TABLE = 'csp_rules_table';
|
||||
export const CSP_RULES_TABLE_ROW_ITEM_NAME = 'csp_rules_table_row_item_name';
|
||||
export const CSP_RULES_FLYOUT_CONTAINER = 'csp_rules_flyout_container';
|
||||
|
|
|
@ -9535,10 +9535,8 @@
|
|||
"xpack.csp.findings.findingsTableCell.addFilterButton": "Ajouter un filtre {field}",
|
||||
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "Ajouter un filtre {field} négatif",
|
||||
"xpack.csp.findings.latestFindings.bottomBarLabel": "Voici les {maxItems} premiers résultats correspondant à votre recherche. Veuillez l'affiner pour en voir davantage.",
|
||||
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceId} - Résultats",
|
||||
"xpack.csp.rules.header.rulesCountLabel": "{count, plural, one { règle} other { règles}}",
|
||||
"xpack.csp.rules.header.totalRulesCount": "Affichage des {rules}",
|
||||
"xpack.csp.rules.rulePageHeader.pageDescriptionTitle": "{integrationType}, {agentPolicyName}",
|
||||
"xpack.csp.rules.rulePageHeader.pageHeaderTitle": "Règles - {integrationName}",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "Aucune intégration Benchmark trouvée",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundWithFiltersTitle": "Nous n'avons trouvé aucune intégration Benchmark avec les filtres ci-dessus.",
|
||||
|
|
|
@ -9522,10 +9522,8 @@
|
|||
"xpack.csp.findings.findingsTableCell.addFilterButton": "{field}フィルターを追加",
|
||||
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "{field}否定フィルターを追加",
|
||||
"xpack.csp.findings.latestFindings.bottomBarLabel": "これらは検索条件に一致した初めの{maxItems}件の調査結果です。他の結果を表示するには検索条件を絞ってください。",
|
||||
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceId} - 調査結果",
|
||||
"xpack.csp.rules.header.rulesCountLabel": "{count, plural, other {個のルール}}",
|
||||
"xpack.csp.rules.header.totalRulesCount": "{rules}を表示しています",
|
||||
"xpack.csp.rules.rulePageHeader.pageDescriptionTitle": "{integrationType}, {agentPolicyName}",
|
||||
"xpack.csp.rules.rulePageHeader.pageHeaderTitle": "ルール - {integrationName}",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "ベンチマーク統合が見つかりません",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundWithFiltersTitle": "上記のフィルターでベンチマーク統合が見つかりませんでした。",
|
||||
|
|
|
@ -9537,10 +9537,8 @@
|
|||
"xpack.csp.findings.findingsTableCell.addFilterButton": "添加 {field} 筛选",
|
||||
"xpack.csp.findings.findingsTableCell.addNegateFilterButton": "添加 {field} 作废筛选",
|
||||
"xpack.csp.findings.latestFindings.bottomBarLabel": "这些是匹配您的搜索的前 {maxItems} 个结果,请优化搜索以查看其他结果。",
|
||||
"xpack.csp.findings.resourceFindings.resourceFindingsPageTitle": "{resourceId} - 结果",
|
||||
"xpack.csp.rules.header.rulesCountLabel": "{count, plural, other { 规则}}",
|
||||
"xpack.csp.rules.header.totalRulesCount": "正在显示 {rules}",
|
||||
"xpack.csp.rules.rulePageHeader.pageDescriptionTitle": "{integrationType},{agentPolicyName}",
|
||||
"xpack.csp.rules.rulePageHeader.pageHeaderTitle": "规则 - {integrationName}",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundTitle": "找不到基准集成",
|
||||
"xpack.csp.benchmarks.benchmarkEmptyState.integrationsNotFoundWithFiltersTitle": "使用上述筛选,我们无法找到任何基准集成。",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue