Skip to content

Commit 990c8f9

Browse files
api-clients-generation-pipeline[bot]ci.datadog-api-spec
and
ci.datadog-api-spec
authored
Add validation endpoint for Security Monitoring Rules (#58)
Co-authored-by: ci.datadog-api-spec <[email protected]> Co-authored-by: api-clients-generation-pipeline[bot] <54105614+api-clients-generation-pipeline[bot]@users.noreply.github.com>
1 parent fe83698 commit 990c8f9

11 files changed

+305
-4
lines changed

.apigentools-info

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
"spec_versions": {
55
"v1": {
66
"apigentools_version": "1.6.6",
7-
"regenerated": "2024-03-26 15:17:41.342524",
8-
"spec_repo_commit": "46383d02"
7+
"regenerated": "2024-03-27 22:12:41.503072",
8+
"spec_repo_commit": "85625198"
99
},
1010
"v2": {
1111
"apigentools_version": "1.6.6",
12-
"regenerated": "2024-03-26 15:17:41.373837",
13-
"spec_repo_commit": "46383d02"
12+
"regenerated": "2024-03-27 22:12:41.520431",
13+
"spec_repo_commit": "85625198"
1414
}
1515
}
1616
}

.generator/schemas/v2/openapi.yaml

+28
Original file line numberDiff line numberDiff line change
@@ -32287,6 +32287,34 @@ paths:
3228732287
tags:
3228832288
- Security Monitoring
3228932289
x-codegen-request-body-name: body
32290+
/api/v2/security_monitoring/rules/validation:
32291+
post:
32292+
description: Validate a detection rule.
32293+
operationId: ValidateSecurityMonitoringRule
32294+
requestBody:
32295+
content:
32296+
application/json:
32297+
schema:
32298+
$ref: '#/components/schemas/SecurityMonitoringRuleCreatePayload'
32299+
required: true
32300+
responses:
32301+
'204':
32302+
description: OK
32303+
'400':
32304+
$ref: '#/components/responses/BadRequestResponse'
32305+
'403':
32306+
$ref: '#/components/responses/NotAuthorizedResponse'
32307+
'429':
32308+
$ref: '#/components/responses/TooManyRequestsResponse'
32309+
security:
32310+
- apiKeyAuth: []
32311+
appKeyAuth: []
32312+
- AuthZ:
32313+
- security_monitoring_rules_write
32314+
summary: Validate a detection rule
32315+
tags:
32316+
- Security Monitoring
32317+
x-codegen-request-body-name: body
3229032318
/api/v2/security_monitoring/rules/{rule_id}:
3229132319
delete:
3229232320
description: Delete an existing rule. Default rules cannot be deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Validate a detection rule returns "OK" response
2+
use datadog_api_client::datadog::configuration::Configuration;
3+
use datadog_api_client::datadogV2::api::api_security_monitoring::SecurityMonitoringAPI;
4+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleCaseCreate;
5+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleCreatePayload;
6+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleDetectionMethod;
7+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleEvaluationWindow;
8+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleKeepAlive;
9+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleMaxSignalDuration;
10+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleOptions;
11+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleQueryAggregation;
12+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleSeverity;
13+
use datadog_api_client::datadogV2::model::SecurityMonitoringRuleTypeCreate;
14+
use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRuleCreatePayload;
15+
use datadog_api_client::datadogV2::model::SecurityMonitoringStandardRuleQuery;
16+
17+
#[tokio::main]
18+
async fn main() {
19+
let body =
20+
SecurityMonitoringRuleCreatePayload::SecurityMonitoringStandardRuleCreatePayload(Box::new(
21+
SecurityMonitoringStandardRuleCreatePayload::new(
22+
vec![
23+
SecurityMonitoringRuleCaseCreate::new(SecurityMonitoringRuleSeverity::INFO)
24+
.condition("a > 0".to_string())
25+
.name("".to_string())
26+
.notifications(vec![]),
27+
],
28+
true,
29+
"My security monitoring rule".to_string(),
30+
"My security monitoring rule".to_string(),
31+
SecurityMonitoringRuleOptions::new()
32+
.detection_method(SecurityMonitoringRuleDetectionMethod::THRESHOLD)
33+
.evaluation_window(SecurityMonitoringRuleEvaluationWindow::THIRTY_MINUTES)
34+
.keep_alive(SecurityMonitoringRuleKeepAlive::THIRTY_MINUTES)
35+
.max_signal_duration(SecurityMonitoringRuleMaxSignalDuration::THIRTY_MINUTES),
36+
vec![SecurityMonitoringStandardRuleQuery::new()
37+
.aggregation(SecurityMonitoringRuleQueryAggregation::COUNT)
38+
.distinct_fields(vec![])
39+
.group_by_fields(vec!["@userIdentity.assumed_role".to_string()])
40+
.name("".to_string())
41+
.query("source:source_here".to_string())],
42+
)
43+
.has_extended_title(true)
44+
.tags(vec!["env:prod".to_string(), "team:security".to_string()])
45+
.type_(SecurityMonitoringRuleTypeCreate::LOG_DETECTION),
46+
));
47+
let configuration = Configuration::new();
48+
let api = SecurityMonitoringAPI::with_config(configuration);
49+
let resp = api.validate_security_monitoring_rule(body).await;
50+
if let Ok(value) = resp {
51+
println!("{:#?}", value);
52+
} else {
53+
println!("{:#?}", resp.unwrap_err());
54+
}
55+
}

