diff --git a/src/platform/packages/shared/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts b/src/platform/packages/shared/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts index 58e1d8ebd1f46..ef774db2cf8db 100644 --- a/src/platform/packages/shared/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts +++ b/src/platform/packages/shared/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts @@ -212,6 +212,9 @@ const SecurityAlertOptional = rt.partial({ 'kibana.alert.workflow_tags': schemaStringArray, 'kibana.alert.workflow_user': schemaString, 'kibana.version': schemaString, + 'service.asset.criticality': schemaString, + 'service.risk.calculated_level': schemaString, + 'service.risk.calculated_score_norm': schemaNumber, tags: schemaStringArray, 'user.asset.criticality': schemaString, }); diff --git a/x-pack/platform/plugins/shared/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap b/x-pack/platform/plugins/shared/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap index 0513842a6126b..160d41fa2f677 100644 --- a/x-pack/platform/plugins/shared/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap +++ b/x-pack/platform/plugins/shared/alerting/server/integration_tests/__snapshots__/alert_as_data_fields.test.ts.snap @@ -1414,6 +1414,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -2517,6 +2532,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -3620,6 +3650,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -4723,6 +4768,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -5826,6 +5886,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -6935,6 +7010,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -8038,6 +8128,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, @@ -9141,6 +9246,21 @@ Object { "required": false, "type": "version", }, + "service.asset.criticality": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_level": Object { + "array": false, + "required": false, + "type": "keyword", + }, + "service.risk.calculated_score_norm": Object { + "array": false, + "required": false, + "type": "float", + }, "signal.ancestors.depth": Object { "path": "kibana.alert.ancestors.depth", "required": false, diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts index e7066058ab8db..ea4f964fd8163 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.13.0/index.ts @@ -40,7 +40,7 @@ export interface BaseFields8130 extends BaseFields8120 { [ALERT_HOST_CRITICALITY]: string | undefined; [ALERT_USER_CRITICALITY]: string | undefined; /** - * Risk scores fields was added aroung 8.5.0, but the fields were not added to the alert schema + * Risk scores fields was added around 8.5.0, but the fields were not added to the alert schema */ [ALERT_HOST_RISK_SCORE_CALCULATED_LEVEL]: string | undefined; [ALERT_HOST_RISK_SCORE_CALCULATED_SCORE_NORM]: number | undefined; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.18.0/index.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.18.0/index.ts new file mode 100644 index 0000000000000..a98e99289da15 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/8.18.0/index.ts @@ -0,0 +1,62 @@ +/* + * 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 type { AlertWithCommonFields800 } from '@kbn/rule-registry-plugin/common/schemas/8.0.0'; +import type { + Ancestor8160, + BaseFields8160, + EqlBuildingBlockFields8160, + EqlShellFields8160, + NewTermsFields8160, +} from '../8.16.0'; +import type { + ALERT_SERVICE_CRITICALITY, + ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL, + ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM, +} from '../../../../../field_maps/field_names'; + +/* DO NOT MODIFY THIS SCHEMA TO ADD NEW FIELDS. These types represent the alerts that shipped in 8.18.0. +Any changes to these types should be bug fixes so the types more accurately represent the alerts from 8.18.0. +If you are adding new fields for a new release of Kibana, create a new sibling folder to this one +for the version to be released and add the field(s) to the schema in that folder. +Then, update `../index.ts` to import from the new folder that has the latest schemas, add the +new schemas to the union of all alert schemas, and re-export the new schemas as the `*Latest` schemas. +*/ + +export type { Ancestor8160 as Ancestor8180 }; + +export interface BaseFields8180 extends BaseFields8160 { + [ALERT_SERVICE_CRITICALITY]: string | undefined; + [ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL]: string | undefined; + [ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM]: number | undefined; +} + +export interface WrappedFields8180 { + _id: string; + _index: string; + _source: T; +} + +export type GenericAlert8180 = AlertWithCommonFields800; + +export type EqlShellFields8180 = EqlShellFields8160 & BaseFields8180; + +export type EqlBuildingBlockFields8180 = EqlBuildingBlockFields8160 & BaseFields8180; + +export type NewTermsFields8180 = NewTermsFields8160 & BaseFields8180; + +export type NewTermsAlert8180 = NewTermsFields8160 & BaseFields8180; + +export type EqlBuildingBlockAlert8180 = AlertWithCommonFields800; + +export type EqlShellAlert8180 = AlertWithCommonFields800; + +export type DetectionAlert8180 = + | GenericAlert8180 + | EqlShellAlert8180 + | EqlBuildingBlockAlert8180 + | NewTermsAlert8180; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts index 816c9a4c81897..6758b88bbb6ae 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/model/alerts/index.ts @@ -15,15 +15,16 @@ import type { DetectionAlert890 } from './8.9.0'; import type { DetectionAlert8120 } from './8.12.0'; import type { DetectionAlert8130 } from './8.13.0'; +import type { DetectionAlert8160 } from './8.16.0'; import type { - Ancestor8160, - BaseFields8160, - DetectionAlert8160, - EqlBuildingBlockFields8160, - EqlShellFields8160, - NewTermsFields8160, - WrappedFields8160, -} from './8.16.0'; + Ancestor8180, + BaseFields8180, + DetectionAlert8180, + EqlBuildingBlockFields8180, + EqlShellFields8180, + NewTermsFields8180, + WrappedFields8180, +} from './8.18.0'; // When new Alert schemas are created for new Kibana versions, add the DetectionAlert type from the new version // here, e.g. `export type DetectionAlert = DetectionAlert800 | DetectionAlert820` if a new schema is created in 8.2.0 @@ -36,14 +37,15 @@ export type DetectionAlert = | DetectionAlert890 | DetectionAlert8120 | DetectionAlert8130 - | DetectionAlert8160; + | DetectionAlert8160 + | DetectionAlert8180; export type { - Ancestor8160 as AncestorLatest, - BaseFields8160 as BaseFieldsLatest, - DetectionAlert8160 as DetectionAlertLatest, - WrappedFields8160 as WrappedFieldsLatest, - EqlBuildingBlockFields8160 as EqlBuildingBlockFieldsLatest, - EqlShellFields8160 as EqlShellFieldsLatest, - NewTermsFields8160 as NewTermsFieldsLatest, + Ancestor8180 as AncestorLatest, + BaseFields8180 as BaseFieldsLatest, + DetectionAlert8180 as DetectionAlertLatest, + WrappedFields8180 as WrappedFieldsLatest, + EqlBuildingBlockFields8180 as EqlBuildingBlockFieldsLatest, + EqlShellFields8180 as EqlShellFieldsLatest, + NewTermsFields8180 as NewTermsFieldsLatest, }; diff --git a/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/alerts.ts b/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/alerts.ts new file mode 100644 index 0000000000000..7c305f8506282 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/alerts.ts @@ -0,0 +1,43 @@ +/* + * 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 { alertsFieldMap8160 } from '../8.16.0'; +import { + ALERT_SERVICE_CRITICALITY, + ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL, + ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM, +} from '../field_names'; + +export const alertsFieldMap8180 = { + ...alertsFieldMap8160, + /** + * Stores the criticality level for the service, as determined by analysts, in relation to the alert. + * The Criticality level is copied from the asset criticality index. + */ + [ALERT_SERVICE_CRITICALITY]: { + type: 'keyword', + array: false, + required: false, + }, + + /** + * Stores the risk score level and score_norm level for the service, as determined by the Risk Engine, in relation to the alert. + * The Risk score is copied from the risk score index. + */ + [ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL]: { + type: 'keyword', + array: false, + required: false, + }, + [ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM]: { + type: 'float', + array: false, + required: false, + }, +} as const; + +export type AlertsFieldMap8180 = typeof alertsFieldMap8180; diff --git a/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/index.ts b/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/index.ts new file mode 100644 index 0000000000000..9b07c180f513f --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/common/field_maps/8.18.0/index.ts @@ -0,0 +1,11 @@ +/* + * 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 type { AlertsFieldMap8180 } from './alerts'; +import { alertsFieldMap8180 } from './alerts'; +export type { AlertsFieldMap8180 }; +export { alertsFieldMap8180 }; diff --git a/x-pack/solutions/security/plugins/security_solution/common/field_maps/field_names.ts b/x-pack/solutions/security/plugins/security_solution/common/field_maps/field_names.ts index b8ef2e61fb390..fa3407367b247 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/field_maps/field_names.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/field_maps/field_names.ts @@ -28,12 +28,16 @@ export const LEGACY_ALERT_USER_CRITICALITY = `${ALERT_NAMESPACE}.user.criticalit export const ALERT_HOST_CRITICALITY = `host.asset.criticality` as const; export const ALERT_USER_CRITICALITY = `user.asset.criticality` as const; +export const ALERT_SERVICE_CRITICALITY = `service.asset.criticality` as const; export const ALERT_HOST_RISK_SCORE_CALCULATED_LEVEL = `host.risk.calculated_level` as const; export const ALERT_HOST_RISK_SCORE_CALCULATED_SCORE_NORM = `host.risk.calculated_score_norm` as const; export const ALERT_USER_RISK_SCORE_CALCULATED_LEVEL = `user.risk.calculated_level` as const; export const ALERT_USER_RISK_SCORE_CALCULATED_SCORE_NORM = `user.risk.calculated_score_norm` as const; +export const ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL = `service.risk.calculated_level` as const; +export const ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM = + `service.risk.calculated_score_norm` as const; export const ALERT_ORIGINAL_EVENT = `${ALERT_NAMESPACE}.original_event` as const; export const ALERT_ORIGINAL_EVENT_ACTION = `${ALERT_ORIGINAL_EVENT}.action` as const; diff --git a/x-pack/solutions/security/plugins/security_solution/common/field_maps/index.ts b/x-pack/solutions/security/plugins/security_solution/common/field_maps/index.ts index 5080ff2660533..f992f78eedb76 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/field_maps/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/field_maps/index.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import type { AlertsFieldMap8160 } from './8.16.0'; -import { alertsFieldMap8160 } from './8.16.0'; +import type { AlertsFieldMap8180 } from './8.18.0'; +import { alertsFieldMap8180 } from './8.18.0'; import type { RulesFieldMap } from './8.0.0/rules'; import { rulesFieldMap } from './8.0.0/rules'; -export type { AlertsFieldMap8160 as AlertsFieldMap, RulesFieldMap }; -export { alertsFieldMap8160 as alertsFieldMap, rulesFieldMap }; +export type { AlertsFieldMap8180 as AlertsFieldMap, RulesFieldMap }; +export { alertsFieldMap8180 as alertsFieldMap, rulesFieldMap }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx index 1d722391ba12b..8643504bf9f6f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/default_config.test.tsx @@ -79,6 +79,11 @@ const platinumBaseColumns = [ id: 'user.risk.calculated_level', displayAsText: 'User Risk Level', }, + { + columnHeaderType: 'not-filtered', + id: 'service.risk.calculated_level', + displayAsText: 'Service Risk Level', + }, { columnHeaderType: 'not-filtered', id: 'host.asset.criticality', @@ -89,6 +94,11 @@ const platinumBaseColumns = [ id: 'user.asset.criticality', displayAsText: 'User Criticality', }, + { + columnHeaderType: 'not-filtered', + id: 'service.asset.criticality', + displayAsText: 'Service Criticality', + }, { columnHeaderType: 'not-filtered', id: 'process.name' }, { columnHeaderType: 'not-filtered', id: 'file.name' }, { columnHeaderType: 'not-filtered', id: 'source.ip' }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/translations.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/translations.ts index b90484e4a795f..0467959a9f7f2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/translations.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_table/translations.ts @@ -140,6 +140,13 @@ export const ALERTS_HEADERS_HOST_CRITICALITY = i18n.translate( } ); +export const ALERTS_HEADERS_SERVICE_RISK_LEVEL = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.serviceRiskLevel', + { + defaultMessage: 'Service Risk Level', + } +); + export const ALERTS_HEADERS_USER_CRITICALITY = i18n.translate( 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.userCriticality', { @@ -147,6 +154,13 @@ export const ALERTS_HEADERS_USER_CRITICALITY = i18n.translate( } ); +export const ALERTS_HEADERS_SERVICE_CRITICALITY = i18n.translate( + 'xpack.securitySolution.eventsViewer.alerts.defaultHeaders.serviceCriticality', + { + defaultMessage: 'Service Criticality', + } +); + export const ACTION_INVESTIGATE_IN_TIMELINE = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.actions.investigateInTimelineTitle', { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts index 6ca0a67244179..14a5f31b63685 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/configurations/security_solution_detections/columns.ts @@ -11,6 +11,8 @@ import { ALERT_USER_CRITICALITY, ALERT_HOST_RISK_SCORE_CALCULATED_LEVEL, ALERT_USER_RISK_SCORE_CALCULATED_LEVEL, + ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL, + ALERT_SERVICE_CRITICALITY, } from '../../../../common/field_maps/field_names'; import type { LicenseService } from '../../../../common/license'; import type { ColumnHeaderOptions } from '../../../../common/types'; @@ -46,6 +48,12 @@ export const userRiskLevelColumn: ColumnHeaderOptions = { displayAsText: i18n.ALERTS_HEADERS_USER_RISK_LEVEL, }; +export const serviceRiskLevelColumn: ColumnHeaderOptions = { + columnHeaderType: defaultColumnHeaderType, + id: ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL, + displayAsText: i18n.ALERTS_HEADERS_SERVICE_RISK_LEVEL, +}; + const getBaseColumns = ( license?: LicenseService ): Array< @@ -81,6 +89,7 @@ const getBaseColumns = ( }, isPlatinumPlus ? hostRiskLevelColumn : null, isPlatinumPlus ? userRiskLevelColumn : null, + isPlatinumPlus ? serviceRiskLevelColumn : null, isPlatinumPlus ? { columnHeaderType: defaultColumnHeaderType, @@ -95,6 +104,13 @@ const getBaseColumns = ( displayAsText: i18n.ALERTS_HEADERS_USER_CRITICALITY, } : null, + isPlatinumPlus + ? { + columnHeaderType: defaultColumnHeaderType, + id: ALERT_SERVICE_CRITICALITY, + displayAsText: i18n.ALERTS_HEADERS_SERVICE_CRITICALITY, + } + : null, { columnHeaderType: defaultColumnHeaderType, id: 'process.name', diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index d8d9e7ce023fe..379bf6c544fe3 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -17,6 +17,7 @@ import { EntityTypeToIdentifierField } from '../../../../../../common/entity_ana import { getAgentTypeForAgentIdField } from '../../../../../common/lib/endpoint/utils/get_agent_type_for_agent_id_field'; import { ALERT_HOST_CRITICALITY, + ALERT_SERVICE_CRITICALITY, ALERT_USER_CRITICALITY, } from '../../../../../../common/field_maps/field_names'; import { AgentStatus } from '../../../../../common/components/endpoint/agents/agent_status'; @@ -283,7 +284,11 @@ const FormattedFieldValueComponent: React.FC<{ iconSide={isButton ? 'right' : undefined} /> ); - } else if (fieldName === ALERT_HOST_CRITICALITY || fieldName === ALERT_USER_CRITICALITY) { + } else if ( + fieldName === ALERT_HOST_CRITICALITY || + fieldName === ALERT_USER_CRITICALITY || + fieldName === ALERT_SERVICE_CRITICALITY + ) { return ( [ const createEnrichmentFactoryFunction = ( - alertField: typeof ALERT_HOST_CRITICALITY | typeof ALERT_USER_CRITICALITY + alertField: + | typeof ALERT_HOST_CRITICALITY + | typeof ALERT_USER_CRITICALITY + | typeof ALERT_SERVICE_CRITICALITY ): CreateEnrichmentFunction => (enrichment) => (event) => { @@ -88,3 +92,25 @@ export const createUserAssetCriticalityEnrichments: CreateCriticalityEnrichment createEnrichmentFunction: createEnrichmentFactoryFunction(ALERT_USER_CRITICALITY), }); }; + +export const createServiceAssetCriticalityEnrichments: CreateCriticalityEnrichment = async ({ + services, + logger, + events, + spaceId, +}) => { + return createSingleFieldMatchEnrichment({ + name: 'Service Asset Criticality', + index: [getAssetCriticalityIndex(spaceId)], + services, + logger, + events, + mappingField: { + eventField: 'service.name', + enrichmentField: 'id_value', + }, + enrichmentResponseFields, + extraFilters: getExtraFiltersForEnrichment('service.name'), + createEnrichmentFunction: createEnrichmentFactoryFunction(ALERT_SERVICE_CRITICALITY), + }); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/service_risk.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/service_risk.ts new file mode 100644 index 0000000000000..52d29ad0b6e81 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/enrichment_by_type/service_risk.ts @@ -0,0 +1,56 @@ +/* + * 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 { set } from '@kbn/safer-lodash-set'; +import { cloneDeep } from 'lodash'; +import { + ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL, + ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM, +} from '../../../../../../../common/field_maps/field_names'; +import { getRiskIndex } from '../../../../../../../common/search_strategy/security_solution/risk_score/common'; +import { RiskScoreFields } from '../../../../../../../common/search_strategy/security_solution/risk_score/all'; +import { createSingleFieldMatchEnrichment } from '../create_single_field_match_enrichment'; +import type { CreateRiskEnrichment } from '../types'; +import { getFieldValue } from '../utils/events'; + +export const createServiceRiskEnrichments: CreateRiskEnrichment = async ({ + services, + logger, + events, + spaceId, +}) => { + return createSingleFieldMatchEnrichment({ + name: 'Service Risk', + index: [getRiskIndex(spaceId, true)], + services, + logger, + events, + mappingField: { + eventField: 'service.name', + enrichmentField: RiskScoreFields.serviceName, + }, + enrichmentResponseFields: [ + RiskScoreFields.serviceName, + RiskScoreFields.serviceRisk, + RiskScoreFields.serviceRiskScore, + ], + createEnrichmentFunction: (enrichment) => (event) => { + const riskLevel = getFieldValue(enrichment, RiskScoreFields.serviceRisk); + const riskScore = getFieldValue(enrichment, RiskScoreFields.serviceRiskScore); + if (!riskLevel && !riskScore) { + return event; + } + const newEvent = cloneDeep(event); + if (riskLevel) { + set(newEvent, `_source.${ALERT_SERVICE_RISK_SCORE_CALCULATED_LEVEL}`, riskLevel); + } + if (riskScore) { + set(newEvent, `_source.${ALERT_SERVICE_RISK_SCORE_CALCULATED_SCORE_NORM}`, riskScore); + } + return newEvent; + }, + }); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts index 415128d1d9f0d..20d5897c1d7bb 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.test.ts @@ -60,6 +60,23 @@ const userEnrichmentResponse = [ }, ]; +const serviceEnrichmentResponse = [ + { + fields: { + 'service.name': ['service name 1'], + 'service.risk.calculated_level': ['Moderate'], + 'service.risk.calculated_score_norm': [50], + }, + }, + { + fields: { + 'service.name': ['service name 2'], + 'service.risk.calculated_level': ['Critical'], + 'service.risk.calculated_score_norm': [90], + }, + }, +]; + const assetCriticalityUserResponse = [ { fields: { @@ -84,6 +101,15 @@ const assetCriticalityHostResponse = [ }, ]; +const assetCriticalityServiceResponse = [ + { + fields: { + id_value: ['service name 1'], + criticality_level: ['high'], + }, + }, +]; + describe('enrichEvents', () => { let ruleExecutionLogger: ReturnType; let alertServices: RuleExecutorServicesMock; @@ -132,7 +158,8 @@ describe('enrichEvents', () => { it('return enriched events with risk score', async () => { mockSearchEnrichments .mockReturnValueOnce(hostEnrichmentResponse) - .mockReturnValueOnce(userEnrichmentResponse); + .mockReturnValueOnce(userEnrichmentResponse) + .mockReturnValueOnce(serviceEnrichmentResponse); mockIsIndexExist.mockImplementation(() => true); const enrichedEvents = await enrichEvents({ @@ -142,8 +169,9 @@ describe('enrichEvents', () => { createAlert('1', { ...createEntity('host', 'host name 1'), ...createEntity('user', 'user name 1'), + ...createEntity('service', 'service name 1'), }), - createAlert('2', createEntity('user', 'user name 2')), + createAlert('2', createEntity('service', 'service name 2')), ], spaceId: 'default', }); @@ -164,10 +192,17 @@ describe('enrichEvents', () => { calculated_score_norm: 50, }, }, + service: { + name: 'service name 1', + risk: { + calculated_level: 'Moderate', + calculated_score_norm: 50, + }, + }, }), createAlert('2', { - user: { - name: 'user name 2', + service: { + name: 'service name 2', risk: { calculated_level: 'Critical', calculated_score_norm: 90, @@ -180,12 +215,14 @@ describe('enrichEvents', () => { it('return enriched events with asset criticality', async () => { mockSearchEnrichments .mockReturnValueOnce(assetCriticalityUserResponse) - .mockReturnValueOnce(assetCriticalityHostResponse); + .mockReturnValueOnce(assetCriticalityHostResponse) + .mockReturnValueOnce(assetCriticalityServiceResponse); // disable risk score enrichments mockIsIndexExist.mockImplementationOnce(() => false); mockIsIndexExist.mockImplementationOnce(() => false); mockIsIndexExist.mockImplementationOnce(() => false); + mockIsIndexExist.mockImplementationOnce(() => false); // enable for asset criticality mockIsIndexExist.mockImplementation(() => true); @@ -196,6 +233,7 @@ describe('enrichEvents', () => { createAlert('1', { ...createEntity('host', 'host name 1'), ...createEntity('user', 'user name 1'), + ...createEntity('service', 'service name 1'), }), createAlert('2', createEntity('host', 'user name 1')), ], @@ -207,9 +245,10 @@ describe('enrichEvents', () => { createAlert('1', { ...createEntity('user', 'user name 1'), ...createEntity('host', 'host name 1'), - + ...createEntity('service', 'service name 1'), 'host.asset.criticality': 'low', 'user.asset.criticality': 'important', + 'service.asset.criticality': 'high', }), createAlert('2', { ...createEntity('host', 'user name 1'), @@ -224,7 +263,6 @@ describe('enrichEvents', () => { }) .mockImplementationOnce(() => userEnrichmentResponse); mockIsIndexExist.mockImplementation(() => true); - mockIsIndexExist.mockImplementation(() => true); const enrichedEvents = await enrichEvents({ logger: ruleExecutionLogger, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts index 7a85a845bb7a9..316fcf3bf234f 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/rule_types/utils/enrichments/index.ts @@ -5,13 +5,16 @@ * 2.0. */ -import { getHostRiskIndex, getUserRiskIndex } from '../../../../../../common/search_strategy'; +import { + getHostRiskIndex, + getRiskIndex, + getUserRiskIndex, +} from '../../../../../../common/search_strategy'; import { createHostRiskEnrichments } from './enrichment_by_type/host_risk'; - import { createUserRiskEnrichments } from './enrichment_by_type/user_risk'; - import { createHostAssetCriticalityEnrichments, + createServiceAssetCriticalityEnrichments, createUserAssetCriticalityEnrichments, } from './enrichment_by_type/asset_criticality'; import { getAssetCriticalityIndex } from '../../../../../../common/entity_analytics/asset_criticality'; @@ -22,6 +25,7 @@ import type { } from './types'; import { applyEnrichmentsToEvents } from './utils/transforms'; import { isIndexExist } from './utils/is_index_exist'; +import { createServiceRiskEnrichments } from './enrichment_by_type/service_risk'; export const enrichEvents: EnrichEventsFunction = async ({ services, @@ -44,16 +48,21 @@ export const enrichEvents: EnrichEventsFunction = async ({ }); } - const [isHostRiskScoreIndexExist, isUserRiskScoreIndexExist] = await Promise.all([ - isIndexExist({ - services, - index: getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled), - }), - isIndexExist({ - services, - index: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled), - }), - ]); + const [isHostRiskScoreIndexExist, isUserRiskScoreIndexExist, isServiceRiskScoreIndexExist] = + await Promise.all([ + isIndexExist({ + services, + index: getHostRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled), + }), + isIndexExist({ + services, + index: getUserRiskIndex(spaceId, true, isNewRiskScoreModuleInstalled), + }), + isIndexExist({ + services, + index: getRiskIndex(spaceId, true), + }), + ]); if (isHostRiskScoreIndexExist) { enrichments.push( @@ -66,7 +75,6 @@ export const enrichEvents: EnrichEventsFunction = async ({ }) ); } - if (isUserRiskScoreIndexExist) { enrichments.push( createUserRiskEnrichments({ @@ -79,6 +87,18 @@ export const enrichEvents: EnrichEventsFunction = async ({ ); } + if (isServiceRiskScoreIndexExist) { + enrichments.push( + createServiceRiskEnrichments({ + services, + logger, + events, + spaceId, + isNewRiskScoreModuleInstalled, + }) + ); + } + const assetCriticalityIndexExist = await isIndexExist({ services, index: getAssetCriticalityIndex(spaceId), @@ -100,6 +120,14 @@ export const enrichEvents: EnrichEventsFunction = async ({ spaceId, }) ); + enrichments.push( + createServiceAssetCriticalityEnrichments({ + services, + logger, + events, + spaceId, + }) + ); } const allEnrichmentsResults = await Promise.allSettled(enrichments);