Skip to content

Commit a256551

Browse files
api-clients-generation-pipeline[bot]ci.datadog-api-spec
andauthored
Add SDS rule should_save_match field (#872)
Co-authored-by: ci.datadog-api-spec <[email protected]>
1 parent b4c0ea5 commit a256551

File tree

7 files changed

+260
-2
lines changed

7 files changed

+260
-2
lines changed

.generated-info

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"spec_repo_commit": "98e3371",
3-
"generated": "2025-08-27 08:50:12.332"
2+
"spec_repo_commit": "62a19e4",
3+
"generated": "2025-08-27 15:06:21.847"
44
}

.generator/schemas/v2/openapi.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39445,6 +39445,12 @@ components:
3944539445
replacement_string:
3944639446
description: Required if type == 'replacement_string'.
3944739447
type: string
39448+
should_save_match:
39449+
description: "Only valid when type == `replacement_string`. When enabled,
39450+
matches can be unmasked in logs by users with \u2018Data Scanner Unmask\u2019
39451+
permission. As a security best practice, avoid masking for highly-sensitive,
39452+
long-lived data."
39453+
type: boolean
3944839454
type:
3944939455
$ref: '#/components/schemas/SensitiveDataScannerTextReplacementType'
3945039456
type: object
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Create Scanning Rule with should_save_match returns "OK" response
2+
use datadog_api_client::datadog;
3+
use datadog_api_client::datadogV2::api_sensitive_data_scanner::SensitiveDataScannerAPI;
4+
use datadog_api_client::datadogV2::model::SensitiveDataScannerGroup;
5+
use datadog_api_client::datadogV2::model::SensitiveDataScannerGroupData;
6+
use datadog_api_client::datadogV2::model::SensitiveDataScannerGroupType;
7+
use datadog_api_client::datadogV2::model::SensitiveDataScannerMetaVersionOnly;
8+
use datadog_api_client::datadogV2::model::SensitiveDataScannerRuleAttributes;
9+
use datadog_api_client::datadogV2::model::SensitiveDataScannerRuleCreate;
10+
use datadog_api_client::datadogV2::model::SensitiveDataScannerRuleCreateRequest;
11+
use datadog_api_client::datadogV2::model::SensitiveDataScannerRuleRelationships;
12+
use datadog_api_client::datadogV2::model::SensitiveDataScannerRuleType;
13+
use datadog_api_client::datadogV2::model::SensitiveDataScannerTextReplacement;
14+
use datadog_api_client::datadogV2::model::SensitiveDataScannerTextReplacementType;
15+
16+
#[tokio::main]
17+
async fn main() {
18+
// there is a valid "scanning_group" in the system
19+
let group_data_id = std::env::var("GROUP_DATA_ID").unwrap();
20+
let body = SensitiveDataScannerRuleCreateRequest::new(
21+
SensitiveDataScannerRuleCreate::new(
22+
SensitiveDataScannerRuleAttributes::new()
23+
.is_enabled(true)
24+
.name("Example-Sensitive-Data-Scanner".to_string())
25+
.pattern("pattern".to_string())
26+
.priority(1)
27+
.tags(vec!["sensitive_data:true".to_string()])
28+
.text_replacement(
29+
SensitiveDataScannerTextReplacement::new()
30+
.replacement_string("REDACTED".to_string())
31+
.should_save_match(true)
32+
.type_(SensitiveDataScannerTextReplacementType::REPLACEMENT_STRING),
33+
),
34+
SensitiveDataScannerRuleRelationships::new().group(
35+
SensitiveDataScannerGroupData::new().data(
36+
SensitiveDataScannerGroup::new()
37+
.id(group_data_id.clone())
38+
.type_(SensitiveDataScannerGroupType::SENSITIVE_DATA_SCANNER_GROUP),
39+
),
40+
),
41+
SensitiveDataScannerRuleType::SENSITIVE_DATA_SCANNER_RULE,
42+
),
43+
SensitiveDataScannerMetaVersionOnly::new(),
44+
);
45+
let configuration = datadog::Configuration::new();
46+
let api = SensitiveDataScannerAPI::with_config(configuration);
47+
let resp = api.create_scanning_rule(body).await;
48+
if let Ok(value) = resp {
49+
println!("{:#?}", value);
50+
} else {
51+
println!("{:#?}", resp.unwrap_err());
52+
}
53+
}