src/datadogV2/api/api_security_monitoring.rs

+86
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,16 @@ pub enum UpdateSecurityMonitoringSuppressionError {
458458
UnknownValue(serde_json::Value),
459459
}
460460

461+
/// ValidateSecurityMonitoringRuleError is a struct for typed errors of method [`SecurityMonitoringAPI::validate_security_monitoring_rule`]
462+
#[derive(Debug, Clone, Serialize, Deserialize)]
463+
#[serde(untagged)]
464+
pub enum ValidateSecurityMonitoringRuleError {
465+
Status400(Option<crate::datadogV2::model::APIErrorResponse>),
466+
Status403(Option<crate::datadogV2::model::APIErrorResponse>),
467+
Status429(Option<crate::datadogV2::model::APIErrorResponse>),
468+
UnknownValue(serde_json::Value),
469+
}
470+
461471
#[derive(Debug, Clone)]
462472
pub struct SecurityMonitoringAPI {
463473
config: configuration::Configuration,
@@ -3015,4 +3025,80 @@ impl SecurityMonitoringAPI {
30153025
Err(Error::ResponseError(local_error))
30163026
}
30173027
}
3028+
3029+
/// Validate a detection rule.
3030+
pub async fn validate_security_monitoring_rule(
3031+
&self,
3032+
body: crate::datadogV2::model::SecurityMonitoringRuleCreatePayload,
3033+
) -> Result<(), Error<ValidateSecurityMonitoringRuleError>> {
3034+
match self
3035+
.validate_security_monitoring_rule_with_http_info(body)
3036+
.await
3037+
{
3038+
Ok(_) => Ok(()),
3039+
Err(err) => Err(err),
3040+
}
3041+
}
3042+
3043+
/// Validate a detection rule.
3044+
pub async fn validate_security_monitoring_rule_with_http_info(
3045+
&self,
3046+
body: crate::datadogV2::model::SecurityMonitoringRuleCreatePayload,
3047+
) -> Result<ResponseContent<()>, Error<ValidateSecurityMonitoringRuleError>> {
3048+
let local_configuration = &self.config;
3049+
let operation_id = "v2.validate_security_monitoring_rule";
3050+
3051+
let local_client = &self.client;
3052+
3053+
let local_uri_str = format!(
3054+
"{}/api/v2/security_monitoring/rules/validation",
3055+
local_configuration.get_operation_host(operation_id)
3056+
);
3057+
let mut local_req_builder =
3058+
local_client.request(reqwest::Method::POST, local_uri_str.as_str());
3059+
3060+
// build user agent
3061+
local_req_builder = local_req_builder.header(
3062+
reqwest::header::USER_AGENT,
3063+
local_configuration.user_agent.clone(),
3064+
);
3065+
3066+
// build auth
3067+
if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
3068+
local_req_builder = local_req_builder.header("DD-API-KEY", &local_key.key);
3069+
};
3070+
if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
3071+
local_req_builder = local_req_builder.header("DD-APPLICATION-KEY", &local_key.key);
3072+
};
3073+
3074+
// build body parameters
3075+
let output = Vec::new();
3076+
let mut ser = serde_json::Serializer::with_formatter(output, DDFormatter);
3077+
if body.serialize(&mut ser).is_ok() {
3078+
local_req_builder = local_req_builder.body(ser.into_inner());
3079+
}
3080+
3081+
let local_req = local_req_builder.build()?;
3082+
let local_resp = local_client.execute(local_req).await?;
3083+
3084+
let local_status = local_resp.status();
3085+
let local_content = local_resp.text().await?;
3086+
3087+
if !local_status.is_client_error() && !local_status.is_server_error() {
3088+
Ok(ResponseContent {
3089+
status: local_status,
3090+
content: local_content,
3091+
entity: None,
3092+
})
3093+
} else {
3094+
let local_entity: Option<ValidateSecurityMonitoringRuleError> =
3095+
serde_json::from_str(&local_content).ok();
3096+
let local_error = ResponseContent {
3097+
status: local_status,
3098+
content: local_content,
3099+
entity: local_entity,
3100+
};
3101+
Err(Error::ResponseError(local_error))
3102+
}
3103+
}
30183104
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2024-03-27T16:23:09.814Z
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"http_interactions": [
3+
{
4+
"request": {
5+
"body": {
6+
"string": "{\"cases\":[{\"condition\":\"a > 0\",\"name\":\"\",\"notifications\":[],\"status\":\"info\"}],\"hasExtendedTitle\":true,\"isEnabled\":true,\"message\":\"My security monitoring rule\",\"name\":\"My security monitoring rule\",\"options\":{\"detectionMethod\":\"threshold\",\"evaluationWindow\":1800,\"keepAlive\":999999,\"maxSignalDuration\":1800},\"queries\":[{\"aggregation\":\"count\",\"distinctFields\":[],\"groupByFields\":[\"@userIdentity.assumed_role\"],\"name\":\"\",\"query\":\"source:source_here\"}],\"tags\":[\"env:prod\",\"team:security\"],\"type\":\"log_detection\"}",
7+
"encoding": null
8+
},
9+
"headers": {
10+
"Accept": [
11+
"*/*"
12+
],
13+
"Content-Type": [
14+
"application/json"
15+
]
16+
},
17+
"method": "post",
18+
"uri": "https://api.datadoghq.com/api/v2/security_monitoring/rules/validation"
19+
},
20+
"response": {
21+
"body": {
22+
"string": "{\"error\":{\"code\":\"InvalidArgument\",\"message\":\"Invalid rule configuration\",\"details\":[{\"code\":\"InvalidArgument\",\"message\":\"Max signal duration must be greater than or equal to keep alive\",\"target\":\"maxSignalDuration\"},{\"code\":\"InvalidArgument\",\"message\":\"Keep alive is not in allowed durations: 0, 1, 5, 10, 15, 30, 60, 120, 180, 360 (in minutes)\",\"target\":\"keepAlive\"}]}}\n",
23+
"encoding": null
24+
},
25+
"headers": {
26+
"Content-Type": [
27+
"application/json"
28+
]
29+
},
30+
"status": {
31+
"code": 400,
32+
"message": "Bad Request"
33+
}
34+
},
35+
"recorded_at": "Wed, 27 Mar 2024 16:23:09 GMT"
36+
}
37+
],
38+
"recorded_with": "VCR 6.0.0"
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2024-03-27T16:23:10.157Z
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"http_interactions": [
3+
{
4+
"request": {
5+
"body": {
6+
"string": "{\"cases\":[{\"condition\":\"a > 0\",\"name\":\"\",\"notifications\":[],\"status\":\"info\"}],\"hasExtendedTitle\":true,\"isEnabled\":true,\"message\":\"My security monitoring rule\",\"name\":\"My security monitoring rule\",\"options\":{\"detectionMethod\":\"threshold\",\"evaluationWindow\":1800,\"keepAlive\":1800,\"maxSignalDuration\":1800},\"queries\":[{\"aggregation\":\"count\",\"distinctFields\":[],\"groupByFields\":[\"@userIdentity.assumed_role\"],\"name\":\"\",\"query\":\"source:source_here\"}],\"tags\":[\"env:prod\",\"team:security\"],\"type\":\"log_detection\"}",
7+
"encoding": null
8+
},
9+
"headers": {
10+
"Accept": [
11+
"*/*"
12+
],
13+
"Content-Type": [
14+
"application/json"
15+
]
16+
},
17+
"method": "post",
18+
"uri": "https://api.datadoghq.com/api/v2/security_monitoring/rules/validation"
19+
},
20+
"response": {
21+
"body": {
22+
"string": "",
23+
"encoding": null
24+
},
25+
"headers": {
26+
"Content-Type": [
27+
"text/html; charset=utf-8"
28+
]
29+
},
30+
"status": {
31+
"code": 204,
32+
"message": "No Content"
33+
}
34+
},
35+
"recorded_at": "Wed, 27 Mar 2024 16:23:10 GMT"
36+
}
37+
],
38+
"recorded_with": "VCR 6.0.0"
39+
}

