├── .circleci
└── config.yml
├── .gitattributes
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── JsonMasking.Tests
├── JsonMasking.Tests.csproj
├── JsonMaskingTests.cs
└── Mocks
│ └── BlacklistPartialMock.cs
├── JsonMasking.sln
├── JsonMasking
├── JsonMasking.cs
└── JsonMasking.csproj
├── LICENSE
├── QA.md
├── README.md
├── appveyor.yml
└── azure-pipelines.yml
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | working_directory: /app
5 | docker:
6 | - image: mcr.microsoft.com/dotnet/sdk:6.0
7 | steps:
8 | - checkout
9 | - run:
10 | name: Checking files
11 | command: ls -al
12 | - run:
13 | name: Restoring nuget packages
14 | command: dotnet restore
15 | - run:
16 | name: Publishing app
17 | command: dotnet publish -c Release -o binn
18 | - store_artifacts:
19 | path: /app/JsonMasking/binn
20 | destination: app-artifact
21 | - persist_to_workspace:
22 | root: /
23 | paths:
24 | - app
25 |
26 | unit_tests:
27 | working_directory: /
28 | docker:
29 | - image: mcr.microsoft.com/dotnet/sdk:6.0
30 | steps:
31 | - attach_workspace:
32 | at: /
33 | - run:
34 | name: Checking files
35 | command: ls /app
36 | - run:
37 | name: Install coverlet
38 | command: |
39 | dotnet tool install --global coverlet.console
40 | cd /app/JsonMasking.Tests
41 | dotnet add package coverlet.msbuild
42 | - run:
43 | name: Running unit tests
44 | command: |
45 | dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=/test-result/result /p:Exclude=[xunit.*]* /app/JsonMasking.sln
46 | - store_artifacts:
47 | path: /test-result
48 | destination: test-result
49 | - persist_to_workspace:
50 | root: /
51 | paths:
52 | - test-result
53 |
54 | workflows:
55 | version: 2
56 | build_publish_deploy:
57 | jobs:
58 | - build
59 | - unit_tests:
60 | requires:
61 | - build
62 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | First off, thanks for taking the time to contribute!
4 |
5 | ### How can I contribute?
6 |
7 | * Fork this project;
8 | * Make your changes / new implementatios;
9 | * Use the pattern for git commts;
10 | * Make sure that the acceptance criteria are met (tests, docs, etc);
11 | * Create a pull request;
12 |
13 | ### Pull Requests
14 |
15 | Template [PULLREQUEST-TEMPLATE](PULLREQUEST-TEMPLATE.md)
16 |
17 | ### Git Commit Messages
18 |
19 | * Use the present tense ("Adds feature" not "Added feature")
20 | * Limit the first line to 72 characters or less
21 | * Reference issues and pull requests liberally
22 | * Consider starting the commit message with an applicable emoji:
23 | * :art: `:art:` when improving the format/structure of the code
24 | * :racehorse: `:racehorse:` when improving performance
25 | * :non-potable_water: `:non-potable_water:` when plugging memory leaks
26 | * :memo: `:memo:` when writing docs
27 | * :penguin: `:penguin:` when fixing something on Linux
28 | * :apple: `:apple:` when fixing something on Mac OS
29 | * :checkered_flag: `:checkered_flag:` when fixing something on Windows
30 | * :bug: `:bug:` when fixing a bug
31 | * :fire: `:fire:` when removing code or files
32 | * :green_heart: `:green_heart:` when fixing the CI build
33 | * :white_check_mark: `:white_check_mark:` when adding tests
34 | * :lock: `:lock:` when dealing with security
35 | * :arrow_up: `:arrow_up:` when upgrading dependencies
36 | * :arrow_down: `:arrow_down:` when downgrading dependencies
37 | * :shirt: `:shirt:` when removing linter warnings
38 | * :bulb: `:bulb:` new idea
39 | * :construction: `:construction:` work in progress
40 | * :heavy_plus_sign: `:heavy_plus_sign:` when adding features
41 | * :heavy_minus_sign: `:heavy_minus_sign:` when removing features
42 | * :speaker: `:mute:` when adding logging
43 | * :mute: `:mute:` when reducing logging
44 | * :facepunch: `:facepunch:` when resolve conflict
45 | * :wrench: `:wrench:` when modify Web.config
46 |
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ```
2 | Please use the following template to submit your issue.
3 | Following this template will allow us to quickly investigate and help you with your issue.
4 | Please be aware that issues which do not conform to this template may be closed.
5 |
6 | DO NOT FORGET TO REMOVE THIS BLOCK
7 | ```
8 |
9 | ### Status
10 |
11 | BUG REPORT / TASK
12 |
13 | ### Checklist
14 |
15 | Add checklist if this is a task
16 |
17 | - [x] Add slack integration
18 | - [_] Support xyz
19 |
20 | ### Steps
21 |
22 | 1. First step
23 | 2. Second step
24 | 3. Third step
25 |
26 | ### Expected behaviour
27 |
28 | How do you think the program should work? Add screenshots and code blocks if necessary.
29 |
30 | ### Actual behaviour
31 |
32 | How does the program work in its current state?
33 |
34 | ### Environment
35 |
36 | You may write here the specifications like the version of the project, services, operating system, or hardware if applicable.
37 |
38 | ### Logs / Stack trace
39 |
40 | ```
41 | Insert your log/stack trace here
42 | ```
43 |
44 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ```
2 | This is a guide to use this Pull Request Template.
3 |
4 | # Title
5 | [feature] Implements the crazy powerful transaction search
6 | [hotfix] Fixes login with e-mail address
7 |
8 | Add a gif that expresses your reaction to the implemented code, make it fun
9 |
10 | DO NOT FORGET TO REMOVE THIS BLOCK
11 | ```
12 | 
13 |
14 | ### Status
15 |
16 | READY / IN DEVELOPMENT
17 |
18 | ### Whats?
19 |
20 | Describe in an objective way what has been done.
21 |
22 | ### Why?
23 |
24 | Why do you need this implementation/fix?
25 |
26 | ### How?
27 |
28 | How did you solve the problem? What are the main flows? Any technical information regarding infrastructure or architecture?
29 |
30 | ### Attachments (if appropriate)
31 |
32 | Add additional informations like screenshots, issue link, zendesk ticket link, jira task link, etc
33 |
34 | ### Definition of Done:
35 | - [ ] Increases API documentation
36 | - [ ] Implements integration tests
37 | - [ ] Implements unit tests
38 | - [ ] Is there appropriate logging included?
39 | - [ ] Does this add new dependencies?
40 | - [ ] Does need add new version in changelog?
41 | - [ ] Does need update readme, contributing, etc?
42 | - [ ] Does need change in CI server?
43 | - [ ] Will this feature require a new piece of infrastructure be implemented?
44 | - [ ] Does this PR require a blog post? If so, ensure marketing has signed off on content.
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/JsonMasking.Tests/JsonMasking.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | false
5 |
6 |
7 |
8 |
9 |
10 | all
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/JsonMasking.Tests/JsonMaskingTests.cs:
--------------------------------------------------------------------------------
1 | using JsonMasking.Tests.Mocks;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using System;
5 | using System.Collections.Generic;
6 | using Xunit;
7 |
8 | namespace JsonMasking.Tests
9 | {
10 | public static class JsonMaskingTests
11 | {
12 |
13 | [Fact]
14 | public static void MaskFields_Should_Mask_No_Field_With_Empty_Blacklist()
15 | {
16 | // arrange
17 | var obj = new
18 | {
19 | Test = "1",
20 | Password = "somepass#here"
21 | };
22 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
23 | string[] blacklist = { };
24 | var mask = "*******";
25 |
26 | // act
27 | var result = json.MaskFields(blacklist, mask);
28 |
29 | // assert
30 | Assert.Equal("{\n \"Test\": \"1\",\n \"Password\": \"somepass#here\"\n}", result.Replace("\r\n", "\n"));
31 | }
32 |
33 | [Fact]
34 | public static void MaskFields_Should_Mask_No_Field_With_Json_Without_Property()
35 | {
36 | // arrange
37 | var obj = new
38 | {
39 | Test = "1",
40 | OtherField = "somepass#here"
41 | };
42 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
43 | string[] blacklist = { "password" };
44 | var mask = "*******";
45 |
46 | // act
47 | var result = json.MaskFields(blacklist, mask);
48 |
49 | // assert
50 | Assert.Equal("{\n \"Test\": \"1\",\n \"OtherField\": \"somepass#here\"\n}", result.Replace("\r\n", "\n"));
51 | }
52 |
53 | [Fact]
54 | public static void MaskFields_Should_Mask_Single_Field()
55 | {
56 | // arrange
57 | var obj = new
58 | {
59 | Test = "1",
60 | Password = "somepass#here"
61 | };
62 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
63 | string[] blacklist = { "password" };
64 | var mask = "----";
65 |
66 | // act
67 | var result = json.MaskFields(blacklist, mask);
68 |
69 | // assert
70 | Assert.Equal("{\n \"Test\": \"1\",\n \"Password\": \"----\"\n}", result.Replace("\r\n", "\n"));
71 | }
72 |
73 | [Fact]
74 | public static void MaskFields_Should_Mask_Integer_Field()
75 | {
76 | // arrange
77 | var obj = new
78 | {
79 | Test = 1,
80 | Password = 123456
81 | };
82 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
83 | string[] blacklist = { "*password" };
84 | var mask = "*******";
85 |
86 | // act
87 | var result = json.MaskFields(blacklist, mask);
88 |
89 | // assert
90 | Assert.Equal("{\n \"Test\": 1,\n \"Password\": \"*******\"\n}", result.Replace("\r\n", "\n"));
91 | }
92 |
93 | [Fact]
94 | public static void MaskFields_Should_Mask_Depth_Field()
95 | {
96 | // arrange
97 | var obj = new
98 | {
99 | DepthObject = new
100 | {
101 | Test = "1",
102 | Password = "somepass#here"
103 | }
104 | };
105 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
106 | string[] blacklist = { "*.password" };
107 | var mask = "*******";
108 |
109 | // act
110 | var result = json.MaskFields(blacklist, mask);
111 |
112 | // assert
113 | Assert.Equal("{\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\"\n }\n}", result.Replace("\r\n", "\n"));
114 | }
115 |
116 | [Fact]
117 | public static void MaskFields_Should_Mask_Multiple_Fields()
118 | {
119 | // arrange
120 | var obj = new
121 | {
122 | Password = "somepass#here",
123 | DepthObject = new
124 | {
125 | Test = "1",
126 | Password = "somepass#here2"
127 | }
128 | };
129 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
130 | string[] blacklist = { "*password" };
131 | var mask = "*******";
132 |
133 | // act
134 | var result = json.MaskFields(blacklist, mask);
135 |
136 | // assert
137 | Assert.Equal("{\n \"Password\": \"*******\",\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\"\n }\n}", result.Replace("\r\n", "\n"));
138 | }
139 |
140 | [Fact]
141 | public static void MaskFields_Should_Mask_Multiple_Fields_With_Multiple_Blacklist()
142 | {
143 | // arrange
144 | var obj = new
145 | {
146 | Password = "somepass#here",
147 | DepthObject = new
148 | {
149 | Test = "1",
150 | CreditCardNumber = "5555000011112222"
151 | }
152 | };
153 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
154 | string[] blacklist = { "password", "*creditcardnumber" };
155 | var mask = "*******";
156 |
157 | // act
158 | var result = json.MaskFields(blacklist, mask);
159 |
160 | // assert
161 | Assert.Equal("{\n \"Password\": \"*******\",\n \"DepthObject\": {\n \"Test\": \"1\",\n \"CreditCardNumber\": \"*******\"\n }\n}", result.Replace("\r\n", "\n"));
162 | }
163 |
164 | [Fact]
165 | public static void MaskFields_Should_Mask_With_Null_Property()
166 | {
167 | // arrange
168 | var obj = new
169 | {
170 | DepthObject = new
171 | {
172 | Test = "1",
173 | Password = (string)null,
174 | }
175 | };
176 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
177 | string[] blacklist = { "*password" };
178 | var mask = "*******";
179 |
180 | // act
181 | var result = json.MaskFields(blacklist, mask);
182 |
183 | // assert
184 | Assert.Equal("{\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\"\n }\n}", result.Replace("\r\n", "\n"));
185 | }
186 |
187 | [Fact]
188 | public static void MaskFields_Should_Throw_Exception_When_Blacklist_Is_Null()
189 | {
190 | // arrange
191 | var obj = new
192 | {
193 | Test = "1",
194 | Password = "somepass#here"
195 | };
196 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
197 | string[] blacklist = null;
198 | var mask = "*******";
199 |
200 | // act
201 | Exception ex = Assert.Throws(() =>
202 | json.MaskFields(blacklist, mask));
203 |
204 | // assert
205 | Assert.Equal("Value cannot be null. (Parameter 'blacklist')", ex.Message);
206 | }
207 |
208 | [Fact]
209 | public static void MaskFields_Should_Throw_Exception_When_Json_Is_Null()
210 | {
211 | // arrange
212 | string json = null;
213 | string[] blacklist = { "password" };
214 | var mask = "*******";
215 |
216 | // act
217 | Exception ex = Assert.Throws(() =>
218 | json.MaskFields(blacklist, mask));
219 |
220 | // assert
221 | Assert.Equal("Value cannot be null. (Parameter 'json')", ex.Message);
222 | }
223 |
224 | [Fact]
225 | public static void MaskFields_Should_Throw_Exception_When_Json_String_Is_Empty()
226 | {
227 | // arrange
228 | string json = "";
229 | string[] blacklist = { "password" };
230 | var mask = "*******";
231 |
232 | // act
233 | Exception ex = Assert.Throws(() =>
234 | json.MaskFields(blacklist, mask));
235 |
236 | // assert
237 | Assert.Equal("Value cannot be null. (Parameter 'json')", ex.Message);
238 | }
239 |
240 | [Fact]
241 | public static void MaskFields_Should_Throw_Exception_When_Json_String_Is_Invalid()
242 | {
243 | // arrange
244 | var json = "invalid json";
245 | string[] blacklist = { "password" };
246 | string mask = "------";
247 |
248 | // act
249 | Exception ex = Assert.Throws(() =>
250 | json.MaskFields(blacklist, mask));
251 |
252 | // assert
253 | Assert.StartsWith("Unexpected character encountered while parsing value", ex.Message);
254 | }
255 |
256 | [Fact]
257 | public static void MaskFields_Should_Mask_With_Wildcard()
258 | {
259 | // arrange
260 | var obj = new
261 | {
262 | DepthObject = new
263 | {
264 | Test = "1",
265 | Password = "somepass#here",
266 | DepthObject = new
267 | {
268 | Test = "1",
269 | Password = "somepass#here"
270 | }
271 | },
272 | DepthObject2 = new
273 | {
274 | Test = "1",
275 | Password = "somepass#here",
276 | DepthObject = new
277 | {
278 | Test = "1",
279 | Password = "somepass#here",
280 | DepthObject = new
281 | {
282 | Test = "1",
283 | Password = new
284 | {
285 | Test1 = "1",
286 | Password2 = "somepass#here"
287 | }
288 | }
289 | }
290 | },
291 | Password = "somepass#here"
292 | };
293 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
294 | string[] blacklist = { "*.DepthObject*.Password" };
295 | var mask = "*******";
296 |
297 | // act
298 | var result = json.MaskFields(blacklist, mask);
299 |
300 | // assert
301 | Assert.Equal("{\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"somepass#here\",\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\"\n }\n },\n \"DepthObject2\": {\n \"Test\": \"1\",\n \"Password\": \"somepass#here\",\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\",\n \"DepthObject\": {\n \"Test\": \"1\",\n \"Password\": \"*******\"\n }\n }\n },\n \"Password\": \"somepass#here\"\n}", result.Replace("\r\n", "\n"));
302 | }
303 |
304 | [Fact]
305 | public static void MaskFields_Should_Mask_Partial_With_Single_Field()
306 | {
307 | // arrange
308 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"somepass#here2\"\n }\n}";
309 |
310 | var blacklistPartialMock = BlacklistPartialMock.DefaultBlackListPartial;
311 |
312 | var obj = new
313 | {
314 | Test = "1",
315 | Card = new
316 | {
317 | Number = "4622943127049865",
318 | Password = "somepass#here2"
319 | }
320 | };
321 |
322 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
323 | string[] blacklist = { "*card.number" };
324 | var mask = "----";
325 |
326 | // act
327 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
328 |
329 | // assert
330 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
331 | }
332 |
333 | [Fact]
334 | public static void MaskFields_Should_Mask_Partial_And_Completely_With_Single_Field()
335 | {
336 | // arrange
337 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"----\"\n },\n \"Password\": \"----\"\n}";
338 |
339 | var blacklistPartialMock = BlacklistPartialMock.DefaultBlackListPartial;
340 |
341 | var obj = new
342 | {
343 | Test = "1",
344 | Card = new
345 | {
346 | Number = "4622943127049865",
347 | Password = "somepass#here2"
348 | },
349 | Password = "somepass#here2"
350 | };
351 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
352 | string[] blacklist = { "*card.number", "*password" };
353 | var mask = "----";
354 |
355 | // act
356 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
357 |
358 | // assert
359 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
360 | }
361 |
362 | [Fact]
363 | public static void MaskFields_Should_Mask_Partial_With_Multiple_Fields()
364 | {
365 | // arrange
366 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"somepass#here2\",\n \"Card\": {\n \"Number\": \"462294*****9865\"\n },\n \"Teste\": {\n \"Card\": {\n \"Number\": \"462294*****9865\"\n }\n }\n }\n}";
367 |
368 | var blacklistPartialMock = BlacklistPartialMock.DefaultBlackListPartial;
369 |
370 | var obj = new
371 | {
372 | Test = "1",
373 | Card = new
374 | {
375 | Number = "4622943127049865",
376 | Password = "somepass#here2",
377 | Card = new
378 | {
379 | Number = "4622943127049865",
380 | },
381 | Teste = new
382 | {
383 | Card = new
384 | {
385 | Number = "4622943127049865",
386 | }
387 | }
388 |
389 | }
390 | };
391 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
392 | string[] blacklist = { "*card.number" };
393 | var mask = "----";
394 |
395 | // act
396 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
397 |
398 | // assert
399 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
400 | }
401 |
402 | [Fact]
403 | public static void MaskFields_Should_Mask_Partial_And_Completely_With_Multiple_Field()
404 | {
405 | // arrange
406 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"----\",\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"----\"\n },\n \"Teste\": {\n \"Card\": {\n \"Number\": \"462294*****9865\",\n \"Password\": \"----\"\n }\n }\n }\n}";
407 |
408 | var blacklistPartialMock = BlacklistPartialMock.DefaultBlackListPartial;
409 |
410 | var obj = new
411 | {
412 | Test = "1",
413 | Card = new
414 | {
415 | Number = "4622943127049865",
416 | Password = "somepass#here2",
417 | Card = new
418 | {
419 | Number = "4622943127049865",
420 | Password = "somepass#here2"
421 | },
422 | Teste = new
423 | {
424 | Card = new
425 | {
426 | Number = "4622943127049865",
427 | Password = "somepass#here2"
428 | }
429 | }
430 | }
431 | };
432 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
433 | string[] blacklist = { "*card.number", "*password" };
434 | var mask = "----";
435 |
436 | // act
437 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
438 |
439 | // assert
440 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
441 | }
442 |
443 | [Fact]
444 | public static void MaskFields_Should_Not_Mask_Partial_If_Property_IsNot_In_Blacklist()
445 | {
446 | // arrange
447 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"4622943127049865\",\n \"Password\": \"somepass#here2\"\n }\n}";
448 |
449 | var blacklistPartialMock = BlacklistPartialMock.DefaultBlackListPartial;
450 |
451 | var obj = new
452 | {
453 | Test = "1",
454 | Card = new
455 | {
456 | Number = "4622943127049865",
457 | Password = "somepass#here2"
458 | }
459 | };
460 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
461 | string[] blacklist = { };
462 | var mask = "----";
463 |
464 | // act
465 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
466 |
467 | // assert
468 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
469 | }
470 |
471 | [Fact]
472 | public static void MaskFields_Should_Mask_Completely_If_Partial_Blacklist_Is_Empty()
473 | {
474 | // arrange
475 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"----\",\n \"Password\": \"somepass#here2\"\n }\n}";
476 |
477 | var blacklistPartialMock = new Dictionary>(StringComparer.OrdinalIgnoreCase){ };
478 |
479 | var obj = new
480 | {
481 | Test = "1",
482 | Card = new
483 | {
484 | Number = "4622943127049865",
485 | Password = "somepass#here2"
486 | }
487 | };
488 |
489 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
490 | string[] blacklist = { "*card.number" };
491 | var mask = "----";
492 |
493 | // act
494 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
495 |
496 | // assert
497 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
498 | }
499 |
500 | [Fact]
501 | public static void MaskFields_Should_Mask_Completely_If_Delegate_Return_Same_Value()
502 | {
503 | // arrange
504 | const string EXPECTED_VALUE = "{\n \"Test\": \"1\",\n \"Card\": {\n \"Number\": \"----\",\n \"Password\": \"somepass#here2\"\n }\n}";
505 |
506 | var blacklistPartialMock = new Dictionary>(StringComparer.OrdinalIgnoreCase)
507 | {
508 | { "*card.number", text => text }
509 | };
510 |
511 | var obj = new
512 | {
513 | Test = "1",
514 | Card = new
515 | {
516 | Number = "4622943127049865",
517 | Password = "somepass#here2"
518 | }
519 | };
520 | var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
521 | string[] blacklist = { "*card.number" };
522 | var mask = "----";
523 |
524 | // act
525 | var result = json.MaskFields(blacklist, mask, blacklistPartialMock);
526 |
527 | // assert
528 | Assert.Equal(EXPECTED_VALUE, result.Replace("\r\n", "\n"));
529 | }
530 |
531 | [Fact]
532 | public static void MaskFields_Should_Mask_With_Wildcard_ForJsonArray()
533 | {
534 | // arrange
535 | var dto1 =
536 | new
537 | {
538 | Name = "Test1",
539 | WillbeMasked = "12345"
540 | };
541 |
542 | var dto2 =
543 | new
544 | {
545 | Name = "Test2",
546 | WillbeMasked = "123"
547 | };
548 |
549 | List