Skip to content

Commit 46b3864

Browse files
api-clients-generation-pipeline[bot]ci.datadog-api-spec
andauthored
Security Monitoring - Validation Endpoint for Suppressions (#2740)
Co-authored-by: ci.datadog-api-spec <[email protected]>
1 parent c1624c0 commit 46b3864

File tree

10 files changed

+327
-0
lines changed

10 files changed

+327
-0
lines changed

.generator/schemas/v2/openapi.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64816,6 +64816,38 @@ paths:
6481664816
summary: Get suppressions affecting a specific rule
6481764817
tags:
6481864818
- Security Monitoring
64819+
/api/v2/security_monitoring/configuration/suppressions/validation:
64820+
post:
64821+
description: Validate a suppression rule.
64822+
operationId: ValidateSecurityMonitoringSuppression
64823+
requestBody:
64824+
content:
64825+
application/json:
64826+
schema:
64827+
$ref: '#/components/schemas/SecurityMonitoringSuppressionUpdateRequest'
64828+
required: true
64829+
responses:
64830+
'204':
64831+
description: OK
64832+
'400':
64833+
$ref: '#/components/responses/BadRequestResponse'
64834+
'403':
64835+
$ref: '#/components/responses/NotAuthorizedResponse'
64836+
'429':
64837+
$ref: '#/components/responses/TooManyRequestsResponse'
64838+
security:
64839+
- apiKeyAuth: []
64840+
appKeyAuth: []
64841+
- AuthZ:
64842+
- security_monitoring_suppressions_write
64843+
summary: Validate a suppression rule
64844+
tags:
64845+
- Security Monitoring
64846+
x-codegen-request-body-name: body
64847+
x-permission:
64848+
operator: OR
64849+
permissions:
64850+
- security_monitoring_suppressions_write
6481964851
/api/v2/security_monitoring/configuration/suppressions/{suppression_id}:
6482064852
delete:
6482164853
description: Delete a specific suppression rule.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"2025-09-01T21:36:42.334Z"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"log": {
3+
"_recordingName": "Security Monitoring/Validate a suppression rule returns \"Bad Request\" response",
4+
"creator": {
5+
"comment": "persister:fs",
6+
"name": "Polly.JS",
7+
"version": "6.0.5"
8+
},
9+
"entries": [
10+
{
11+
"_id": "703e6a45408a1cf4017d6f3d7e7b26c9",
12+
"_order": 0,
13+
"cache": {},
14+
"request": {
15+
"bodySize": 94,
16+
"cookies": [],
17+
"headers": [
18+
{
19+
"_fromType": "array",
20+
"name": "accept",
21+
"value": "*/*"
22+
},
23+
{
24+
"_fromType": "array",
25+
"name": "content-type",
26+
"value": "application/json"
27+
}
28+
],
29+
"headersSize": 615,
30+
"httpVersion": "HTTP/1.1",
31+
"method": "POST",
32+
"postData": {
33+
"mimeType": "application/json",
34+
"params": [],
35+
"text": "{\"data\":{\"attributes\":{\"data_exclusion_query\":\"not enough attributes\"},\"type\":\"suppressions\"}}"
36+
},
37+
"queryString": [],
38+
"url": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions/validation"
39+
},
40+
"response": {
41+
"bodySize": 204,
42+
"content": {
43+
"mimeType": "application/json",
44+
"size": 204,
45+
"text": "{\"errors\":[\"input_validation_error(Field 'data.attributes.rule_query' is invalid: field 'rule_query' is required)\",\"input_validation_error(Field 'data.attributes.name' is invalid: name cannot be empty)\"]}"
46+
},
47+
"cookies": [],
48+
"headers": [
49+
{
50+
"name": "content-type",
51+
"value": "application/json"
52+
}
53+
],
54+
"headersSize": 654,
55+
"httpVersion": "HTTP/1.1",
56+
"redirectURL": "",
57+
"status": 400,
58+
"statusText": "Bad Request"
59+
},
60+
"startedDateTime": "2025-09-01T21:36:42.339Z",
61+
"time": 423
62+
}
63+
],
64+
"pages": [],
65+
"version": "1.2"
66+
}
67+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"2025-09-01T21:36:20.593Z"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"log": {
3+
"_recordingName": "Security Monitoring/Validate a suppression rule returns \"OK\" response",
4+
"creator": {
5+
"comment": "persister:fs",
6+
"name": "Polly.JS",
7+
"version": "6.0.5"
8+
},
9+
"entries": [
10+
{
11+
"_id": "7fdeeb56d69b7809c8f48bdeffca83e0",
12+
"_order": 0,
13+
"cache": {},
14+
"request": {
15+
"bodySize": 285,
16+
"cookies": [],
17+
"headers": [
18+
{
19+
"_fromType": "array",
20+
"name": "accept",
21+
"value": "*/*"
22+
},
23+
{
24+
"_fromType": "array",
25+
"name": "content-type",
26+
"value": "application/json"
27+
}
28+
],
29+
"headersSize": 616,
30+
"httpVersion": "HTTP/1.1",
31+
"method": "POST",
32+
"postData": {
33+
"mimeType": "application/json",
34+
"params": [],
35+
"text": "{\"data\":{\"attributes\":{\"data_exclusion_query\":\"source:cloudtrail account_id:12345\",\"description\":\"This rule suppresses low-severity signals in staging environments.\",\"enabled\":true,\"name\":\"Custom suppression\",\"rule_query\":\"type:log_detection source:cloudtrail\"},\"type\":\"suppressions\"}}"
36+
},
37+
"queryString": [],
38+
"url": "https://api.datadoghq.com/api/v2/security_monitoring/configuration/suppressions/validation"
39+
},
40+
"response": {
41+
"bodySize": 0,
42+
"content": {
43+
"mimeType": "text/plain",
44+
"size": 0
45+
},
46+
"cookies": [],
47+
"headers": [],
48+
"headersSize": 601,
49+
"httpVersion": "HTTP/1.1",
50+
"redirectURL": "",
51+
"status": 204,
52+
"statusText": "No Content"
53+
},
54+
"startedDateTime": "2025-09-01T21:36:20.597Z",
55+
"time": 501
56+
}
57+
],
58+
"pages": [],
59+
"version": "1.2"
60+
}
61+
}