tests/scenarios/features/v2/security_monitoring.feature

+14
Original file line numberDiff line numberDiff line change
@@ -606,3 +606,17 @@ Feature: Security Monitoring
606606
Then the response status is 200 OK
607607
And the response "name" is equal to "{{ unique }}-Updated"
608608
And the response "id" has the same value as "security_rule.id"
609+
610+
@skip-go @skip-java @skip-python @skip-ruby @skip-rust @skip-typescript @skip-validation @team:DataDog/k9-cloud-security-platform
611+
Scenario: Validate a detection rule returns "Bad Request" response
612+
Given new "ValidateSecurityMonitoringRule" request
613+
And body with value {"cases":[{"name":"","status":"info","notifications":[],"condition":"a > 0"}],"hasExtendedTitle":true,"isEnabled":true,"message":"My security monitoring rule","name":"My security monitoring rule","options":{"evaluationWindow":1800,"keepAlive":999999,"maxSignalDuration":1800,"detectionMethod":"threshold"},"queries":[{"query":"source:source_here","groupByFields":["@userIdentity.assumed_role"],"distinctFields":[],"aggregation":"count","name":""}],"tags":["env:prod","team:security"],"type":"log_detection"}
614+
When the request is sent
615+
Then the response status is 400 Bad Request
616+
617+
@team:DataDog/k9-cloud-security-platform
618+
Scenario: Validate a detection rule returns "OK" response
619+
Given new "ValidateSecurityMonitoringRule" request
620+
And body with value {"cases":[{"name":"","status":"info","notifications":[],"condition":"a > 0"}],"hasExtendedTitle":true,"isEnabled":true,"message":"My security monitoring rule","name":"My security monitoring rule","options":{"evaluationWindow":1800,"keepAlive":1800,"maxSignalDuration":1800,"detectionMethod":"threshold"},"queries":[{"query":"source:source_here","groupByFields":["@userIdentity.assumed_role"],"distinctFields":[],"aggregation":"count","name":""}],"tags":["env:prod","team:security"],"type":"log_detection"}
621+
When the request is sent
622+
Then the response status is 204 OK

