diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index d3d603bec..87a84afd6 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -20470,6 +20470,8 @@ components: $ref: '#/components/schemas/SecurityMonitoringRuleMaxSignalDuration' newValueOptions: $ref: '#/components/schemas/SecurityMonitoringRuleNewValueOptions' + sequenceDetectionOptions: + $ref: '#/components/schemas/SecurityMonitoringRuleSequenceDetectionOptions' thirdPartyRuleOptions: $ref: '#/components/schemas/SecurityMonitoringRuleThirdPartyOptions' type: object @@ -40786,6 +40788,7 @@ components: - hardcoded - third_party - anomaly_threshold + - sequence_detection type: string x-enum-varnames: - THRESHOLD @@ -40795,6 +40798,7 @@ components: - HARDCODED - THIRD_PARTY - ANOMALY_THRESHOLD + - SEQUENCE_DETECTION SecurityMonitoringRuleEvaluationWindow: description: 'A time window is specified to match when at least one of the cases matches true. This is a sliding window @@ -41008,6 +41012,8 @@ components: $ref: '#/components/schemas/SecurityMonitoringRuleMaxSignalDuration' newValueOptions: $ref: '#/components/schemas/SecurityMonitoringRuleNewValueOptions' + sequenceDetectionOptions: + $ref: '#/components/schemas/SecurityMonitoringRuleSequenceDetectionOptions' thirdPartyRuleOptions: $ref: '#/components/schemas/SecurityMonitoringRuleThirdPartyOptions' type: object @@ -41083,6 +41089,47 @@ components: oneOf: - $ref: '#/components/schemas/SecurityMonitoringStandardRuleResponse' - $ref: '#/components/schemas/SecurityMonitoringSignalRuleResponse' + SecurityMonitoringRuleSequenceDetectionOptions: + description: Options on sequence detection method. + properties: + stepTransitions: + description: Transitions defining the allowed order of steps and their evaluation + windows. + items: + $ref: '#/components/schemas/SecurityMonitoringRuleSequenceDetectionStepTransition' + type: array + steps: + description: Steps that define the conditions to be matched in sequence. + items: + $ref: '#/components/schemas/SecurityMonitoringRuleSequenceDetectionStep' + type: array + type: object + SecurityMonitoringRuleSequenceDetectionStep: + description: Step definition for sequence detection containing the step name, + condition, and evaluation window. + properties: + condition: + description: Condition referencing rule queries (e.g., `a > 0`). + type: string + evaluationWindow: + $ref: '#/components/schemas/SecurityMonitoringRuleEvaluationWindow' + name: + description: Unique name identifying the step. + type: string + type: object + SecurityMonitoringRuleSequenceDetectionStepTransition: + description: Transition from a parent step to a child step within a sequence + detection rule. + properties: + child: + description: Name of the child step. + type: string + evaluationWindow: + $ref: '#/components/schemas/SecurityMonitoringRuleEvaluationWindow' + parent: + description: Name of the parent step. + type: string + type: object SecurityMonitoringRuleSeverity: description: Severity of the Security Signal. enum: diff --git a/examples/v2_security-monitoring_CreateSecurityMonitoringRule_2899714190.rs b/examples/v2_security-monitoring_CreateSecurityMonitoringRule_2899714190.rs new file mode 100644 index 000000000..3d89374f5 --- /dev/null +++ b/examples/v2_security-monitoring_CreateSecurityMonitoringRule_2899714190.rs @@ -0,0 +1,96 @@ +// Create a detection rule with detection method 'sequence_detection' returns "OK" +// response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleCaseCreate; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleCreatePayload; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleDetectionMethod; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleEvaluationWindow; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleKeepAlive; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleMaxSignalDuration; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleOptions; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleQueryAggregation; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionStep; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionStepTransition; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSeverity; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleTypeCreate; +use datadog_api_client::datadogV2::model::SecurityMonitoringStandardDataSource; +use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRuleCreatePayload; +use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRuleQuery; + +#[tokio::main] +async fn main() { + let body = + SecurityMonitoringRuleCreatePayload::SecurityMonitoringStandardRuleCreatePayload(Box::new( + SecurityMonitoringStandardRuleCreatePayload::new( + vec![ + SecurityMonitoringRuleCaseCreate::new(SecurityMonitoringRuleSeverity::INFO) + .condition("step_b > 0".to_string()) + .name("".to_string()) + .notifications(vec![]), + ], + true, + "Logs and signals asdf".to_string(), + "Example-Security-Monitoring".to_string(), + SecurityMonitoringRuleOptions::new() + .detection_method(SecurityMonitoringRuleDetectionMethod::SEQUENCE_DETECTION) + .evaluation_window(SecurityMonitoringRuleEvaluationWindow::ZERO_MINUTES) + .keep_alive(SecurityMonitoringRuleKeepAlive::FIVE_MINUTES) + .max_signal_duration(SecurityMonitoringRuleMaxSignalDuration::TEN_MINUTES) + .sequence_detection_options( + SecurityMonitoringRuleSequenceDetectionOptions::new() + .step_transitions(vec![ + SecurityMonitoringRuleSequenceDetectionStepTransition::new() + .child("step_b".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::FIFTEEN_MINUTES, + ) + .parent("step_a".to_string()), + ]) + .steps(vec![ + SecurityMonitoringRuleSequenceDetectionStep::new() + .condition("a > 0".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::ONE_MINUTE, + ) + .name("step_a".to_string()), + SecurityMonitoringRuleSequenceDetectionStep::new() + .condition("b > 0".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::ONE_MINUTE, + ) + .name("step_b".to_string()), + ]), + ), + vec![ + SecurityMonitoringStandardRuleQuery::new() + .aggregation(SecurityMonitoringRuleQueryAggregation::COUNT) + .data_source(SecurityMonitoringStandardDataSource::LOGS) + .distinct_fields(vec![]) + .group_by_fields(vec![]) + .has_optional_group_by_fields(false) + .name("".to_string()) + .query("service:logs-rule-reducer source:paul test2".to_string()), + SecurityMonitoringStandardRuleQuery::new() + .aggregation(SecurityMonitoringRuleQueryAggregation::COUNT) + .data_source(SecurityMonitoringStandardDataSource::LOGS) + .distinct_fields(vec![]) + .group_by_fields(vec![]) + .has_optional_group_by_fields(false) + .name("".to_string()) + .query("service:logs-rule-reducer source:paul test1".to_string()), + ], + ) + .tags(vec![]) + .type_(SecurityMonitoringRuleTypeCreate::LOG_DETECTION), + )); + let configuration = datadog::Configuration::new(); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api.create_security_monitoring_rule(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_security-monitoring_ValidateSecurityMonitoringRule_4152369508.rs b/examples/v2_security-monitoring_ValidateSecurityMonitoringRule_4152369508.rs new file mode 100644 index 000000000..24b7323f7 --- /dev/null +++ b/examples/v2_security-monitoring_ValidateSecurityMonitoringRule_4152369508.rs @@ -0,0 +1,92 @@ +// Validate a detection rule with detection method 'sequence_detection' returns +// "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleCaseCreate; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleDetectionMethod; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleEvaluationWindow; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleKeepAlive; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleMaxSignalDuration; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleOptions; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleQueryAggregation; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionStep; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSequenceDetectionStepTransition; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSeverity; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleTypeCreate; +use datadog_api_client::datadogV2::model::SecurityMonitoringRuleValidatePayload; +use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRulePayload; +use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRuleQuery; + +#[tokio::main] +async fn main() { + let body = + SecurityMonitoringRuleValidatePayload::SecurityMonitoringStandardRulePayload(Box::new( + SecurityMonitoringStandardRulePayload::new( + vec![ + SecurityMonitoringRuleCaseCreate::new(SecurityMonitoringRuleSeverity::INFO) + .condition("step_b > 0".to_string()) + .name("".to_string()) + .notifications(vec![]), + ], + true, + "My security monitoring rule".to_string(), + "My security monitoring rule".to_string(), + SecurityMonitoringRuleOptions::new() + .detection_method(SecurityMonitoringRuleDetectionMethod::SEQUENCE_DETECTION) + .evaluation_window(SecurityMonitoringRuleEvaluationWindow::ZERO_MINUTES) + .keep_alive(SecurityMonitoringRuleKeepAlive::FIVE_MINUTES) + .max_signal_duration(SecurityMonitoringRuleMaxSignalDuration::TEN_MINUTES) + .sequence_detection_options( + SecurityMonitoringRuleSequenceDetectionOptions::new() + .step_transitions(vec![ + SecurityMonitoringRuleSequenceDetectionStepTransition::new() + .child("step_b".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::FIFTEEN_MINUTES, + ) + .parent("step_a".to_string()), + ]) + .steps(vec![ + SecurityMonitoringRuleSequenceDetectionStep::new() + .condition("a > 0".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::ONE_MINUTE, + ) + .name("step_a".to_string()), + SecurityMonitoringRuleSequenceDetectionStep::new() + .condition("b > 0".to_string()) + .evaluation_window( + SecurityMonitoringRuleEvaluationWindow::ONE_MINUTE, + ) + .name("step_b".to_string()), + ]), + ), + vec![ + SecurityMonitoringStandardRuleQuery::new() + .aggregation(SecurityMonitoringRuleQueryAggregation::COUNT) + .distinct_fields(vec![]) + .group_by_fields(vec!["@userIdentity.assumed_role".to_string()]) + .name("".to_string()) + .query("source:source_here".to_string()), + SecurityMonitoringStandardRuleQuery::new() + .aggregation(SecurityMonitoringRuleQueryAggregation::COUNT) + .distinct_fields(vec![]) + .group_by_fields(vec![]) + .name("".to_string()) + .query("source:source_here2".to_string()), + ], + ) + .has_extended_title(true) + .tags(vec!["env:prod".to_string(), "team:security".to_string()]) + .type_(SecurityMonitoringRuleTypeCreate::LOG_DETECTION), + )); + let configuration = datadog::Configuration::new(); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api.validate_security_monitoring_rule(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index e74b43336..d73ed3b92 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -5222,6 +5222,12 @@ pub mod model_security_monitoring_rule_new_value_options_learning_method; pub use self::model_security_monitoring_rule_new_value_options_learning_method::SecurityMonitoringRuleNewValueOptionsLearningMethod; pub mod model_security_monitoring_rule_new_value_options_learning_threshold; pub use self::model_security_monitoring_rule_new_value_options_learning_threshold::SecurityMonitoringRuleNewValueOptionsLearningThreshold; +pub mod model_security_monitoring_rule_sequence_detection_options; +pub use self::model_security_monitoring_rule_sequence_detection_options::SecurityMonitoringRuleSequenceDetectionOptions; +pub mod model_security_monitoring_rule_sequence_detection_step_transition; +pub use self::model_security_monitoring_rule_sequence_detection_step_transition::SecurityMonitoringRuleSequenceDetectionStepTransition; +pub mod model_security_monitoring_rule_sequence_detection_step; +pub use self::model_security_monitoring_rule_sequence_detection_step::SecurityMonitoringRuleSequenceDetectionStep; pub mod model_security_monitoring_rule_third_party_options; pub use self::model_security_monitoring_rule_third_party_options::SecurityMonitoringRuleThirdPartyOptions; pub mod model_security_monitoring_third_party_root_query; diff --git a/src/datadogV2/model/model_historical_job_options.rs b/src/datadogV2/model/model_historical_job_options.rs index ab2862b00..13905deb7 100644 --- a/src/datadogV2/model/model_historical_job_options.rs +++ b/src/datadogV2/model/model_historical_job_options.rs @@ -34,6 +34,10 @@ pub struct HistoricalJobOptions { /// Options on new value detection method. #[serde(rename = "newValueOptions")] pub new_value_options: Option, + /// Options on sequence detection method. + #[serde(rename = "sequenceDetectionOptions")] + pub sequence_detection_options: + Option, /// Options on third party detection method. #[serde(rename = "thirdPartyRuleOptions")] pub third_party_rule_options: @@ -54,6 +58,7 @@ impl HistoricalJobOptions { keep_alive: None, max_signal_duration: None, new_value_options: None, + sequence_detection_options: None, third_party_rule_options: None, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, @@ -108,6 +113,14 @@ impl HistoricalJobOptions { self } + pub fn sequence_detection_options( + mut self, + value: crate::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions, + ) -> Self { + self.sequence_detection_options = Some(value); + self + } + pub fn third_party_rule_options( mut self, value: crate::datadogV2::model::SecurityMonitoringRuleThirdPartyOptions, @@ -166,6 +179,9 @@ impl<'de> Deserialize<'de> for HistoricalJobOptions { let mut new_value_options: Option< crate::datadogV2::model::SecurityMonitoringRuleNewValueOptions, > = None; + let mut sequence_detection_options: Option< + crate::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions, + > = None; let mut third_party_rule_options: Option< crate::datadogV2::model::SecurityMonitoringRuleThirdPartyOptions, > = None; @@ -250,6 +266,13 @@ impl<'de> Deserialize<'de> for HistoricalJobOptions { new_value_options = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "sequenceDetectionOptions" => { + if v.is_null() { + continue; + } + sequence_detection_options = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "thirdPartyRuleOptions" => { if v.is_null() { continue; @@ -272,6 +295,7 @@ impl<'de> Deserialize<'de> for HistoricalJobOptions { keep_alive, max_signal_duration, new_value_options, + sequence_detection_options, third_party_rule_options, additional_properties, _unparsed, diff --git a/src/datadogV2/model/model_security_monitoring_rule_detection_method.rs b/src/datadogV2/model/model_security_monitoring_rule_detection_method.rs index 1d9877a6d..f1a60dca5 100644 --- a/src/datadogV2/model/model_security_monitoring_rule_detection_method.rs +++ b/src/datadogV2/model/model_security_monitoring_rule_detection_method.rs @@ -14,6 +14,7 @@ pub enum SecurityMonitoringRuleDetectionMethod { HARDCODED, THIRD_PARTY, ANOMALY_THRESHOLD, + SEQUENCE_DETECTION, UnparsedObject(crate::datadog::UnparsedObject), } @@ -27,6 +28,7 @@ impl ToString for SecurityMonitoringRuleDetectionMethod { Self::HARDCODED => String::from("hardcoded"), Self::THIRD_PARTY => String::from("third_party"), Self::ANOMALY_THRESHOLD => String::from("anomaly_threshold"), + Self::SEQUENCE_DETECTION => String::from("sequence_detection"), Self::UnparsedObject(v) => v.value.to_string(), } } @@ -58,6 +60,7 @@ impl<'de> Deserialize<'de> for SecurityMonitoringRuleDetectionMethod { "hardcoded" => Self::HARDCODED, "third_party" => Self::THIRD_PARTY, "anomaly_threshold" => Self::ANOMALY_THRESHOLD, + "sequence_detection" => Self::SEQUENCE_DETECTION, _ => Self::UnparsedObject(crate::datadog::UnparsedObject { value: serde_json::Value::String(s.into()), }), diff --git a/src/datadogV2/model/model_security_monitoring_rule_options.rs b/src/datadogV2/model/model_security_monitoring_rule_options.rs index dc7c79980..19d9111c6 100644 --- a/src/datadogV2/model/model_security_monitoring_rule_options.rs +++ b/src/datadogV2/model/model_security_monitoring_rule_options.rs @@ -49,6 +49,10 @@ pub struct SecurityMonitoringRuleOptions { /// Options on new value detection method. #[serde(rename = "newValueOptions")] pub new_value_options: Option, + /// Options on sequence detection method. + #[serde(rename = "sequenceDetectionOptions")] + pub sequence_detection_options: + Option, /// Options on third party detection method. #[serde(rename = "thirdPartyRuleOptions")] pub third_party_rule_options: @@ -72,6 +76,7 @@ impl SecurityMonitoringRuleOptions { keep_alive: None, max_signal_duration: None, new_value_options: None, + sequence_detection_options: None, third_party_rule_options: None, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, @@ -147,6 +152,14 @@ impl SecurityMonitoringRuleOptions { self } + pub fn sequence_detection_options( + mut self, + value: crate::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions, + ) -> Self { + self.sequence_detection_options = Some(value); + self + } + pub fn third_party_rule_options( mut self, value: crate::datadogV2::model::SecurityMonitoringRuleThirdPartyOptions, @@ -212,6 +225,9 @@ impl<'de> Deserialize<'de> for SecurityMonitoringRuleOptions { let mut new_value_options: Option< crate::datadogV2::model::SecurityMonitoringRuleNewValueOptions, > = None; + let mut sequence_detection_options: Option< + crate::datadogV2::model::SecurityMonitoringRuleSequenceDetectionOptions, + > = None; let mut third_party_rule_options: Option< crate::datadogV2::model::SecurityMonitoringRuleThirdPartyOptions, > = None; @@ -325,6 +341,13 @@ impl<'de> Deserialize<'de> for SecurityMonitoringRuleOptions { new_value_options = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } + "sequenceDetectionOptions" => { + if v.is_null() { + continue; + } + sequence_detection_options = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } "thirdPartyRuleOptions" => { if v.is_null() { continue; @@ -350,6 +373,7 @@ impl<'de> Deserialize<'de> for SecurityMonitoringRuleOptions { keep_alive, max_signal_duration, new_value_options, + sequence_detection_options, third_party_rule_options, additional_properties, _unparsed, diff --git a/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_options.rs b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_options.rs new file mode 100644 index 000000000..c1925bbb9 --- /dev/null +++ b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_options.rs @@ -0,0 +1,132 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Options on sequence detection method. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct SecurityMonitoringRuleSequenceDetectionOptions { + /// Transitions defining the allowed order of steps and their evaluation windows. + #[serde(rename = "stepTransitions")] + pub step_transitions: + Option>, + /// Steps that define the conditions to be matched in sequence. + #[serde(rename = "steps")] + pub steps: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl SecurityMonitoringRuleSequenceDetectionOptions { + pub fn new() -> SecurityMonitoringRuleSequenceDetectionOptions { + SecurityMonitoringRuleSequenceDetectionOptions { + step_transitions: None, + steps: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn step_transitions( + mut self, + value: Vec, + ) -> Self { + self.step_transitions = Some(value); + self + } + + pub fn steps( + mut self, + value: Vec, + ) -> Self { + self.steps = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for SecurityMonitoringRuleSequenceDetectionOptions { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for SecurityMonitoringRuleSequenceDetectionOptions { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SecurityMonitoringRuleSequenceDetectionOptionsVisitor; + impl<'a> Visitor<'a> for SecurityMonitoringRuleSequenceDetectionOptionsVisitor { + type Value = SecurityMonitoringRuleSequenceDetectionOptions; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut step_transitions: Option> = None; + let mut steps: Option< + Vec, + > = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "stepTransitions" => { + if v.is_null() { + continue; + } + step_transitions = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "steps" => { + if v.is_null() { + continue; + } + steps = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = SecurityMonitoringRuleSequenceDetectionOptions { + step_transitions, + steps, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(SecurityMonitoringRuleSequenceDetectionOptionsVisitor) + } +} diff --git a/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step.rs b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step.rs new file mode 100644 index 000000000..4336f6734 --- /dev/null +++ b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step.rs @@ -0,0 +1,154 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Step definition for sequence detection containing the step name, condition, and evaluation window. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct SecurityMonitoringRuleSequenceDetectionStep { + /// Condition referencing rule queries (e.g., `a > 0`). + #[serde(rename = "condition")] + pub condition: Option, + /// A time window is specified to match when at least one of the cases matches true. This is a sliding window + /// and evaluates in real time. For third party detection method, this field is not used. + #[serde(rename = "evaluationWindow")] + pub evaluation_window: Option, + /// Unique name identifying the step. + #[serde(rename = "name")] + pub name: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl SecurityMonitoringRuleSequenceDetectionStep { + pub fn new() -> SecurityMonitoringRuleSequenceDetectionStep { + SecurityMonitoringRuleSequenceDetectionStep { + condition: None, + evaluation_window: None, + name: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn condition(mut self, value: String) -> Self { + self.condition = Some(value); + self + } + + pub fn evaluation_window( + mut self, + value: crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow, + ) -> Self { + self.evaluation_window = Some(value); + self + } + + pub fn name(mut self, value: String) -> Self { + self.name = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for SecurityMonitoringRuleSequenceDetectionStep { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for SecurityMonitoringRuleSequenceDetectionStep { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SecurityMonitoringRuleSequenceDetectionStepVisitor; + impl<'a> Visitor<'a> for SecurityMonitoringRuleSequenceDetectionStepVisitor { + type Value = SecurityMonitoringRuleSequenceDetectionStep; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut condition: Option = None; + let mut evaluation_window: Option< + crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow, + > = None; + let mut name: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "condition" => { + if v.is_null() { + continue; + } + condition = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "evaluationWindow" => { + if v.is_null() { + continue; + } + evaluation_window = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _evaluation_window) = evaluation_window { + match _evaluation_window { + crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow::UnparsedObject(_evaluation_window) => { + _unparsed = true; + }, + _ => {} + } + } + } + "name" => { + if v.is_null() { + continue; + } + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = SecurityMonitoringRuleSequenceDetectionStep { + condition, + evaluation_window, + name, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(SecurityMonitoringRuleSequenceDetectionStepVisitor) + } +} diff --git a/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step_transition.rs b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step_transition.rs new file mode 100644 index 000000000..3a09e175a --- /dev/null +++ b/src/datadogV2/model/model_security_monitoring_rule_sequence_detection_step_transition.rs @@ -0,0 +1,154 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Transition from a parent step to a child step within a sequence detection rule. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct SecurityMonitoringRuleSequenceDetectionStepTransition { + /// Name of the child step. + #[serde(rename = "child")] + pub child: Option, + /// A time window is specified to match when at least one of the cases matches true. This is a sliding window + /// and evaluates in real time. For third party detection method, this field is not used. + #[serde(rename = "evaluationWindow")] + pub evaluation_window: Option, + /// Name of the parent step. + #[serde(rename = "parent")] + pub parent: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl SecurityMonitoringRuleSequenceDetectionStepTransition { + pub fn new() -> SecurityMonitoringRuleSequenceDetectionStepTransition { + SecurityMonitoringRuleSequenceDetectionStepTransition { + child: None, + evaluation_window: None, + parent: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn child(mut self, value: String) -> Self { + self.child = Some(value); + self + } + + pub fn evaluation_window( + mut self, + value: crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow, + ) -> Self { + self.evaluation_window = Some(value); + self + } + + pub fn parent(mut self, value: String) -> Self { + self.parent = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for SecurityMonitoringRuleSequenceDetectionStepTransition { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for SecurityMonitoringRuleSequenceDetectionStepTransition { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SecurityMonitoringRuleSequenceDetectionStepTransitionVisitor; + impl<'a> Visitor<'a> for SecurityMonitoringRuleSequenceDetectionStepTransitionVisitor { + type Value = SecurityMonitoringRuleSequenceDetectionStepTransition; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut child: Option = None; + let mut evaluation_window: Option< + crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow, + > = None; + let mut parent: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "child" => { + if v.is_null() { + continue; + } + child = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "evaluationWindow" => { + if v.is_null() { + continue; + } + evaluation_window = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _evaluation_window) = evaluation_window { + match _evaluation_window { + crate::datadogV2::model::SecurityMonitoringRuleEvaluationWindow::UnparsedObject(_evaluation_window) => { + _unparsed = true; + }, + _ => {} + } + } + } + "parent" => { + if v.is_null() { + continue; + } + parent = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = SecurityMonitoringRuleSequenceDetectionStepTransition { + child, + evaluation_window, + parent, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(SecurityMonitoringRuleSequenceDetectionStepTransitionVisitor) + } +} diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen b/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen new file mode 100644 index 000000000..6c0fc3d68 --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-09-12T15:45:55.719Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json b/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json new file mode 100644 index 000000000..985b8a29b --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Create-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json @@ -0,0 +1,63 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"cases\":[{\"condition\":\"step_b > 0\",\"name\":\"\",\"notifications\":[],\"status\":\"info\"}],\"isEnabled\":true,\"message\":\"Logs and signals asdf\",\"name\":\"Test-Create_a_detection_rule_with_detection_method_sequence_detection_returns_OK_response-1757691955\",\"options\":{\"detectionMethod\":\"sequence_detection\",\"evaluationWindow\":0,\"keepAlive\":300,\"maxSignalDuration\":600,\"sequenceDetectionOptions\":{\"stepTransitions\":[{\"child\":\"step_b\",\"evaluationWindow\":900,\"parent\":\"step_a\"}],\"steps\":[{\"condition\":\"a > 0\",\"evaluationWindow\":60,\"name\":\"step_a\"},{\"condition\":\"b > 0\",\"evaluationWindow\":60,\"name\":\"step_b\"}]}},\"queries\":[{\"aggregation\":\"count\",\"dataSource\":\"logs\",\"distinctFields\":[],\"groupByFields\":[],\"hasOptionalGroupByFields\":false,\"name\":\"\",\"query\":\"service:logs-rule-reducer source:paul test2\"},{\"aggregation\":\"count\",\"dataSource\":\"logs\",\"distinctFields\":[],\"groupByFields\":[],\"hasOptionalGroupByFields\":false,\"name\":\"\",\"query\":\"service:logs-rule-reducer source:paul test1\"}],\"tags\":[],\"type\":\"log_detection\"}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/rules" + }, + "response": { + "body": { + "string": "{\"name\":\"Test-Create_a_detection_rule_with_detection_method_sequence_detection_returns_OK_response-1757691955\",\"createdAt\":1757691955862,\"isDefault\":false,\"isPartner\":false,\"isEnabled\":true,\"isBeta\":false,\"isDeleted\":false,\"isDeprecated\":false,\"queries\":[{\"query\":\"service:logs-rule-reducer source:paul test2\",\"groupByFields\":[],\"hasOptionalGroupByFields\":false,\"distinctFields\":[],\"aggregation\":\"count\",\"name\":\"\",\"dataSource\":\"logs\"},{\"query\":\"service:logs-rule-reducer source:paul test1\",\"groupByFields\":[],\"hasOptionalGroupByFields\":false,\"distinctFields\":[],\"aggregation\":\"count\",\"name\":\"\",\"dataSource\":\"logs\"}],\"options\":{\"evaluationWindow\":0,\"detectionMethod\":\"sequence_detection\",\"maxSignalDuration\":600,\"keepAlive\":300,\"sequenceDetectionOptions\":{\"steps\":[{\"name\":\"step_a\",\"condition\":\"a \\u003e 0\",\"evaluationWindow\":60},{\"name\":\"step_b\",\"condition\":\"b \\u003e 0\",\"evaluationWindow\":60}],\"stepTransitions\":[{\"parent\":\"step_a\",\"child\":\"step_b\",\"evaluationWindow\":900}]}},\"cases\":[{\"name\":\"\",\"status\":\"info\",\"notifications\":[],\"condition\":\"step_b \\u003e 0\"}],\"message\":\"Logs and signals asdf\",\"tags\":[],\"hasExtendedTitle\":false,\"type\":\"log_detection\",\"filters\":[],\"version\":1,\"id\":\"k0l-txb-xxx\",\"blocking\":false,\"metadata\":{\"entities\":null,\"sources\":null},\"creationAuthorId\":1445416,\"creator\":{\"handle\":\"frog@datadoghq.com\",\"name\":\"frog\"},\"updater\":{\"handle\":\"\",\"name\":\"\"}}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Fri, 12 Sep 2025 15:45:55 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "*/*" + ] + }, + "method": "delete", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/rules/k0l-txb-xxx" + }, + "response": { + "body": { + "string": "", + "encoding": null + }, + "headers": {}, + "status": { + "code": 204, + "message": "No Content" + } + }, + "recorded_at": "Fri, 12 Sep 2025 15:45:55 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen b/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen new file mode 100644 index 000000000..50a6e3a2a --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-09-12T15:43:48.016Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json b/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json new file mode 100644 index 000000000..805b29b2e --- /dev/null +++ b/tests/scenarios/cassettes/v2/security_monitoring/Validate-a-detection-rule-with-detection-method-sequence-detection-returns-OK-response.json @@ -0,0 +1,35 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"cases\":[{\"condition\":\"step_b > 0\",\"name\":\"\",\"notifications\":[],\"status\":\"info\"}],\"hasExtendedTitle\":true,\"isEnabled\":true,\"message\":\"My security monitoring rule\",\"name\":\"My security monitoring rule\",\"options\":{\"detectionMethod\":\"sequence_detection\",\"evaluationWindow\":0,\"keepAlive\":300,\"maxSignalDuration\":600,\"sequenceDetectionOptions\":{\"stepTransitions\":[{\"child\":\"step_b\",\"evaluationWindow\":900,\"parent\":\"step_a\"}],\"steps\":[{\"condition\":\"a > 0\",\"evaluationWindow\":60,\"name\":\"step_a\"},{\"condition\":\"b > 0\",\"evaluationWindow\":60,\"name\":\"step_b\"}]}},\"queries\":[{\"aggregation\":\"count\",\"distinctFields\":[],\"groupByFields\":[\"@userIdentity.assumed_role\"],\"name\":\"\",\"query\":\"source:source_here\"},{\"aggregation\":\"count\",\"distinctFields\":[],\"groupByFields\":[],\"name\":\"\",\"query\":\"source:source_here2\"}],\"tags\":[\"env:prod\",\"team:security\"],\"type\":\"log_detection\"}", + "encoding": null + }, + "headers": { + "Accept": [ + "*/*" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v2/security_monitoring/rules/validation" + }, + "response": { + "body": { + "string": "", + "encoding": null + }, + "headers": {}, + "status": { + "code": 204, + "message": "No Content" + } + }, + "recorded_at": "Fri, 12 Sep 2025 15:43:48 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/features/v2/security_monitoring.feature b/tests/scenarios/features/v2/security_monitoring.feature index 612e8fc5f..f4eab41ff 100644 --- a/tests/scenarios/features/v2/security_monitoring.feature +++ b/tests/scenarios/features/v2/security_monitoring.feature @@ -211,6 +211,16 @@ Feature: Security Monitoring And the response "message" is equal to "Test rule" And the response "referenceTables" is equal to [{"tableName": "synthetics_test_reference_table_dont_delete", "columnName": "value", "logFieldPath":"testtag", "checkPresence":true, "ruleQueryName":"a"}] + @team:DataDog/k9-cloud-security-platform + Scenario: Create a detection rule with detection method 'sequence_detection' returns "OK" response + Given new "CreateSecurityMonitoringRule" request + And body with value {"name":"{{ unique }}","type":"log_detection","isEnabled":true,"queries":[{"aggregation":"count","dataSource":"logs","distinctFields":[],"groupByFields":[],"hasOptionalGroupByFields":false,"name":"","query":"service:logs-rule-reducer source:paul test2"},{"aggregation":"count","dataSource":"logs","distinctFields":[],"groupByFields":[],"hasOptionalGroupByFields":false,"name":"","query":"service:logs-rule-reducer source:paul test1"}],"cases":[{"name":"","status":"info","notifications":[],"condition":"step_b > 0"}],"message":"Logs and signals asdf","options":{"detectionMethod":"sequence_detection","evaluationWindow":0,"keepAlive":300,"maxSignalDuration":600,"sequenceDetectionOptions":{"stepTransitions":[{"child":"step_b","evaluationWindow":900,"parent":"step_a"}],"steps":[{"condition":"a > 0","evaluationWindow":60,"name":"step_a"},{"condition":"b > 0","evaluationWindow":60,"name":"step_b"}]}},"tags":[]} + 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 "options.detectionMethod" is equal to "sequence_detection" + @team:DataDog/k9-cloud-security-platform Scenario: Create a detection rule with detection method 'third_party' returns "OK" response Given new "CreateSecurityMonitoringRule" request @@ -1483,6 +1493,13 @@ Feature: Security Monitoring When the request is sent Then the response status is 204 OK + @team:DataDog/k9-cloud-security-platform + Scenario: Validate a detection rule with detection method 'sequence_detection' returns "OK" response + Given new "ValidateSecurityMonitoringRule" request + And body with value {"cases":[{"name":"","status":"info","notifications":[],"condition":"step_b > 0"}],"hasExtendedTitle":true,"isEnabled":true,"message":"My security monitoring rule","name":"My security monitoring rule","options":{"evaluationWindow":0,"keepAlive":300,"maxSignalDuration":600,"detectionMethod":"sequence_detection","sequenceDetectionOptions":{"stepTransitions":[{"child":"step_b","evaluationWindow":900,"parent":"step_a"}],"steps":[{"condition":"a > 0","evaluationWindow":60,"name":"step_a"},{"condition":"b > 0","evaluationWindow":60,"name":"step_b"}]}},"queries":[{"query":"source:source_here","groupByFields":["@userIdentity.assumed_role"],"distinctFields":[],"aggregation":"count","name":""},{"query":"source:source_here2","groupByFields":[],"distinctFields":[],"aggregation":"count","name":""}],"tags":["env:prod","team:security"],"type":"log_detection"} + When the request is sent + Then the response status is 204 OK + @team:DataDog/k9-cloud-security-platform Scenario: Validate a suppression rule returns "Bad Request" response Given new "ValidateSecurityMonitoringSuppression" request