Skip to content

Commit 5620ffa

Browse files
Support configuring masking operators from appsettings.json
1 parent daaf82d commit 5620ffa

File tree

7 files changed

+120
-10
lines changed

7 files changed

+120
-10
lines changed

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog for Serilog.Enrichers.Sensitive
22

3+
## 1.7.1
4+
5+
- Support confguring masking operators using [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) (mainly `appsettings.json`)
6+
37
## 1.7.0
48

59
- Allow configuration through `appsettings.json` files.

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>1.7.0.0</Version>
3+
<Version>1.7.1.0</Version>
44
<Authors>Sander van Vliet, Huibert Jan Nieuwkamer, Scott Toberman</Authors>
55
<Company>Codenizer BV</Company>
66
<Copyright>2023 Sander van Vliet</Copyright>

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,31 @@ If you are configuring your logger through `appsettings.json`, you can configure
267267
}
268268
```
269269

270-
Note that `options` is the argument name of the `WithSensitiveDataMasking` extension method and must match exactly.
270+
Note that `options` is the argument name of the `WithSensitiveDataMasking` extension method and must match exactly.
271+
272+
### Masking operators
273+
274+
To configure masking operators you will need to specify the fully qualified name of the masking operator type. For example: `MyApplication.Logging.Serilog.MyCustomMaskingOperator, MyAppliation.Logging` for the type `MyCustomMaskingOperator` in the `MyApplication.Logging` assembly.
275+
276+
An example config file:
277+
278+
```json
279+
{
280+
"Serilog": {
281+
"Using": [
282+
"Serilog.Enrichers.Sensitive"
283+
],
284+
"Enrich": [
285+
{
286+
"Name": "WithSensitiveDataMasking",
287+
"Args": {
288+
"options": {
289+
"MaskValue": "CUSTOM_MASK_FROM_JSON",
290+
"MaskingOperators": [ "MyApplication.Logging.Serilog.MyCustomMaskingOperator, MyAppliation.Logging" ]
291+
}
292+
}
293+
}
294+
]
295+
}
296+
}
297+
```

src/Serilog.Enrichers.Sensitive/SensitiveDataEnricher.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ public SensitiveDataEnricher(SensitiveDataEnricherOptions options)
2828
public SensitiveDataEnricher(
2929
Action<SensitiveDataEnricherOptions>? options)
3030
{
31-
var enricherOptions = new SensitiveDataEnricherOptions();
31+
var enricherOptions = new SensitiveDataEnricherOptions(
32+
MaskingMode.Globally,
33+
DefaultMaskValue,
34+
DefaultOperators.Select(o => o.GetType().AssemblyQualifiedName),
35+
new List<string>(),
36+
new List<string>());
3237

3338
if (options != null)
3439
{

src/Serilog.Enrichers.Sensitive/SensitiveDataEnricherOptions.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,69 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Reflection;
35

46
namespace Serilog.Enrichers.Sensitive
57
{
68
public class SensitiveDataEnricherOptions
79
{
10+
public SensitiveDataEnricherOptions(
11+
MaskingMode mode = MaskingMode.Globally,
12+
string maskValue = SensitiveDataEnricher.DefaultMaskValue,
13+
IEnumerable<string>? maskingOperators = null,
14+
IEnumerable<string>? maskProperties = null,
15+
IEnumerable<string>? excludeProperties = null)
16+
{
17+
Mode = mode;
18+
MaskValue = maskValue;
19+
MaskingOperators = maskingOperators == null ? new List<IMaskingOperator>() : ResolveMaskingOperators(maskingOperators);
20+
MaskProperties = maskProperties?.ToList() ?? new List<string>();
21+
ExcludeProperties = excludeProperties?.ToList() ?? new List<string>();
22+
}
23+
24+
private static List<IMaskingOperator> ResolveMaskingOperators(IEnumerable<string> maskingOperatorTypeNames)
25+
{
26+
var maskingOperators = new List<IMaskingOperator>();
27+
28+
foreach (var typeName in maskingOperatorTypeNames)
29+
{
30+
if (string.IsNullOrWhiteSpace(typeName))
31+
{
32+
continue;
33+
}
34+
35+
var type = Type.GetType(
36+
typeName,
37+
Assembly.Load,
38+
(assembly, fullTypeName, _) =>
39+
{
40+
if (assembly == null)
41+
{
42+
return null;
43+
}
44+
45+
var singleOrDefault = assembly.GetTypes().SingleOrDefault(t => t.FullName == fullTypeName);
46+
47+
return singleOrDefault;
48+
});
49+
50+
if (type == null)
51+
{
52+
throw new Exception(
53+
$"Could not find the masking operator type '{typeName}', check if the type name matches 'Assembly.Namespace.TypeName, Assembly' format and that the assembly can be resolved by the application (i.e. in the same folder)");
54+
}
55+
56+
var instance = Activator.CreateInstance(type);
57+
58+
if (instance != null)
59+
{
60+
maskingOperators.Add((IMaskingOperator)instance);
61+
}
62+
}
63+
64+
return maskingOperators;
65+
}
66+
867
/// <summary>
968
/// Sets whether masking should happen for all log messages ('Globally') or only in sensitive areas ('SensitiveArea')
1069
/// </summary>

test/Serilog.Enrichers.Sensitive.Tests.Unit/Serilog.Enrichers.Sensitive.Tests.Unit.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
<ItemGroup>
1313
<PackageReference Include="FluentAssertions" Version="6.7.0" />
1414
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
15+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.2" />
1516
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
17+
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
1618
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
1719
<PackageReference Include="Serilog" Version="2.12.0" />
1820
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />

test/Serilog.Enrichers.Sensitive.Tests.Unit/WhenConfiguringFromJson.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Serilog.Enrichers.Sensitive.Tests.Unit;
1010
public class WhenConfiguringFromJson
1111
{
1212
[Fact]
13-
public void GivenJsonConfigurationWithMaskValue_EnricherIsConfiguredWithTheCorrectMaskValue()
13+
public void GivenJsonConfigurationWithMaskingOperator_MaskingOperatorIsUsedAndFullMessageIsMasked()
1414
{
1515
var jsonConfiguration = @"
1616
{
@@ -20,9 +20,8 @@ public void GivenJsonConfigurationWithMaskValue_EnricherIsConfiguredWithTheCorre
2020
""Name"": ""WithSensitiveDataMasking"",
2121
""Args"": {
2222
""options"": {
23-
""MaskValue"": ""^^"",
24-
""ExcludeProperties"": [ ""email"" ],
25-
""Mode"": ""Globally""
23+
""MaskValue"": ""MASK FROM JSON"",
24+
""MaskingOperators"": [ ""Serilog.Enrichers.Sensitive.Tests.Unit.MyTestMaskingOperator, Serilog.Enrichers.Sensitive.Tests.Unit"" ]
2625
}
2726
}
2827
}]
@@ -44,11 +43,25 @@ public void GivenJsonConfigurationWithMaskValue_EnricherIsConfiguredWithTheCorre
4443
.WriteTo.Sink(inMemorySink)
4544
.CreateLogger();
4645

47-
logger.Information("Test value foo@bar.net");
46+
logger.Information("A test message");
4847

4948
inMemorySink
5049
.Should()
51-
.HaveMessage("Test value ^^", "the e-mail address is replaced with the configured masking value")
50+
.HaveMessage("MASK FROM JSON", "the custom masking operator matches everything")
5251
.Appearing().Once();
5352
}
53+
}
54+
55+
public class MyTestMaskingOperator : IMaskingOperator
56+
{
57+
public static IMaskingOperator Instance = new MyTestMaskingOperator();
58+
59+
public MaskingResult Mask(string input, string mask)
60+
{
61+
return new MaskingResult
62+
{
63+
Match = true,
64+
Result = mask
65+
};
66+
}
5467
}

0 commit comments

Comments
 (0)