tests/scenarios/features/v2/undo.json

+6
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,12 @@
17511751
"type": "unsafe"
17521752
}
17531753
},
1754+
"ValidateSecurityMonitoringRule": {
1755+
"tag": "Security Monitoring",
1756+
"undo": {
1757+
"type": "idempotent"
1758+
}
1759+
},
17541760
"DeleteSecurityMonitoringRule": {
17551761
"tag": "Security Monitoring",
17561762
"undo": {

tests/scenarios/function_mappings.rs

+32
Original file line numberDiff line numberDiff line change
@@ -2347,6 +2347,10 @@ pub fn collect_function_calls(world: &mut DatadogWorld) {
23472347
"v2.CreateSecurityMonitoringRule".into(),
23482348
test_v2_create_security_monitoring_rule,
23492349
);
2350+
world.function_mappings.insert(
2351+
"v2.ValidateSecurityMonitoringRule".into(),
2352+
test_v2_validate_security_monitoring_rule,
2353+
);
23502354
world.function_mappings.insert(
23512355
"v2.DeleteSecurityMonitoringRule".into(),
23522356
test_v2_delete_security_monitoring_rule,
@@ -17660,6 +17664,34 @@ fn test_v2_create_security_monitoring_rule(
1766017664
world.response.code = response.status.as_u16();
1766117665
}
1766217666

17667+
fn test_v2_validate_security_monitoring_rule(
17668+
world: &mut DatadogWorld,
17669+
_parameters: &HashMap<String, Value>,
17670+
) {
17671+
let api = world
17672+
.api_instances
17673+
.v2_api_security_monitoring
17674+
.as_ref()
17675+
.expect("api instance not found");
17676+
let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap();
17677+
let response = match block_on(api.validate_security_monitoring_rule_with_http_info(body)) {
17678+
Ok(response) => response,
17679+
Err(error) => {
17680+
return match error {
17681+
Error::ResponseError(e) => {
17682+
world.response.code = e.status.as_u16();
17683+
if let Some(entity) = e.entity {
17684+
world.response.object = serde_json::to_value(entity).unwrap();
17685+
}
17686+
}
17687+
_ => panic!("error parsing response: {error}"),
17688+
};
17689+
}
17690+
};
17691+
world.response.object = serde_json::to_value(response.entity).unwrap();
17692+
world.response.code = response.status.as_u16();
17693+
}
17694+
1766317695
fn test_v2_delete_security_monitoring_rule(
1766417696
world: &mut DatadogWorld,
1766517697
_parameters: &HashMap<String, Value>,

0 commit comments

Comments
 (0)