Skip to content

Commit 6ea87ff

Browse files
Improvements to e-mail masking
1 parent 90b6c51 commit 6ea87ff

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

src/Serilog.Enrichers.Sensitive/EmailAddressMaskingOperator.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,30 @@ namespace Serilog.Enrichers.Sensitive
44
{
55
public class EmailAddressMaskingOperator : IMaskingOperator
66
{
7-
private static readonly Regex EmailReplaceRegex = new Regex("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])");
7+
private static readonly Regex EmailReplaceRegex = new Regex(
8+
"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])",
9+
RegexOptions.IgnoreCase);
810

911
public MaskingResult Mask(string input, string mask)
1012
{
13+
// Naive approach to deal with URL encoded values
14+
// most likely this should properly URL decode once it
15+
// finds this marker in the input string
16+
if (input.Contains("%40"))
17+
{
18+
input = input.Replace("%40", "@");
19+
}
20+
21+
// Early exit so we avoid the regex.
22+
// Probably needs a benchmark to see
23+
// if this actually helps.
1124
if (!input.Contains("@"))
1225
{
1326
return MaskingResult.NoMatch;
1427
}
1528

29+
// Note that if we get here we _always_ assume
30+
// a successful replacement.
1631
return new MaskingResult
1732
{
1833
Result = EmailReplaceRegex.Replace(input, mask),

src/Serilog.Enrichers.Sensitive/Serilog.Enrichers.Sensitive.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<PropertyGroup>
1010
<IsPackable>true</IsPackable>
11-
<Version>0.1.1</Version>
11+
<Version>0.1.2</Version>
1212
<Title>Serilog enricher to mask sensitive data</Title>
1313
<Description>An enricher to be used for masking sensitive (PII) data using Serilog</Description>
1414
<Copyright>2020 Sander van Vliet</Copyright>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace Serilog.Enrichers.Sensitive.Tests.Unit
5+
{
6+
public class WhenMaskingEmailAddresses
7+
{
8+
[Fact]
9+
public void GivenSimpleMailAddress_AddressIsMasked()
10+
{
11+
TheMaskedResultOf("[email protected]")
12+
.Should()
13+
.Be(Mask);
14+
}
15+
16+
[Fact]
17+
public void GivenSimpleMailAddressButUppercase_AddressIsMasked()
18+
{
19+
TheMaskedResultOf("[email protected]")
20+
.Should()
21+
.Be(Mask);
22+
}
23+
24+
[Fact]
25+
public void GivenEmailAddressWithBoxQualifier_AddressIsMasked()
26+
{
27+
TheMaskedResultOf("[email protected]")
28+
.Should()
29+
.Be(Mask);
30+
}
31+
32+
[Fact]
33+
public void GivenEmailAddressWithSubdomains_AddressIsMasked()
34+
{
35+
TheMaskedResultOf("[email protected]")
36+
.Should()
37+
.Be(Mask);
38+
}
39+
40+
[Fact]
41+
public void GivenEmailAddressInUrl_EntireStringIsMasked()
42+
{
43+
TheMaskedResultOf("https://foo.com/api/1/some/[email protected]")
44+
.Should()
45+
.Be("https:" + Mask); // I don't even regex
46+
}
47+
48+
[Fact]
49+
public void GivenEmailAddressUrlEncoded_AddressIsMasked()
50+
{
51+
TheMaskedResultOf("test%40email.com")
52+
.Should()
53+
.Be(Mask); // I don't even regex
54+
}
55+
56+
private const string Mask = "***MASK***";
57+
58+
private static string TheMaskedResultOf(string input)
59+
{
60+
var maskingResult = new EmailAddressMaskingOperator().Mask(input, Mask);
61+
62+
return maskingResult.Match
63+
? maskingResult.Result
64+
: input;
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)