diff --git a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts index 3ad655738e24e..ac978f3b220e4 100644 --- a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts +++ b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_belongs_to_runtime_mapping.ts @@ -18,8 +18,8 @@ export const getBelongsToRuntimeMapping = (): MappingRuntimeFields => ({ source: ` if (!doc.containsKey('rule.benchmark.posture_type')) { - def identifier = doc["cluster_id"].value; - emit(identifier); + def belongs_to = doc["cluster_id"].value; + emit(belongs_to); return } else @@ -29,21 +29,21 @@ export const getBelongsToRuntimeMapping = (): MappingRuntimeFields => ({ def policy_template_type = doc["rule.benchmark.posture_type"].value; if (policy_template_type == "cspm") { - def identifier = doc["cloud.account.name"].value; - emit(identifier); + def belongs_to = doc["cloud.account.name"].value; + emit(belongs_to); return } if (policy_template_type == "kspm") { - def identifier = doc["cluster_id"].value; - emit(identifier); + def belongs_to = doc["cluster_id"].value; + emit(belongs_to); return } } - def identifier = doc["cluster_id"].value; - emit(identifier); + def belongs_to = doc["cluster_id"].value; + emit(belongs_to); return } `, diff --git a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_posture_type_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_posture_type_runtime_mapping.ts new file mode 100644 index 0000000000000..2fb06e6ea550c --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_posture_type_runtime_mapping.ts @@ -0,0 +1,34 @@ +/* + * 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 { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; + +/** + * Creates the `safe_posture_type` runtime field with the value of either + * `kspm` or `cspm` based on the value of `rule.benchmark.posture_type` + */ +export const getSafePostureTypeRuntimeMapping = (): MappingRuntimeFields => ({ + safe_posture_type: { + type: 'keyword', + script: { + source: ` + if (!doc.containsKey('rule.benchmark.posture_type')) + { + def safe_posture_type = 'kspm'; + emit(safe_posture_type); + return + } + else + { + def safe_posture_type = doc["rule.benchmark.posture_type"].value; + emit(safe_posture_type); + return + } + `, + }, + }, +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index bc61f75181ef3..d242c7a587dda 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -8,6 +8,8 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { schema } from '@kbn/config-schema'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import { getSafePostureTypeRuntimeMapping } from '../../../common/runtime_mappings/get_safe_posture_type_runtime_mapping'; import type { PosturePolicyTemplate, ComplianceDashboardData } from '../../../common/types'; import { CSPM_POLICY_TEMPLATE, @@ -69,18 +71,20 @@ export const defineGetComplianceDashboardRoute = (router: CspRouter): void => const policyTemplate = request.params.policy_template as PosturePolicyTemplate; + // runtime mappings create the `safe_posture_type` field, which equals to `kspm` or `cspm` based on the value and existence of the `posture_type` field which was introduced at 8.7 + // the `query` is then being passed to our getter functions to filter per posture type even for older findings before 8.7 + const runtimeMappings: MappingRuntimeFields = getSafePostureTypeRuntimeMapping(); const query: QueryDslQueryContainer = { bool: { - // TODO: FIX POSTURE_TYPE - filter: [{ term: { 'rule.benchmark.posture_type': policyTemplate } }], + filter: [{ term: { safe_posture_type: policyTemplate } }], }, }; const [stats, groupedFindingsEvaluation, clustersWithoutTrends, trends] = await Promise.all( [ - getStats(esClient, query, pitId), - getGroupedFindingsEvaluation(esClient, query, pitId), - getClusters(esClient, query, pitId), + getStats(esClient, query, pitId, runtimeMappings), + getGroupedFindingsEvaluation(esClient, query, pitId, runtimeMappings), + getClusters(esClient, query, pitId, runtimeMappings), getTrends(esClient, policyTemplate), ] ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 736ba66a25508..750c035dc9e08 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -13,6 +13,7 @@ import type { AggregationsTopHitsAggregate, SearchHit, } from '@elastic/elasticsearch/lib/api/types'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { CspFinding } from '../../../common/schemas/csp_finding'; import type { Cluster } from '../../../common/types'; import { @@ -40,9 +41,15 @@ interface ClustersQueryResult { export type ClusterWithoutTrend = Omit; -export const getClustersQuery = (query: QueryDslQueryContainer, pitId: string): SearchRequest => ({ +export const getClustersQuery = ( + query: QueryDslQueryContainer, + pitId: string, + runtimeMappings: MappingRuntimeFields +): SearchRequest => ({ size: 0, - runtime_mappings: getIdentifierRuntimeMapping(), + // creates the `asset_identifier` and `safe_posture_type` runtime fields, + // `safe_posture_type` is used by the `query` to filter by posture type for older findings without this field + runtime_mappings: { ...runtimeMappings, ...getIdentifierRuntimeMapping() }, query, aggs: { aggs_by_asset_identifier: { @@ -101,10 +108,11 @@ export const getClustersFromAggs = (clusters: ClusterBucket[]): ClusterWithoutTr export const getClusters = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer, - pitId: string + pitId: string, + runtimeMappings: MappingRuntimeFields ): Promise => { const queryResult = await esClient.search( - getClustersQuery(query, pitId) + getClustersQuery(query, pitId, runtimeMappings) ); const clusters = queryResult.aggregations?.aggs_by_asset_identifier.buckets; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts index f20b43619e914..239801350c7af 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts @@ -11,6 +11,7 @@ import type { QueryDslQueryContainer, SearchRequest, } from '@elastic/elasticsearch/lib/api/types'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { calculatePostureScore } from '../../../common/utils/helpers'; import type { ComplianceDashboardData } from '../../../common/types'; import { KeyDocCount } from './compliance_dashboard'; @@ -62,8 +63,15 @@ export const failedFindingsAggQuery = { }, }; -export const getRisksEsQuery = (query: QueryDslQueryContainer, pitId: string): SearchRequest => ({ +export const getRisksEsQuery = ( + query: QueryDslQueryContainer, + pitId: string, + runtimeMappings: MappingRuntimeFields +): SearchRequest => ({ size: 0, + // creates the `safe_posture_type` runtime fields, + // `safe_posture_type` is used by the `query` to filter by posture type for older findings without this field + runtime_mappings: runtimeMappings, query, aggs: failedFindingsAggQuery, pit: { @@ -90,10 +98,11 @@ export const getFailedFindingsFromAggs = ( export const getGroupedFindingsEvaluation = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer, - pitId: string + pitId: string, + runtimeMappings: MappingRuntimeFields ): Promise => { const resourceTypesQueryResult = await esClient.search( - getRisksEsQuery(query, pitId) + getRisksEsQuery(query, pitId, runtimeMappings) ); const ruleSections = resourceTypesQueryResult.aggregations?.aggs_by_resource_type.buckets; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts index a59d7487488e0..2f0e1c1b17102 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts @@ -7,6 +7,7 @@ import { ElasticsearchClient } from '@kbn/core/server'; import type { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsearch/lib/api/types'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { calculatePostureScore } from '../../../common/utils/helpers'; import type { ComplianceDashboardData } from '../../../common/types'; @@ -41,10 +42,14 @@ const uniqueResourcesCountQuery = { export const getEvaluationsQuery = ( query: QueryDslQueryContainer, - pitId: string + pitId: string, + runtimeMappings: MappingRuntimeFields ): SearchRequest => ({ - query, size: 0, + // creates the `safe_posture_type` runtime fields, + // `safe_posture_type` is used by the `query` to filter by posture type for older findings without this field + runtime_mappings: runtimeMappings, + query, aggs: { ...findingsEvaluationAggsQuery, ...uniqueResourcesCountQuery, @@ -75,10 +80,11 @@ export const getStatsFromFindingsEvaluationsAggs = ( export const getStats = async ( esClient: ElasticsearchClient, query: QueryDslQueryContainer, - pitId: string + pitId: string, + runtimeMappings: MappingRuntimeFields ): Promise => { const evaluationsQueryResult = await esClient.search( - getEvaluationsQuery(query, pitId) + getEvaluationsQuery(query, pitId, runtimeMappings) ); const findingsEvaluations = evaluationsQueryResult.aggregations; diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts index 854ac6150d460..6f21112efcb90 100644 --- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts +++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts @@ -14,6 +14,7 @@ import { import { SearchRequest } from '@kbn/data-plugin/common'; import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/core/server'; +import { getSafePostureTypeRuntimeMapping } from '../../common/runtime_mappings/get_safe_posture_type_runtime_mapping'; import { getIdentifierRuntimeMapping } from '../../common/runtime_mappings/get_identifier_runtime_mapping'; import { FindingsStatsTaskResult, TaskHealthStatus, ScoreByPolicyTemplateBucket } from './types'; import { @@ -108,15 +109,15 @@ export function taskRunner(coreStartServices: CspServerPluginStartServices, logg const getScoreQuery = (): SearchRequest => ({ index: LATEST_FINDINGS_INDEX_DEFAULT_NS, size: 0, - runtime_mappings: getIdentifierRuntimeMapping(), + // creates the safe_posture_type and asset_identifier runtime fields + runtime_mappings: { ...getIdentifierRuntimeMapping(), ...getSafePostureTypeRuntimeMapping() }, query: { match_all: {}, }, aggs: { score_by_policy_template: { terms: { - // TODO: FIX POSTURE_TYPE - field: 'rule.benchmark.posture_type', + field: 'safe_posture_type', }, aggs: { total_findings: {