From 7d1ab758a132d4a0f53fd5f1a65a5b621ba19760 Mon Sep 17 00:00:00 2001 From: "api-clients-generation-pipeline[bot]" <54105614+api-clients-generation-pipeline[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 09:31:23 +0000 Subject: [PATCH] Add baselineUserLocationsDuration to Impossible Travel rule options (#3361) Co-authored-by: ci.datadog-api-spec --- .generator/schemas/v2/openapi.yaml | 13 ++++++ ...cationsDuration-returns-OK-response.frozen | 1 + ...rLocationsDuration-returns-OK-response.yml | 41 +++++++++++++++++ ...CreateSecurityMonitoringRule_3243059428.rb | 43 +++++++++++++++++ features/v2/security_monitoring.feature | 13 ++++++ ...nitoring_rule_impossible_travel_options.rb | 46 +++++++++++++++++-- 6 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.frozen create mode 100644 cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.yml create mode 100644 examples/v2/security-monitoring/CreateSecurityMonitoringRule_3243059428.rb diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 27ad049e17c7..25ad70c2815c 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -70685,11 +70685,20 @@ components: properties: baselineUserLocations: $ref: "#/components/schemas/SecurityMonitoringRuleImpossibleTravelOptionsBaselineUserLocations" + baselineUserLocationsDuration: + $ref: "#/components/schemas/SecurityMonitoringRuleImpossibleTravelOptionsBaselineUserLocationsDuration" type: object SecurityMonitoringRuleImpossibleTravelOptionsBaselineUserLocations: description: "If true, signals are suppressed for the first 24 hours. In that time, Datadog learns the user's regular\naccess locations. This can be helpful to reduce noise and infer VPN usage or credentialed API access." example: true type: boolean + SecurityMonitoringRuleImpossibleTravelOptionsBaselineUserLocationsDuration: + description: The duration in days during which Datadog learns the user's regular access locations. After this period, signals are generated for accesses from unknown locations. + format: int32 + maximum: 30 + minimum: 1 + nullable: true + type: integer SecurityMonitoringRuleInstantaneousBaseline: description: When set to true, Datadog uses previous values that fall within the defined learning window to construct the baseline, enabling the system to establish an accurate baseline more rapidly rather than relying solely on gradual learning over time. example: false @@ -143305,6 +143314,7 @@ paths: hardcodedEvaluatorType: log4shell impossibleTravelOptions: baselineUserLocations: true + baselineUserLocationsDuration: 7 newValueOptions: instantaneousBaseline: false learningMethod: duration @@ -144061,6 +144071,7 @@ paths: hardcodedEvaluatorType: log4shell impossibleTravelOptions: baselineUserLocations: true + baselineUserLocationsDuration: 7 keepAlive: 3600 maxSignalDuration: 86400 newValueOptions: @@ -144162,6 +144173,7 @@ paths: hardcodedEvaluatorType: log4shell impossibleTravelOptions: baselineUserLocations: true + baselineUserLocationsDuration: 7 keepAlive: 0 maxSignalDuration: 0 newValueOptions: @@ -144275,6 +144287,7 @@ paths: hardcodedEvaluatorType: log4shell impossibleTravelOptions: baselineUserLocations: true + baselineUserLocationsDuration: 7 keepAlive: 1800 maxSignalDuration: 1800 newValueOptions: diff --git a/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.frozen b/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.frozen new file mode 100644 index 000000000000..55a8fd17a925 --- /dev/null +++ b/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.frozen @@ -0,0 +1 @@ +2026-05-20T15:12:27.397Z \ No newline at end of file diff --git a/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.yml b/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.yml new file mode 100644 index 000000000000..9cfb739d7115 --- /dev/null +++ b/cassettes/features/v2/security_monitoring/Create-a-detection-rule-with-type-impossible-travel-and-baselineUserLocationsDuration-returns-OK-response.yml @@ -0,0 +1,41 @@ +http_interactions: +- recorded_at: Wed, 20 May 2026 15:12:27 GMT + request: + body: + encoding: UTF-8 + string: '{"cases":[{"name":"","notifications":[],"status":"info"}],"filters":[],"hasExtendedTitle":true,"isEnabled":true,"message":"test","name":"Test-Create_a_detection_rule_with_type_impossible_travel_and_baselineUserLocationsDuration_returns_OK_res-1779289947","options":{"detectionMethod":"impossible_travel","evaluationWindow":900,"impossibleTravelOptions":{"baselineUserLocations":true,"baselineUserLocationsDuration":7},"keepAlive":3600,"maxSignalDuration":86400},"queries":[{"aggregation":"geo_data","distinctFields":[],"groupByFields":["@usr.id"],"metric":"@network.client.geoip","query":"*"}],"tags":[],"type":"log_detection"}' + headers: + Accept: + - application/json + Content-Type: + - application/json + method: POST + uri: https://api.datadoghq.com/api/v2/security_monitoring/rules + response: + body: + encoding: UTF-8 + string: '{"name":"Test-Create_a_detection_rule_with_type_impossible_travel_and_baselineUserLocationsDuration_returns_OK_res-1779289947","createdAt":1779289949181,"isDefault":false,"isPartner":false,"isEnabled":true,"isBeta":false,"isDeleted":false,"isDeprecated":false,"queries":[{"query":"*","groupByFields":["@usr.id"],"hasOptionalGroupByFields":false,"distinctFields":[],"metric":"@network.client.geoip","metrics":["@network.client.geoip"],"aggregation":"geo_data","name":"","dataSource":"logs"}],"options":{"evaluationWindow":900,"detectionMethod":"impossible_travel","maxSignalDuration":86400,"keepAlive":3600,"impossibleTravelOptions":{"baselineUserLocations":true,"baselineUserLocationsDuration":7,"detectIpTransition":false}},"cases":[{"name":"","status":"info","notifications":[]}],"message":"test","tags":[],"hasExtendedTitle":true,"type":"log_detection","filters":[],"version":1,"id":"v2k-viu-svz","blocking":false,"metadata":{"entities":null,"sources":null},"creationAuthorId":2320499,"creator":{"handle":"9919ec9b-ebc7-49ee-8dc8-03626e717cca","name":"CI + Account"},"updater":{"handle":"","name":""}}' + headers: + Content-Type: + - application/json + status: + code: 200 + message: OK +- recorded_at: Wed, 20 May 2026 15:12:27 GMT + request: + body: null + headers: + Accept: + - '*/*' + method: DELETE + uri: https://api.datadoghq.com/api/v2/security_monitoring/rules/v2k-viu-svz + response: + body: + encoding: UTF-8 + string: '' + headers: {} + status: + code: 204 + message: No Content +recorded_with: VCR 6.0.0 diff --git a/examples/v2/security-monitoring/CreateSecurityMonitoringRule_3243059428.rb b/examples/v2/security-monitoring/CreateSecurityMonitoringRule_3243059428.rb new file mode 100644 index 000000000000..36a913e57765 --- /dev/null +++ b/examples/v2/security-monitoring/CreateSecurityMonitoringRule_3243059428.rb @@ -0,0 +1,43 @@ +# Create a detection rule with type 'impossible_travel' and baselineUserLocationsDuration returns "OK" response + +require "datadog_api_client" +api_instance = DatadogAPIClient::V2::SecurityMonitoringAPI.new + +body = DatadogAPIClient::V2::SecurityMonitoringStandardRuleCreatePayload.new({ + queries: [ + DatadogAPIClient::V2::SecurityMonitoringStandardRuleQuery.new({ + aggregation: DatadogAPIClient::V2::SecurityMonitoringRuleQueryAggregation::GEO_DATA, + group_by_fields: [ + "@usr.id", + ], + distinct_fields: [], + metric: "@network.client.geoip", + query: "*", + }), + ], + cases: [ + DatadogAPIClient::V2::SecurityMonitoringRuleCaseCreate.new({ + name: "", + status: DatadogAPIClient::V2::SecurityMonitoringRuleSeverity::INFO, + notifications: [], + }), + ], + has_extended_title: true, + message: "test", + is_enabled: true, + options: DatadogAPIClient::V2::SecurityMonitoringRuleOptions.new({ + max_signal_duration: DatadogAPIClient::V2::SecurityMonitoringRuleMaxSignalDuration::ONE_DAY, + evaluation_window: DatadogAPIClient::V2::SecurityMonitoringRuleEvaluationWindow::FIFTEEN_MINUTES, + keep_alive: DatadogAPIClient::V2::SecurityMonitoringRuleKeepAlive::ONE_HOUR, + detection_method: DatadogAPIClient::V2::SecurityMonitoringRuleDetectionMethod::IMPOSSIBLE_TRAVEL, + impossible_travel_options: DatadogAPIClient::V2::SecurityMonitoringRuleImpossibleTravelOptions.new({ + baseline_user_locations: true, + baseline_user_locations_duration: 7, + }), + }), + name: "Example-Security-Monitoring", + type: DatadogAPIClient::V2::SecurityMonitoringRuleTypeCreate::LOG_DETECTION, + tags: [], + filters: [], +}) +p api_instance.create_security_monitoring_rule(body) diff --git a/features/v2/security_monitoring.feature b/features/v2/security_monitoring.feature index 95306c995407..ff9964a900d9 100644 --- a/features/v2/security_monitoring.feature +++ b/features/v2/security_monitoring.feature @@ -559,6 +559,19 @@ Feature: Security Monitoring And the response "type" is equal to "application_security" And the response "message" is equal to "Test rule" + @skip-validation @team:DataDog/k9-cloud-siem + Scenario: Create a detection rule with type 'impossible_travel' and baselineUserLocationsDuration returns "OK" response + Given new "CreateSecurityMonitoringRule" request + And body with value {"queries":[{"aggregation":"geo_data","groupByFields":["@usr.id"],"distinctFields":[],"metric":"@network.client.geoip","query":"*"}],"cases":[{"name":"","status":"info","notifications":[]}],"hasExtendedTitle":true,"message":"test","isEnabled":true,"options":{"maxSignalDuration":86400,"evaluationWindow":900,"keepAlive":3600,"detectionMethod":"impossible_travel","impossibleTravelOptions":{"baselineUserLocations":true,"baselineUserLocationsDuration":7}},"name":"{{ unique }}","type":"log_detection","tags":[],"filters":[]} + When the request is sent + Then the response status is 200 OK + And the response "name" is equal to "{{ unique }}" + And the response "type" is equal to "log_detection" + And the response "message" is equal to "test" + And the response "options.detectionMethod" is equal to "impossible_travel" + And the response "options.impossibleTravelOptions.baselineUserLocations" is equal to true + And the response "options.impossibleTravelOptions.baselineUserLocationsDuration" is equal to 7 + @skip-validation @team:DataDog/k9-cloud-siem Scenario: Create a detection rule with type 'impossible_travel' returns "OK" response Given new "CreateSecurityMonitoringRule" request diff --git a/lib/datadog_api_client/v2/models/security_monitoring_rule_impossible_travel_options.rb b/lib/datadog_api_client/v2/models/security_monitoring_rule_impossible_travel_options.rb index 66467b7402f1..6cdbff73cc3f 100644 --- a/lib/datadog_api_client/v2/models/security_monitoring_rule_impossible_travel_options.rb +++ b/lib/datadog_api_client/v2/models/security_monitoring_rule_impossible_travel_options.rb @@ -25,13 +25,17 @@ class SecurityMonitoringRuleImpossibleTravelOptions # access locations. This can be helpful to reduce noise and infer VPN usage or credentialed API access. attr_accessor :baseline_user_locations + # The duration in days during which Datadog learns the user's regular access locations. After this period, signals are generated for accesses from unknown locations. + attr_reader :baseline_user_locations_duration + attr_accessor :additional_properties # Attribute mapping from ruby-style variable name to JSON key. # @!visibility private def self.attribute_map { - :'baseline_user_locations' => :'baselineUserLocations' + :'baseline_user_locations' => :'baselineUserLocations', + :'baseline_user_locations_duration' => :'baselineUserLocationsDuration' } end @@ -39,10 +43,19 @@ def self.attribute_map # @!visibility private def self.openapi_types { - :'baseline_user_locations' => :'Boolean' + :'baseline_user_locations' => :'Boolean', + :'baseline_user_locations_duration' => :'Integer' } end + # List of attributes with nullable: true + # @!visibility private + def self.openapi_nullable + Set.new([ + :'baseline_user_locations_duration', + ]) + end + # Initializes the object # @param attributes [Hash] Model attributes in the form of hash # @!visibility private @@ -64,6 +77,32 @@ def initialize(attributes = {}) if attributes.key?(:'baseline_user_locations') self.baseline_user_locations = attributes[:'baseline_user_locations'] end + + if attributes.key?(:'baseline_user_locations_duration') + self.baseline_user_locations_duration = attributes[:'baseline_user_locations_duration'] + end + end + + # Check to see if the all the properties in the model are valid + # @return true if the model is valid + # @!visibility private + def valid? + return false if !@baseline_user_locations_duration.nil? && @baseline_user_locations_duration > 30 + return false if !@baseline_user_locations_duration.nil? && @baseline_user_locations_duration < 1 + true + end + + # Custom attribute writer method with validation + # @param baseline_user_locations_duration [Object] Object to be assigned + # @!visibility private + def baseline_user_locations_duration=(baseline_user_locations_duration) + if !baseline_user_locations_duration.nil? && baseline_user_locations_duration > 30 + fail ArgumentError, 'invalid value for "baseline_user_locations_duration", must be smaller than or equal to 30.' + end + if !baseline_user_locations_duration.nil? && baseline_user_locations_duration < 1 + fail ArgumentError, 'invalid value for "baseline_user_locations_duration", must be greater than or equal to 1.' + end + @baseline_user_locations_duration = baseline_user_locations_duration end # Returns the object in the form of hash, with additionalProperties support. @@ -93,6 +132,7 @@ def ==(o) return true if self.equal?(o) self.class == o.class && baseline_user_locations == o.baseline_user_locations && + baseline_user_locations_duration == o.baseline_user_locations_duration && additional_properties == o.additional_properties end @@ -100,7 +140,7 @@ def ==(o) # @return [Integer] Hash code # @!visibility private def hash - [baseline_user_locations, additional_properties].hash + [baseline_user_locations, baseline_user_locations_duration, additional_properties].hash end end end