"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aggregateLatestFindings = void 0; exports.removeFindingsStatsTask = removeFindingsStatsTask; exports.scheduleFindingsStatsTask = scheduleFindingsStatsTask; exports.setupFindingsStatsTask = setupFindingsStatsTask; exports.taskRunner = taskRunner; var _securitysolutionEsUtils = require("@kbn/securitysolution-es-utils"); var _get_safe_vulnerabilities_query_filter = require("../../common/utils/get_safe_vulnerabilities_query_filter"); var _get_safe_posture_type_runtime_mapping = require("../../common/runtime_mappings/get_safe_posture_type_runtime_mapping"); var _get_identifier_runtime_mapping = require("../../common/runtime_mappings/get_identifier_runtime_mapping"); var _constants = require("../../common/constants"); var _task_manager_util = require("../lib/task_manager_util"); var _task_state = require("./task_state"); /* * 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. */ const CSPM_FINDINGS_STATS_TASK_ID = 'cloud_security_posture-findings_stats'; const CSPM_FINDINGS_STATS_TASK_TYPE = 'cloud_security_posture-stats_task'; const CSPM_FINDINGS_STATS_INTERVAL = '5m'; async function scheduleFindingsStatsTask(taskManager, logger) { await (0, _task_manager_util.scheduleTaskSafe)(taskManager, { id: CSPM_FINDINGS_STATS_TASK_ID, taskType: CSPM_FINDINGS_STATS_TASK_TYPE, schedule: { interval: CSPM_FINDINGS_STATS_INTERVAL }, state: _task_state.emptyState, params: {} }, logger); } async function removeFindingsStatsTask(taskManager, logger) { await (0, _task_manager_util.removeTaskSafe)(taskManager, CSPM_FINDINGS_STATS_TASK_ID, logger); } function setupFindingsStatsTask(taskManager, coreStartServices, logger) { try { taskManager.registerTaskDefinitions({ [CSPM_FINDINGS_STATS_TASK_TYPE]: { title: 'Aggregate latest findings index for score calculation', stateSchemaByVersion: _task_state.stateSchemaByVersion, createTaskRunner: taskRunner(coreStartServices, logger) } }); logger.info(`Registered task successfully [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}]`); } catch (errMsg) { const error = (0, _securitysolutionEsUtils.transformError)(errMsg); logger.error(`Task registration failed [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}] ${error.message}`); } } function taskRunner(coreStartServices, logger) { return ({ taskInstance }) => { const state = taskInstance.state; return { async run() { try { logger.info(`Runs task: ${CSPM_FINDINGS_STATS_TASK_TYPE}`); const esClient = (await coreStartServices)[0].elasticsearch.client.asInternalUser; const status = await aggregateLatestFindings(esClient, logger); const updatedState = { runs: state.runs + 1, health_status: status }; return { state: updatedState }; } catch (errMsg) { const error = (0, _securitysolutionEsUtils.transformError)(errMsg); logger.warn(`Error executing alerting health check task: ${error.message}`); const updatedState = { runs: state.runs + 1, health_status: 'error' }; return { state: updatedState }; } } }; }; } const getScoreQuery = () => ({ index: _constants.LATEST_FINDINGS_INDEX_DEFAULT_NS, size: 0, // creates the safe_posture_type and asset_identifier runtime fields runtime_mappings: { ...(0, _get_identifier_runtime_mapping.getIdentifierRuntimeMapping)(), ...(0, _get_safe_posture_type_runtime_mapping.getSafePostureTypeRuntimeMapping)() }, query: { match_all: {} }, aggs: { score_by_policy_template: { terms: { field: 'safe_posture_type' }, aggs: { total_findings: { value_count: { field: 'result.evaluation' } }, passed_findings: { filter: { term: { 'result.evaluation': 'passed' } } }, failed_findings: { filter: { term: { 'result.evaluation': 'failed' } } }, score_by_cluster_id: { terms: { field: 'asset_identifier' }, aggregations: { total_findings: { value_count: { field: 'result.evaluation' } }, passed_findings: { filter: { term: { 'result.evaluation': 'passed' } } }, failed_findings: { filter: { term: { 'result.evaluation': 'failed' } } } } } } } } }); const getVulnStatsTrendQuery = () => ({ index: _constants.LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, size: 0, query: (0, _get_safe_vulnerabilities_query_filter.getSafeVulnerabilitiesQueryFilter)(), aggs: { critical: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.CRITICAL } } }, high: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.HIGH } } }, medium: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.MEDIUM } } }, low: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.LOW } } }, vulnerabilities_stats_by_cloud_account: { terms: { field: 'cloud.account.id' }, aggs: { cloud_account_id: { terms: { field: 'cloud.account.id', size: 1 } }, cloud_account_name: { terms: { field: 'cloud.account.name', size: 1 } }, critical: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.CRITICAL } } }, high: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.HIGH } } }, medium: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.MEDIUM } } }, low: { filter: { term: { 'vulnerability.severity': _constants.VULNERABILITIES_SEVERITY.LOW } } } } } } }); const getFindingsScoresDocIndexingPromises = (esClient, scoresByPolicyTemplatesBuckets) => scoresByPolicyTemplatesBuckets.map(policyTemplateTrend => { // creating score per cluster id objects const clustersStats = Object.fromEntries(policyTemplateTrend.score_by_cluster_id.buckets.map(clusterStats => { const clusterId = clusterStats.key; return [clusterId, { total_findings: clusterStats.total_findings.value, passed_findings: clusterStats.passed_findings.doc_count, failed_findings: clusterStats.failed_findings.doc_count }]; })); // each document contains the policy template and its scores return esClient.index({ index: _constants.BENCHMARK_SCORE_INDEX_DEFAULT_NS, document: { policy_template: policyTemplateTrend.key, passed_findings: policyTemplateTrend.passed_findings.doc_count, failed_findings: policyTemplateTrend.failed_findings.doc_count, total_findings: policyTemplateTrend.total_findings.value, score_by_cluster_id: clustersStats } }); }); const getVulnStatsTrendDocIndexingPromises = (esClient, vulnStatsAggs) => { if (!vulnStatsAggs) return; const scoreByCloudAccount = Object.fromEntries(vulnStatsAggs.vulnerabilities_stats_by_cloud_account.buckets.map(accountScore => { const cloudAccountId = accountScore.key; return [cloudAccountId, { cloudAccountId: accountScore.key, cloudAccountName: accountScore.cloud_account_name.buckets[0].key, critical: accountScore.critical.doc_count, high: accountScore.high.doc_count, medium: accountScore.medium.doc_count, low: accountScore.low.doc_count }]; })); return esClient.index({ index: _constants.BENCHMARK_SCORE_INDEX_DEFAULT_NS, document: { policy_template: _constants.VULN_MGMT_POLICY_TEMPLATE, critical: vulnStatsAggs.critical.doc_count, high: vulnStatsAggs.high.doc_count, medium: vulnStatsAggs.medium.doc_count, low: vulnStatsAggs.low.doc_count, vulnerabilities_stats_by_cloud_account: scoreByCloudAccount } }); }; const aggregateLatestFindings = async (esClient, logger) => { try { var _scoreIndexQueryResul; const startAggTime = performance.now(); const scoreIndexQueryResult = await esClient.search(getScoreQuery()); const vulnStatsTrendIndexQueryResult = await esClient.search(getVulnStatsTrendQuery()); if (!scoreIndexQueryResult.aggregations && !vulnStatsTrendIndexQueryResult.aggregations) { logger.warn(`No data found in latest findings index`); return 'warning'; } const totalAggregationTime = performance.now() - startAggTime; logger.debug(`Executed aggregation query [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}] [Duration: ${Number(totalAggregationTime).toFixed(2)}ms]`); // getting score per policy template buckets const scoresByPolicyTemplatesBuckets = ((_scoreIndexQueryResul = scoreIndexQueryResult.aggregations) === null || _scoreIndexQueryResul === void 0 ? void 0 : _scoreIndexQueryResul.score_by_policy_template.buckets) || []; // iterating over the buckets and return promises which will index a modified document into the scores index const findingsScoresDocIndexingPromises = getFindingsScoresDocIndexingPromises(esClient, scoresByPolicyTemplatesBuckets); const vulnStatsTrendDocIndexingPromises = getVulnStatsTrendDocIndexingPromises(esClient, vulnStatsTrendIndexQueryResult.aggregations); const startIndexTime = performance.now(); // executing indexing commands await Promise.all([...findingsScoresDocIndexingPromises, vulnStatsTrendDocIndexingPromises].filter(Boolean)); const totalIndexTime = Number(performance.now() - startIndexTime).toFixed(2); logger.debug(`Finished saving results [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}] [Duration: ${totalIndexTime}ms]`); const totalTaskTime = Number(performance.now() - startAggTime).toFixed(2); logger.debug(`Finished run ended [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}] [Duration: ${totalTaskTime}ms]`); return 'ok'; } catch (errMsg) { const error = (0, _securitysolutionEsUtils.transformError)(errMsg); logger.error(`Failure during task run [Task: ${CSPM_FINDINGS_STATS_TASK_TYPE}] ${error.message}`); logger.error(errMsg); return 'error'; } }; exports.aggregateLatestFindings = aggregateLatestFindings;