src/datadogV2/model/model_sensitive_data_scanner_text_replacement.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub struct SensitiveDataScannerTextReplacement {
1818
/// Required if type == 'replacement_string'.
1919
#[serde(rename = "replacement_string")]
2020
pub replacement_string: Option<String>,
21+
/// Only valid when type == `replacement_string`. When enabled, matches can be unmasked in logs by users with ‘Data Scanner Unmask’ permission. As a security best practice, avoid masking for highly-sensitive, long-lived data.
22+
#[serde(rename = "should_save_match")]
23+
pub should_save_match: Option<bool>,
2124
/// Type of the replacement text. None means no replacement.
2225
/// hash means the data will be stubbed. replacement_string means that
2326
/// one can chose a text to replace the data. partial_replacement_from_beginning
@@ -38,6 +41,7 @@ impl SensitiveDataScannerTextReplacement {
3841
SensitiveDataScannerTextReplacement {
3942
number_of_chars: None,
4043
replacement_string: None,
44+
should_save_match: None,
4145
type_: None,
4246
additional_properties: std::collections::BTreeMap::new(),
4347
_unparsed: false,
@@ -54,6 +58,11 @@ impl SensitiveDataScannerTextReplacement {
5458
self
5559
}
5660

61+
pub fn should_save_match(mut self, value: bool) -> Self {
62+
self.should_save_match = Some(value);
63+
self
64+
}
65+
5766
pub fn type_(
5867
mut self,
5968
value: crate::datadogV2::model::SensitiveDataScannerTextReplacementType,
@@ -96,6 +105,7 @@ impl<'de> Deserialize<'de> for SensitiveDataScannerTextReplacement {
96105
{
97106
let mut number_of_chars: Option<i64> = None;
98107
let mut replacement_string: Option<String> = None;
108+
let mut should_save_match: Option<bool> = None;
99109
let mut type_: Option<
100110
crate::datadogV2::model::SensitiveDataScannerTextReplacementType,
101111
> = None;
@@ -121,6 +131,13 @@ impl<'de> Deserialize<'de> for SensitiveDataScannerTextReplacement {
121131
replacement_string =
122132
Some(serde_json::from_value(v).map_err(M::Error::custom)?);
123133
}
134+
"should_save_match" => {
135+
if v.is_null() {
136+
continue;
137+
}
138+
should_save_match =
139+
Some(serde_json::from_value(v).map_err(M::Error::custom)?);
140+
}
124141
"type" => {
125142
if v.is_null() {
126143
continue;
@@ -146,6 +163,7 @@ impl<'de> Deserialize<'de> for SensitiveDataScannerTextReplacement {
146163
let content = SensitiveDataScannerTextReplacement {
147164
number_of_chars,
148165
replacement_string,
166+
should_save_match,
149167
type_,
150168
additional_properties,
151169
_unparsed,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2025-08-26T20:31:44.042Z
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
{
2+
"http_interactions": [
3+
{
4+
"request": {
5+
"body": "",
6+
"headers": {
7+
"Accept": [
8+
"application/json"
9+
]
10+
},
11+
"method": "get",
12+
"uri": "https://api.datadoghq.com/api/v2/sensitive-data-scanner/config"
13+
},
14+
"response": {
15+
"body": {
16+
"string": "{\"data\":{\"id\":\"7957915c634d4dcb581fa154157f5ad9c2947f50be632fb5599862069f4d2d87\",\"attributes\":{},\"type\":\"sensitive_data_scanner_configuration\",\"relationships\":{\"groups\":{\"data\":[]}}},\"meta\":{\"version\":275277,\"count_limit\":250,\"group_count_limit\":20,\"is_pci_compliant\":false,\"has_highlight_enabled\":true,\"has_multi_pass_enabled\":true,\"has_cascading_enabled\":false,\"is_configuration_superseded\":false,\"is_float_sampling_rate_enabled\":false,\"min_sampling_rate\":10.0}}\n",
17+
"encoding": null
18+
},
19+
"headers": {
20+
"Content-Type": [
21+
"application/json"
22+
]
23+
},
24+
"status": {
25+
"code": 200,
26+
"message": "OK"
27+
}
28+
},
29+
"recorded_at": "Tue, 26 Aug 2025 20:31:44 GMT"
30+
},
31+
{
32+
"request": {
33+
"body": {
34+
"string": "{\"data\":{\"attributes\":{\"filter\":{\"query\":\"*\"},\"is_enabled\":false,\"name\":\"my-test-group\",\"product_list\":[\"logs\"],\"samplings\":[{\"product\":\"logs\",\"rate\":100}]},\"relationships\":{\"configuration\":{\"data\":{\"id\":\"7957915c634d4dcb581fa154157f5ad9c2947f50be632fb5599862069f4d2d87\",\"type\":\"sensitive_data_scanner_configuration\"}},\"rules\":{\"data\":[]}},\"type\":\"sensitive_data_scanner_group\"},\"meta\":{}}",
35+
"encoding": null
36+
},
37+
"headers": {
38+
"Accept": [
39+
"application/json"
40+
],
41+
"Content-Type": [
42+
"application/json"
43+
]
44+
},
45+
"method": "post",
46+
"uri": "https://api.datadoghq.com/api/v2/sensitive-data-scanner/config/groups"
47+
},
48+
"response": {
49+
"body": {
50+
"string": "{\"data\":{\"id\":\"18cc2267-f3cc-4c15-917d-d3efb15deb03\",\"attributes\":{\"name\":\"my-test-group\",\"is_enabled\":false,\"filter\":{\"query\":\"*\"},\"product_list\":[\"logs\"],\"samplings\":[{\"product\":\"logs\",\"rate\":100.0}]},\"type\":\"sensitive_data_scanner_group\",\"relationships\":{\"configuration\":{\"data\":{\"id\":\"7957915c634d4dcb581fa154157f5ad9c2947f50be632fb5599862069f4d2d87\",\"type\":\"sensitive_data_scanner_configuration\"}},\"rules\":{\"data\":[]}}},\"meta\":{\"version\":275278}}\n",
51+
"encoding": null
52+
},
53+
"headers": {
54+
"Content-Type": [
55+
"application/json"
56+
]
57+
},
58+
"status": {
59+
"code": 200,
60+
"message": "OK"
61+
}
62+
},
63+
"recorded_at": "Tue, 26 Aug 2025 20:31:44 GMT"
64+
},
65+
{
66+
"request": {
67+
"body": {
68+
"string": "{\"data\":{\"attributes\":{\"is_enabled\":true,\"name\":\"Test-Create_Scanning_Rule_with_should_save_match_returns_OK_response-1756240304\",\"pattern\":\"pattern\",\"priority\":1,\"tags\":[\"sensitive_data:true\"],\"text_replacement\":{\"replacement_string\":\"REDACTED\",\"should_save_match\":true,\"type\":\"replacement_string\"}},\"relationships\":{\"group\":{\"data\":{\"id\":\"18cc2267-f3cc-4c15-917d-d3efb15deb03\",\"type\":\"sensitive_data_scanner_group\"}}},\"type\":\"sensitive_data_scanner_rule\"},\"meta\":{}}",
69+
"encoding": null
70+
},
71+
"headers": {
72+
"Accept": [
73+
"application/json"
74+
],
75+
"Content-Type": [
76+
"application/json"
77+
]
78+
},
79+
"method": "post",
80+
"uri": "https://api.datadoghq.com/api/v2/sensitive-data-scanner/config/rules"
81+
},
82+
"response": {
83+
"body": {
84+
"string": "{\"data\":{\"id\":\"0e517b8a-04c1-4ae0-b57b-22b8e081190c\",\"attributes\":{\"name\":\"Test-Create_Scanning_Rule_with_should_save_match_returns_OK_response-1756240304\",\"namespaces\":[],\"excluded_namespaces\":[],\"pattern\":\"pattern\",\"text_replacement\":{\"replacement_string\":\"REDACTED\",\"should_save_match\":true,\"type\":\"replacement_string\"},\"tags\":[\"sensitive_data:true\"],\"labels\":[],\"is_enabled\":true,\"priority\":1},\"type\":\"sensitive_data_scanner_rule\",\"relationships\":{\"group\":{\"data\":{\"id\":\"18cc2267-f3cc-4c15-917d-d3efb15deb03\",\"type\":\"sensitive_data_scanner_group\"}}}},\"meta\":{\"version\":275279}}\n",
85+
"encoding": null
86+
},
87+
"headers": {
88+
"Content-Type": [
89+
"application/json"
90+
]
91+
},
92+
"status": {
93+
"code": 200,
94+
"message": "OK"
95+
}
96+
},
97+
"recorded_at": "Tue, 26 Aug 2025 20:31:44 GMT"
98+
},
99+
{
100+
"request": {
101+
"body": {
102+
"string": "{\"meta\":{}}",
103+
"encoding": null
104+
},
105+
"headers": {
106+
"Accept": [
107+
"application/json"
108+
],
109+
"Content-Type": [
110+
"application/json"
111+
]
112+
},
113+
"method": "delete",
114+
"uri": "https://api.datadoghq.com/api/v2/sensitive-data-scanner/config/rules/0e517b8a-04c1-4ae0-b57b-22b8e081190c"
115+
},
116+
"response": {
117+
"body": {
118+
"string": "{\"meta\":{\"version\":275280}}\n",
119+
"encoding": null
120+
},
121+
"headers": {
122+
"Content-Type": [
123+
"application/json"
124+
]
125+
},
126+
"status": {
127+
"code": 200,
128+
"message": "OK"
129+
}
130+
},
131+
"recorded_at": "Tue, 26 Aug 2025 20:31:44 GMT"
132+
},
133+
{
134+
"request": {
135+
"body": {
136+
"string": "{\"meta\":{}}",
137+
"encoding": null
138+
},
139+
"headers": {
140+
"Accept": [
141+
"application/json"
142+
],
143+
"Content-Type": [
144+
"application/json"
145+
]
146+
},
147+
"method": "delete",
148+
"uri": "https://api.datadoghq.com/api/v2/sensitive-data-scanner/config/groups/18cc2267-f3cc-4c15-917d-d3efb15deb03"
149+
},
150+
"response": {
151+
"body": {
152+
"string": "{\"meta\":{\"version\":275281}}\n",
153+
"encoding": null
154+
},
155+
"headers": {
156+
"Content-Type": [
157+
"application/json"
158+
]
159+
},
160+
"status": {
161+
"code": 200,
162+
"message": "OK"
163+
}
164+
},
165+
"recorded_at": "Tue, 26 Aug 2025 20:31:44 GMT"
166+
}
167+
],
168+
"recorded_with": "VCR 6.0.0"
169+
}

tests/scenarios/features/v2/sensitive_data_scanner.feature

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ Feature: Sensitive Data Scanner
5050
And the response "data.attributes.included_keyword_configuration.character_count" is equal to 35
5151
And the response "data.attributes.included_keyword_configuration.keywords[0]" is equal to "credit card"
5252

53+
@team:DataDog/sensitive-data-scanner
54+
Scenario: Create Scanning Rule with should_save_match returns "OK" response
55+
Given a valid "configuration" in the system
56+
And there is a valid "scanning_group" in the system
57+
And new "CreateScanningRule" request
58+
And body with value {"meta":{},"data":{"type":"sensitive_data_scanner_rule","attributes":{"name":"{{ unique }}","pattern":"pattern","text_replacement":{"type":"replacement_string","replacement_string":"REDACTED","should_save_match":true},"tags":["sensitive_data:true"],"is_enabled":true,"priority":1},"relationships":{"group":{"data":{"type":"{{ group.data.type }}","id":"{{ group.data.id }}"}}}}}
59+
When the request is sent
60+
Then the response status is 200 OK
61+
And the response "data.type" is equal to "sensitive_data_scanner_rule"
62+
And the response "data.attributes.name" is equal to "{{ unique }}"
63+
5364
@generated @skip @team:DataDog/sensitive-data-scanner
5465
Scenario: Delete Scanning Group returns "Bad Request" response
5566
Given new "DeleteScanningGroup" request

0 commit comments

Comments
 (0)