diff --git a/.generated-info b/.generated-info index 66145f566eb..21bf0d0ca19 100644 --- a/.generated-info +++ b/.generated-info @@ -1,4 +1,4 @@ { - "spec_repo_commit": "98e3371", - "generated": "2025-08-27 08:46:33.176" + "spec_repo_commit": "62a19e4", + "generated": "2025-08-27 15:03:03.841" } diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 7d9ee66a127..e09c9480479 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -39445,6 +39445,12 @@ components: replacement_string: description: Required if type == 'replacement_string'. type: string + should_save_match: + description: "Only valid when type == `replacement_string`. When enabled, + matches can be unmasked in logs by users with \u2018Data Scanner Unmask\u2019 + permission. As a security best practice, avoid masking for highly-sensitive, + long-lived data." + type: boolean type: $ref: '#/components/schemas/SensitiveDataScannerTextReplacementType' type: object diff --git a/examples/v2/sensitive-data-scanner/CreateScanningRule_502667299.java b/examples/v2/sensitive-data-scanner/CreateScanningRule_502667299.java new file mode 100644 index 00000000000..8f67d250bc2 --- /dev/null +++ b/examples/v2/sensitive-data-scanner/CreateScanningRule_502667299.java @@ -0,0 +1,69 @@ +// Create Scanning Rule with should_save_match returns "OK" response + +import com.datadog.api.client.ApiClient; +import com.datadog.api.client.ApiException; +import com.datadog.api.client.v2.api.SensitiveDataScannerApi; +import com.datadog.api.client.v2.model.SensitiveDataScannerCreateRuleResponse; +import com.datadog.api.client.v2.model.SensitiveDataScannerGroup; +import com.datadog.api.client.v2.model.SensitiveDataScannerGroupData; +import com.datadog.api.client.v2.model.SensitiveDataScannerGroupType; +import com.datadog.api.client.v2.model.SensitiveDataScannerMetaVersionOnly; +import com.datadog.api.client.v2.model.SensitiveDataScannerRuleAttributes; +import com.datadog.api.client.v2.model.SensitiveDataScannerRuleCreate; +import com.datadog.api.client.v2.model.SensitiveDataScannerRuleCreateRequest; +import com.datadog.api.client.v2.model.SensitiveDataScannerRuleRelationships; +import com.datadog.api.client.v2.model.SensitiveDataScannerRuleType; +import com.datadog.api.client.v2.model.SensitiveDataScannerTextReplacement; +import com.datadog.api.client.v2.model.SensitiveDataScannerTextReplacementType; +import java.util.Collections; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = ApiClient.getDefaultApiClient(); + SensitiveDataScannerApi apiInstance = new SensitiveDataScannerApi(defaultClient); + + // there is a valid "scanning_group" in the system + String GROUP_DATA_ID = System.getenv("GROUP_DATA_ID"); + + SensitiveDataScannerRuleCreateRequest body = + new SensitiveDataScannerRuleCreateRequest() + .meta(new SensitiveDataScannerMetaVersionOnly()) + .data( + new SensitiveDataScannerRuleCreate() + .type(SensitiveDataScannerRuleType.SENSITIVE_DATA_SCANNER_RULE) + .attributes( + new SensitiveDataScannerRuleAttributes() + .name("Example-Sensitive-Data-Scanner") + .pattern("pattern") + .textReplacement( + new SensitiveDataScannerTextReplacement() + .type( + SensitiveDataScannerTextReplacementType.REPLACEMENT_STRING) + .replacementString("REDACTED") + .shouldSaveMatch(true)) + .tags(Collections.singletonList("sensitive_data:true")) + .isEnabled(true) + .priority(1L)) + .relationships( + new SensitiveDataScannerRuleRelationships() + .group( + new SensitiveDataScannerGroupData() + .data( + new SensitiveDataScannerGroup() + .type( + SensitiveDataScannerGroupType + .SENSITIVE_DATA_SCANNER_GROUP) + .id(GROUP_DATA_ID))))); + + try { + SensitiveDataScannerCreateRuleResponse result = apiInstance.createScanningRule(body); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling SensitiveDataScannerApi#createScanningRule"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/datadog/api/client/v2/model/SensitiveDataScannerTextReplacement.java b/src/main/java/com/datadog/api/client/v2/model/SensitiveDataScannerTextReplacement.java index b6bde7aff04..91822de437d 100644 --- a/src/main/java/com/datadog/api/client/v2/model/SensitiveDataScannerTextReplacement.java +++ b/src/main/java/com/datadog/api/client/v2/model/SensitiveDataScannerTextReplacement.java @@ -20,6 +20,7 @@ @JsonPropertyOrder({ SensitiveDataScannerTextReplacement.JSON_PROPERTY_NUMBER_OF_CHARS, SensitiveDataScannerTextReplacement.JSON_PROPERTY_REPLACEMENT_STRING, + SensitiveDataScannerTextReplacement.JSON_PROPERTY_SHOULD_SAVE_MATCH, SensitiveDataScannerTextReplacement.JSON_PROPERTY_TYPE }) @jakarta.annotation.Generated( @@ -32,6 +33,9 @@ public class SensitiveDataScannerTextReplacement { public static final String JSON_PROPERTY_REPLACEMENT_STRING = "replacement_string"; private String replacementString; + public static final String JSON_PROPERTY_SHOULD_SAVE_MATCH = "should_save_match"; + private Boolean shouldSaveMatch; + public static final String JSON_PROPERTY_TYPE = "type"; private SensitiveDataScannerTextReplacementType type = SensitiveDataScannerTextReplacementType.NONE; @@ -79,6 +83,29 @@ public void setReplacementString(String replacementString) { this.replacementString = replacementString; } + public SensitiveDataScannerTextReplacement shouldSaveMatch(Boolean shouldSaveMatch) { + this.shouldSaveMatch = shouldSaveMatch; + return this; + } + + /** + * 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. + * + * @return shouldSaveMatch + */ + @jakarta.annotation.Nullable + @JsonProperty(JSON_PROPERTY_SHOULD_SAVE_MATCH) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Boolean getShouldSaveMatch() { + return shouldSaveMatch; + } + + public void setShouldSaveMatch(Boolean shouldSaveMatch) { + this.shouldSaveMatch = shouldSaveMatch; + } + public SensitiveDataScannerTextReplacement type(SensitiveDataScannerTextReplacementType type) { this.type = type; this.unparsed |= !type.isValid(); @@ -168,6 +195,7 @@ public boolean equals(Object o) { return Objects.equals(this.numberOfChars, sensitiveDataScannerTextReplacement.numberOfChars) && Objects.equals( this.replacementString, sensitiveDataScannerTextReplacement.replacementString) + && Objects.equals(this.shouldSaveMatch, sensitiveDataScannerTextReplacement.shouldSaveMatch) && Objects.equals(this.type, sensitiveDataScannerTextReplacement.type) && Objects.equals( this.additionalProperties, sensitiveDataScannerTextReplacement.additionalProperties); @@ -175,7 +203,8 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(numberOfChars, replacementString, type, additionalProperties); + return Objects.hash( + numberOfChars, replacementString, shouldSaveMatch, type, additionalProperties); } @Override @@ -184,6 +213,7 @@ public String toString() { sb.append("class SensitiveDataScannerTextReplacement {\n"); sb.append(" numberOfChars: ").append(toIndentedString(numberOfChars)).append("\n"); sb.append(" replacementString: ").append(toIndentedString(replacementString)).append("\n"); + sb.append(" shouldSaveMatch: ").append(toIndentedString(shouldSaveMatch)).append("\n"); sb.append(" type: ").append(toIndentedString(type)).append("\n"); sb.append(" additionalProperties: ") .append(toIndentedString(additionalProperties)) diff --git a/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.freeze b/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.freeze new file mode 100644 index 00000000000..5834759fc6e --- /dev/null +++ b/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.freeze @@ -0,0 +1 @@ +2025-08-26T20:31:44.042Z \ No newline at end of file diff --git a/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.json b/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.json new file mode 100644 index 00000000000..30d02f14597 --- /dev/null +++ b/src/test/resources/cassettes/features/v2/Create_Scanning_Rule_with_should_save_match_returns_OK_response.json @@ -0,0 +1,148 @@ +[ + { + "httpRequest": { + "headers": {}, + "method": "GET", + "path": "/api/v2/sensitive-data-scanner/config", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"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", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "01611a93-5e74-0630-3c51-f707c3b51e80" + }, + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"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\":{}}" + }, + "headers": {}, + "method": "POST", + "path": "/api/v2/sensitive-data-scanner/config/groups", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"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", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa3" + }, + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"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\":{}}" + }, + "headers": {}, + "method": "POST", + "path": "/api/v2/sensitive-data-scanner/config/rules", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"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", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "d5695745-b55e-b2d2-1a8b-ce6beb63aa60" + }, + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"meta\":{}}" + }, + "headers": {}, + "method": "DELETE", + "path": "/api/v2/sensitive-data-scanner/config/rules/0e517b8a-04c1-4ae0-b57b-22b8e081190c", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"meta\":{\"version\":275280}}\n", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "9cb4750b-c912-e7e7-baa6-31b157a5f623" + }, + { + "httpRequest": { + "body": { + "type": "JSON", + "json": "{\"meta\":{}}" + }, + "headers": {}, + "method": "DELETE", + "path": "/api/v2/sensitive-data-scanner/config/groups/18cc2267-f3cc-4c15-917d-d3efb15deb03", + "keepAlive": false, + "secure": true + }, + "httpResponse": { + "body": "{\"meta\":{\"version\":275281}}\n", + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "statusCode": 200, + "reasonPhrase": "OK" + }, + "times": { + "remainingTimes": 1 + }, + "timeToLive": { + "unlimited": true + }, + "id": "e79ae3f1-26ed-be85-15ec-0a35094e9e3f" + } +] \ No newline at end of file diff --git a/src/test/resources/cassettes/features/v2/Delete_Scanning_Rule_returns_OK_response.json b/src/test/resources/cassettes/features/v2/Delete_Scanning_Rule_returns_OK_response.json index ffb4f3b5743..9ad219aac0a 100644 --- a/src/test/resources/cassettes/features/v2/Delete_Scanning_Rule_returns_OK_response.json +++ b/src/test/resources/cassettes/features/v2/Delete_Scanning_Rule_returns_OK_response.json @@ -23,7 +23,7 @@ "timeToLive": { "unlimited": true }, - "id": "01611a93-5e74-0630-3c51-f707c3b51e81" + "id": "01611a93-5e74-0630-3c51-f707c3b51e82" }, { "httpRequest": { @@ -53,7 +53,7 @@ "timeToLive": { "unlimited": true }, - "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa4" + "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa5" }, { "httpRequest": { diff --git a/src/test/resources/cassettes/features/v2/Reorder_Groups_returns_Bad_Request_response.json b/src/test/resources/cassettes/features/v2/Reorder_Groups_returns_Bad_Request_response.json index 7d5e057867b..e7cdf64acef 100644 --- a/src/test/resources/cassettes/features/v2/Reorder_Groups_returns_Bad_Request_response.json +++ b/src/test/resources/cassettes/features/v2/Reorder_Groups_returns_Bad_Request_response.json @@ -23,7 +23,7 @@ "timeToLive": { "unlimited": true }, - "id": "01611a93-5e74-0630-3c51-f707c3b51e82" + "id": "01611a93-5e74-0630-3c51-f707c3b51e83" }, { "httpRequest": { @@ -53,7 +53,7 @@ "timeToLive": { "unlimited": true }, - "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa5" + "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa6" }, { "httpRequest": { diff --git a/src/test/resources/cassettes/features/v2/Update_Scanning_Group_returns_OK_response.json b/src/test/resources/cassettes/features/v2/Update_Scanning_Group_returns_OK_response.json index 87e0c845cf0..6f03b4bad25 100644 --- a/src/test/resources/cassettes/features/v2/Update_Scanning_Group_returns_OK_response.json +++ b/src/test/resources/cassettes/features/v2/Update_Scanning_Group_returns_OK_response.json @@ -23,7 +23,7 @@ "timeToLive": { "unlimited": true }, - "id": "01611a93-5e74-0630-3c51-f707c3b51e83" + "id": "01611a93-5e74-0630-3c51-f707c3b51e84" }, { "httpRequest": { @@ -53,7 +53,7 @@ "timeToLive": { "unlimited": true }, - "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa6" + "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa7" }, { "httpRequest": { diff --git a/src/test/resources/cassettes/features/v2/Update_Scanning_Rule_returns_Bad_Request_response.json b/src/test/resources/cassettes/features/v2/Update_Scanning_Rule_returns_Bad_Request_response.json index bd3e8bac090..3f04b7236f0 100644 --- a/src/test/resources/cassettes/features/v2/Update_Scanning_Rule_returns_Bad_Request_response.json +++ b/src/test/resources/cassettes/features/v2/Update_Scanning_Rule_returns_Bad_Request_response.json @@ -23,7 +23,7 @@ "timeToLive": { "unlimited": true }, - "id": "01611a93-5e74-0630-3c51-f707c3b51e80" + "id": "01611a93-5e74-0630-3c51-f707c3b51e81" }, { "httpRequest": { @@ -53,7 +53,7 @@ "timeToLive": { "unlimited": true }, - "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa3" + "id": "e6af4a2f-dfda-8f06-6f3a-f5528b238aa4" }, { "httpRequest": { diff --git a/src/test/resources/com/datadog/api/client/v2/api/sensitive_data_scanner.feature b/src/test/resources/com/datadog/api/client/v2/api/sensitive_data_scanner.feature index 75cc05b7978..4516e161032 100644 --- a/src/test/resources/com/datadog/api/client/v2/api/sensitive_data_scanner.feature +++ b/src/test/resources/com/datadog/api/client/v2/api/sensitive_data_scanner.feature @@ -50,6 +50,17 @@ Feature: Sensitive Data Scanner And the response "data.attributes.included_keyword_configuration.character_count" is equal to 35 And the response "data.attributes.included_keyword_configuration.keywords[0]" is equal to "credit card" + @team:DataDog/sensitive-data-scanner + Scenario: Create Scanning Rule with should_save_match returns "OK" response + Given a valid "configuration" in the system + And there is a valid "scanning_group" in the system + And new "CreateScanningRule" request + 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 }}"}}}}} + When the request is sent + Then the response status is 200 OK + And the response "data.type" is equal to "sensitive_data_scanner_rule" + And the response "data.attributes.name" is equal to "{{ unique }}" + @generated @skip @team:DataDog/sensitive-data-scanner Scenario: Delete Scanning Group returns "Bad Request" response Given new "DeleteScanningGroup" request