-
Notifications
You must be signed in to change notification settings - Fork 43
Add sensitive data masking support #1343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 29 commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
dfccdb9
[Automated] Update the native jar versions
TharmiganK 3fc78ff
Add initial implementation
TharmiganK 87787b6
Refactor with perf improvements
TharmiganK 8ca08e7
Refactor by adding a builder
TharmiganK 1b60ee2
Fix checkstyle issues
TharmiganK abce20d
Merge remote-tracking branch 'origin/master' into sensitive-data
TharmiganK 0180d53
Add sensitive data masking support for root logger
TharmiganK 7840838
Add annotation caching support
TharmiganK 376f57e
Merge remote-tracking branch 'origin/master' into sensitive-data
TharmiganK c44d90d
Merge branch 'master' into sensitive-data
TharmiganK 77f026d
Merge branch 'master' into sensitive-data
TharmiganK 3490499
Fix xml toString
TharmiganK e6e3aa4
Support undefined fields in the record to string
TharmiganK 1ad62cf
Add tests for masked string function
TharmiganK 49eac01
Merge branch 'master' into sensitive-data
TharmiganK 2f2e3cb
Refactor class to reduce cognitive complexity
TharmiganK 7dbb203
Add support for map value
TharmiganK 78298b2
Fix issues with field names with special characters
TharmiganK f989b46
Merge branch 'master' into sensitive-data
TharmiganK 1eefb16
Merge branch 'master' into sensitive-data
TharmiganK 466d4b8
Add support to enable sensitive data masking via configuration
TharmiganK 868fca5
Add tests for masked logging
TharmiganK e04e2a2
Enhance type processing to handle intersection and reference types
TharmiganK 1ad022a
Add support for sensitive data masking in templates and value functions
TharmiganK b6cb527
Add tests for readonly types
TharmiganK de5cdb1
Add an integration test
TharmiganK 60a4cf4
Update changelog
TharmiganK 11fdebd
Update spec
TharmiganK 1d3bff0
Merge remote-tracking branch 'origin/master' into sensitive-data
TharmiganK 7e27003
Optimize Unicode escaping by using a pre-computed hex lookup table
TharmiganK 1dca782
Add tests for masking structurally similar records and basic types
TharmiganK d19d781
Add test for masking special characters in strings
TharmiganK dbf5679
Merge branch 'master' into sensitive-data
TharmiganK 6516ecd
Update spec to clarify masking behavior and type extraction
TharmiganK 652f323
Add tests for masking empty arrays, tables, and records
TharmiganK a467533
Merge branch 'master' into sensitive-data
TharmiganK f93c481
Add documentation for sensitive data masking features
TharmiganK dfc98fa
Enhance sensitive data masking documentation and functionality
TharmiganK 7a8b9aa
Refactor sensitive data masking strategy to use a dedicated maskStrin…
TharmiganK cc0cc5d
Refactor sensitive data annotation from @SensitiveData to @Sensitive
TharmiganK a4a6766
Deprecate processTemplate function and replace with evaluateTemplate …
TharmiganK a3f61e9
Merge branch 'master' into sensitive-data
TharmiganK 37603fb
Address review suggestions
TharmiganK 5e45310
Address review suggestions
TharmiganK 42156dd
Update integration-tests/tests/resources/samples/masked-logger/Config…
daneshk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright (c) 2025 WSO2 LLC. (https://www.wso2.com). | ||
| // | ||
| // WSO2 LLC. licenses this file to you under the Apache License, | ||
| // Version 2.0 (the "License"); you may not use this file except | ||
| // in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| import ballerina/jballerina.java; | ||
|
|
||
| # Exclude the field from log output | ||
| public const EXCLUDE = "EXCLUDE"; | ||
|
|
||
| # Replacement function type for sensitive data masking | ||
| public type ReplacementFunction isolated function (string input) returns string; | ||
|
|
||
| # Replacement strategy for sensitive data | ||
| # | ||
| # + replacement - The replacement value. This can be a string which will be used to replace the | ||
| # entire value, or a function that takes the original value and returns a masked version. | ||
| public type Replacement record {| | ||
| string|ReplacementFunction replacement; | ||
| |}; | ||
|
|
||
| # Masking strategy for sensitive data | ||
| public type MaskingStrategy EXCLUDE|Replacement; | ||
|
|
||
| # Represents sensitive data with a masking strategy | ||
| # | ||
| # + strategy - The masking strategy to apply (default: EXCLUDE) | ||
TharmiganK marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public type SensitiveDataConfig record {| | ||
| MaskingStrategy strategy = EXCLUDE; | ||
| |}; | ||
|
|
||
| # Marks a record field or type as sensitive, excluding it from log output | ||
| # | ||
| # + strategy - The masking strategy to apply (default: EXCLUDE) | ||
TharmiganK marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public annotation SensitiveDataConfig SensitiveData on record field; | ||
|
|
||
| configurable boolean enableSensitiveDataMasking = false; | ||
|
|
||
| # Returns a masked string representation of the given data based on the sensitive data masking configuration. | ||
| # | ||
| # + data - The data to be masked | ||
| # + return - The masked string representation of the data | ||
| public isolated function toMaskedString(anydata data) returns string = @java:Method { | ||
| 'class: "io.ballerina.stdlib.log.Utils" | ||
| } external; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Copyright (c) 2025 WSO2 LLC. (https://www.wso2.com). | ||
| // | ||
| // WSO2 LLC. licenses this file to you under the Apache License, | ||
| // Version 2.0 (the "License"); you may not use this file except | ||
| // in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| import ballerina/io; | ||
| import ballerina/test; | ||
|
|
||
| final Logger maskerJsonLogger = check fromConfig(enableSensitiveDataMasking = true); | ||
| final Logger maskerLogger = check fromConfig(enableSensitiveDataMasking = true, format = LOGFMT); | ||
|
|
||
| string[] maskedLogs = []; | ||
|
|
||
| function addMaskedLogs(io:FileOutputStream fileOutputStream, io:Printable... values) { | ||
| var firstValue = values[0]; | ||
| if firstValue is string { | ||
| maskedLogs.push(firstValue); | ||
| } | ||
| } | ||
|
|
||
| final readonly & User user = { | ||
| name: "John Doe", | ||
| ssn: "123-45-6789", | ||
| password: "password123", | ||
| mail: "john.doe@example.com", | ||
| creditCard: "4111-1111-1111-1111" | ||
| }; | ||
|
|
||
| isolated function getUser() returns User => user; | ||
|
|
||
| @test:Config { | ||
| groups: ["logMasking"] | ||
| } | ||
| function testLogMasking() returns error? { | ||
| test:when(mock_fprintln).call("addMaskedLogs"); | ||
| maskerJsonLogger.printInfo("user logged in", user = user); | ||
| string expectedLog = string `"message":"user logged in","user":{"name":"John Doe","password":"*****","mail":"joh**************com"},"env":"test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
|
|
||
| maskerLogger.printInfo("user logged in", user = user); | ||
| expectedLog = string `message="user logged in" user={"name":"John Doe","password":"*****","mail":"joh**************com"} env="test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
|
|
||
| map<json> userTmp = user; | ||
| maskerJsonLogger.printInfo("user logged in", user = userTmp); | ||
| expectedLog = string `"message":"user logged in","user":{"name":"John Doe","password":"*****","mail":"joh**************com"},"env":"test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
|
|
||
| userTmp = check user.cloneWithType(); | ||
| maskerJsonLogger.printInfo("user logged in", user = userTmp); | ||
| expectedLog = string `"message":"user logged in","user":{"name":"John Doe","ssn":"123-45-6789","password":"password123","mail":"john.doe@example.com","creditCard":"4111-1111-1111-1111"},"env":"test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
|
|
||
| maskerLogger.printDebug(`user login event. user details: ${user}`); | ||
| expectedLog = string `message="user login event. user details: {\"name\":\"John Doe\",\"password\":\"*****\",\"mail\":\"joh**************com\"}" env="test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
|
|
||
| maskerLogger.printWarn("user login attempt failed", user = getUser); | ||
| expectedLog = string `message="user login attempt failed" user={"name":"John Doe","password":"*****","mail":"joh**************com"} env="test"`; | ||
| test:assertEquals(maskedLogs.length(), 1); | ||
| test:assertTrue(maskedLogs[0].includes(expectedLog)); | ||
| maskedLogs.removeAll(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.