features/v2/security_monitoring.feature

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,3 +1389,17 @@ Feature: Security Monitoring
13891389
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"}
13901390
When the request is sent
13911391
Then the response status is 204 OK
1392+
1393+
@team:DataDog/k9-cloud-security-platform
1394+
Scenario: Validate a suppression rule returns "Bad Request" response
1395+
Given new "ValidateSecurityMonitoringSuppression" request
1396+
And body with value {"data": {"attributes": {"data_exclusion_query": "not enough attributes"}, "type": "suppressions"}}
1397+
When the request is sent
1398+
Then the response status is 400 Bad Request
1399+
1400+
@team:DataDog/k9-cloud-security-platform
1401+
Scenario: Validate a suppression rule returns "OK" response
1402+
Given new "ValidateSecurityMonitoringSuppression" request
1403+
And body with value {"data": {"attributes": {"data_exclusion_query": "source:cloudtrail account_id:12345", "description": "This rule suppresses low-severity signals in staging environments.", "enabled": true, "name": "Custom suppression", "rule_query": "type:log_detection source:cloudtrail"}, "type": "suppressions"}}
1404+
When the request is sent
1405+
Then the response status is 204 OK

features/v2/undo.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,6 +3024,12 @@
30243024
"type": "safe"
30253025
}
30263026
},
3027+
"ValidateSecurityMonitoringSuppression": {
3028+
"tag": "Security Monitoring",
3029+
"undo": {
3030+
"type": "idempotent"
3031+
}
3032+
},
30273033
"DeleteSecurityMonitoringSuppression": {
30283034
"tag": "Security Monitoring",
30293035
"undo": {

private/bdd_runner/src/support/scenarios_model_mapping.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3905,6 +3905,13 @@ export const ScenariosModelMappings: { [key: string]: OperationMapping } = {
39053905
},
39063906
operationResponseType: "SecurityMonitoringSuppressionsResponse",
39073907
},
3908+
"SecurityMonitoringApi.V2.ValidateSecurityMonitoringSuppression": {
3909+
body: {
3910+
type: "SecurityMonitoringSuppressionUpdateRequest",
3911+
format: "",
3912+
},
3913+
operationResponseType: "{}",
3914+
},
39083915
"SecurityMonitoringApi.V2.GetSecurityMonitoringSuppression": {
39093916
suppressionId: {
39103917
type: "string",

services/security_monitoring/src/v2/SecurityMonitoringApi.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3919,6 +3919,63 @@ export class SecurityMonitoringApiRequestFactory extends BaseAPIRequestFactory {
39193919

39203920
return requestContext;
39213921
}
3922+
3923+
public async validateSecurityMonitoringSuppression(
3924+
body: SecurityMonitoringSuppressionUpdateRequest,
3925+
_options?: Configuration,
3926+
): Promise<RequestContext> {
3927+
const _config = _options || this.configuration;
3928+
3929+
// verify required parameter 'body' is not null or undefined
3930+
if (body === null || body === undefined) {
3931+
throw new RequiredError("body", "validateSecurityMonitoringSuppression");
3932+
}
3933+
3934+
// Path Params
3935+
const localVarPath =
3936+
"/api/v2/security_monitoring/configuration/suppressions/validation";
3937+
3938+
// Make Request Context
3939+
const { server, overrides } = _config.getServerAndOverrides(
3940+
"SecurityMonitoringApi.v2.validateSecurityMonitoringSuppression",
3941+
SecurityMonitoringApi.operationServers,
3942+
);
3943+
const requestContext = server.makeRequestContext(
3944+
localVarPath,
3945+
HttpMethod.POST,
3946+
overrides,
3947+
);
3948+
requestContext.setHeaderParam("Accept", "*/*");
3949+
requestContext.setHttpConfig(_config.httpConfig);
3950+
3951+
// Set User-Agent
3952+
if (this.userAgent) {
3953+
requestContext.setHeaderParam("User-Agent", this.userAgent);
3954+
}
3955+
3956+
// Body Params
3957+
const contentType = getPreferredMediaType(["application/json"]);
3958+
requestContext.setHeaderParam("Content-Type", contentType);
3959+
const serializedBody = stringify(
3960+
serialize(
3961+
body,
3962+
TypingInfo,
3963+
"SecurityMonitoringSuppressionUpdateRequest",
3964+
"",
3965+
),
3966+
contentType,
3967+
);
3968+
requestContext.setBody(serializedBody);
3969+
3970+
// Apply auth methods
3971+
applySecurityAuthentication(_config, requestContext, [
3972+
"apiKeyAuth",
3973+
"appKeyAuth",
3974+
"AuthZ",
3975+
]);
3976+
3977+
return requestContext;
3978+
}
39223979
}
39233980

39243981
export class SecurityMonitoringApiResponseProcessor {
@@ -7440,6 +7497,55 @@ export class SecurityMonitoringApiResponseProcessor {
74407497
'Unknown API Status Code!\nBody: "' + body + '"',
74417498
);
74427499
}
7500+
7501+
/**
7502+
* Unwraps the actual response sent by the server from the response context and deserializes the response content
7503+
* to the expected objects
7504+
*
7505+
* @params response Response returned by the server for a request to validateSecurityMonitoringSuppression
7506+
* @throws ApiException if the response code was not in [200, 299]
7507+
*/
7508+
public async validateSecurityMonitoringSuppression(
7509+
response: ResponseContext,
7510+
): Promise<void> {
7511+
const contentType = normalizeMediaType(response.headers["content-type"]);
7512+
if (response.httpStatusCode === 204) {
7513+
return;
7514+
}
7515+
if (
7516+
response.httpStatusCode === 400 ||
7517+
response.httpStatusCode === 403 ||
7518+
response.httpStatusCode === 429
7519+
) {
7520+
const bodyText = parse(await response.body.text(), contentType);
7521+
let body: APIErrorResponse;
7522+
try {
7523+
body = deserialize(
7524+
bodyText,
7525+
TypingInfo,
7526+
"APIErrorResponse",
7527+
) as APIErrorResponse;
7528+
} catch (error) {
7529+
logger.debug(`Got error deserializing error: ${error}`);
7530+
throw new ApiException<APIErrorResponse>(
7531+
response.httpStatusCode,
7532+
bodyText,
7533+
);
7534+
}
7535+
throw new ApiException<APIErrorResponse>(response.httpStatusCode, body);
7536+
}
7537+
7538+
// Work around for missing responses in specification, e.g. for petstore.yaml
7539+
if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) {
7540+
return;
7541+
}
7542+
7543+
const body = (await response.body.text()) || "";
7544+
throw new ApiException<string>(
7545+
response.httpStatusCode,
7546+
'Unknown API Status Code!\nBody: "' + body + '"',
7547+
);
7548+
}
74437549
}
74447550

