diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.test.ts new file mode 100644 index 000000000000..95495ebea0d9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.test.ts @@ -0,0 +1,15 @@ +/* + * 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 { transformDiffableFieldValues } from './diffable_rule_fields_mappings'; + +describe('transformDiffableFieldValues', () => { + it('transforms rule_schedule into "from" value', () => { + const result = transformDiffableFieldValues('from', { interval: '5m', lookback: '4m' }); + expect(result).toEqual({ type: 'TRANSFORMED_FIELD', value: 'now-540s' }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.ts index 7caa0469eebe..d7e359b0daa2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.ts @@ -15,6 +15,7 @@ import type { } from '../../../../../../common/api/detection_engine'; import { type AllFieldsDiff } from '../../../../../../common/api/detection_engine'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import { calculateFromValue } from '../../../rule_types/utils/utils'; /** * Retrieves and transforms the value for a specific field from a DiffableRule group. @@ -201,7 +202,8 @@ export const transformDiffableFieldValues = ( diffableFieldValue: RuleSchedule | InlineKqlQuery | unknown ): TransformValuesReturnType => { if (fieldName === 'from' && isRuleSchedule(diffableFieldValue)) { - return { type: 'TRANSFORMED_FIELD', value: `now-${diffableFieldValue.lookback}` }; + const from = calculateFromValue(diffableFieldValue.interval, diffableFieldValue.lookback); + return { type: 'TRANSFORMED_FIELD', value: from }; } else if (fieldName === 'to') { return { type: 'TRANSFORMED_FIELD', value: `now` }; } else if (fieldName === 'saved_id' && isInlineQuery(diffableFieldValue)) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/rule_params_modifier.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/rule_params_modifier.ts index 1d633817c7b5..756fbd3998d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/rule_params_modifier.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/rule_params_modifier.ts @@ -5,8 +5,6 @@ * 2.0. */ -import moment from 'moment'; -import { parseInterval } from '@kbn/data-plugin/common/search/aggs/utils/date_interval_utils'; import type { RuleParamsModifierResult } from '@kbn/alerting-plugin/server/rules_client/methods/bulk_edit'; import type { ExperimentalFeatures } from '../../../../../../common'; import type { InvestigationFieldsCombined, RuleAlertType } from '../../../rule_schema'; @@ -17,6 +15,7 @@ import type { } from '../../../../../../common/api/detection_engine/rule_management'; import { BulkActionEditTypeEnum } from '../../../../../../common/api/detection_engine/rule_management'; import { invariant } from '../../../../../../common/utils/invariant'; +import { calculateFromValue } from '../../../rule_types/utils/utils'; export const addItemsToArray = (arr: T[], items: T[]): T[] => Array.from(new Set([...arr, ...items])); @@ -256,10 +255,7 @@ const applyBulkActionEditToRuleParams = ( } // update look-back period in from and meta.from fields case BulkActionEditTypeEnum.set_schedule: { - const interval = parseInterval(action.value.interval) ?? moment.duration(0); - const parsedFrom = parseInterval(action.value.lookback) ?? moment.duration(0); - - const from = parsedFrom.asSeconds() + interval.asSeconds(); + const from = calculateFromValue(action.value.interval, action.value.lookback); ruleParams = { ...ruleParams, @@ -267,7 +263,7 @@ const applyBulkActionEditToRuleParams = ( ...ruleParams.meta, from: action.value.lookback, }, - from: `now-${from}s`, + from, }; break; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts index 0a625ed5f245..3430d2ae903e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.test.ts @@ -48,6 +48,7 @@ import { addToSearchAfterReturn, getUnprocessedExceptionsWarnings, getDisabledActionsWarningText, + calculateFromValue, } from './utils'; import type { BulkResponseErrorAggregation, SearchAfterAndBulkCreateReturnType } from '../types'; import { @@ -586,6 +587,23 @@ describe('utils', () => { }); }); + describe('calculateFromValue', () => { + test('should return formatted `from` value from rule schedule fields', () => { + const from = calculateFromValue('5m', '4m'); + expect(from).toEqual('now-540s'); + }); + + test('should return formatted `from` value from rule schedule fields with no lookback', () => { + const from = calculateFromValue('5m', '0m'); + expect(from).toEqual('now-300s'); + }); + + test('should return formatted `from` value from rule schedule fields with invalid moment fields', () => { + const from = calculateFromValue('5', '5'); + expect(from).toEqual('now-0s'); + }); + }); + describe('getMaxCatchupRatio', () => { test('should return 0 if gap is 0', () => { const catchup = getNumCatchupIntervals({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts index 271a2ce64883..75c8b30dc88d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/utils.ts @@ -568,6 +568,20 @@ export const getCatchupTuples = ({ return catchupTuples; }; +/** + * Takes the rule schedule fields `interval` and `lookback` and uses them to calculate the `from` value for a rule + * + * @param interval string representing the interval on which the rule runs + * @param lookback string representing the rule's additional lookback + * @returns string representing the rule's 'from' property + */ +export const calculateFromValue = (interval: string, lookback: string) => { + const parsedInterval = parseInterval(interval) ?? moment.duration(0); + const parsedFrom = parseInterval(lookback) ?? moment.duration(0); + const duration = parsedFrom.asSeconds() + parsedInterval.asSeconds(); + return `now-${duration}s`; +}; + /** * Given errors from a search query this will return an array of strings derived from the errors. * @param errors The errors to derive the strings from