74457551
export interface SecurityMonitoringApiCancelHistoricalJobRequest {
@@ -8405,6 +8511,13 @@ export interface SecurityMonitoringApiValidateSecurityMonitoringRuleRequest {
84058511
body: SecurityMonitoringRuleValidatePayload;
84068512
}
84078513

8514+
export interface SecurityMonitoringApiValidateSecurityMonitoringSuppressionRequest {
8515+
/**
8516+
* @type SecurityMonitoringSuppressionUpdateRequest
8517+
*/
8518+
body: SecurityMonitoringSuppressionUpdateRequest;
8519+
}
8520+
84088521
export class SecurityMonitoringApi {
84098522
private requestFactory: SecurityMonitoringApiRequestFactory;
84108523
private responseProcessor: SecurityMonitoringApiResponseProcessor;
@@ -10149,4 +10262,28 @@ export class SecurityMonitoringApi {
1014910262
});
1015010263
});
1015110264
}
10265+
10266+
/**
10267+
* Validate a suppression rule.
10268+
* @param param The request object
10269+
*/
10270+
public validateSecurityMonitoringSuppression(
10271+
param: SecurityMonitoringApiValidateSecurityMonitoringSuppressionRequest,
10272+
options?: Configuration,
10273+
): Promise<void> {
10274+
const requestContextPromise =
10275+
this.requestFactory.validateSecurityMonitoringSuppression(
10276+
param.body,
10277+
options,
10278+
);
10279+
return requestContextPromise.then((requestContext) => {
10280+
return this.configuration.httpApi
10281+
.send(requestContext)
10282+
.then((responseContext) => {
10283+
return this.responseProcessor.validateSecurityMonitoringSuppression(
10284+
responseContext,
10285+
);
10286+
});
10287+
});
10288+
}
1015210289
}

services/security_monitoring/src/v2/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export {
5353
SecurityMonitoringApiUpdateSecurityMonitoringRuleRequest,
5454
SecurityMonitoringApiUpdateSecurityMonitoringSuppressionRequest,
5555
SecurityMonitoringApiValidateSecurityMonitoringRuleRequest,
56+
SecurityMonitoringApiValidateSecurityMonitoringSuppressionRequest,
5657
SecurityMonitoringApi,
5758
} from "./SecurityMonitoringApi";
5859

0 commit comments

Comments
 (0)