serilog-contrib / Serilog.Enrichers.Sensitive

A Serilog LogEvent enricher that masks sensitive data
MIT License
111 stars 23 forks source link

Exception on importing settings from appsettings.json on version 1.7.1 #25

Closed alexandries98 closed 1 year ago

alexandries98 commented 1 year ago

Hello,

I was trying to use the new functionality of adding MaskingOperators from appsetings.json, but I am getting this exception:

System.InvalidOperationException: 'Cannot create instance of type 'Serilog.Enrichers.Sensitive.SensitiveDataEnricherOptions' because it is missing a public parameterless constructor.'

image

At first, I thought that I made a bad configuration on MaskingOperators, but if I delete it from json and use only MaskValue and MaskProperties, the behavior remains the same, throwing the exception.

.UseSerilog((_, _, configuration) => configuration
            .ReadFrom.Configuration(builder.Configuration));
"Serilog": {
    "Using": [     
      "Serilog.Enrichers.Sensitive"
    ],
    "Enrich": [
      {
        "Name": "WithSensitiveDataMasking",
        "Args": {
          "options": {
            "MaskValue": "**SECRET**",
            "MaskProperties": [ "secret" ],
            "MaskingOperators": [ "SerilogPOC.API.LoggingMasks.CustomSecretMaskingOperator, API" ]
          }
        }
      }
    ]
  }

However, in version 1.7.0 the exception is not happening and things work as expected.

Thank you! 😄

SViradiya-MarutiTech commented 1 year ago

I think there is some issue in namespace,. can you check calling constructor?

sandermvanvliet commented 1 year ago

Here's a repro test case I just made and that seems to work fine:

    [Fact]
    public void ReproCaseIssue25()
    {
        const string jsonConfiguration = @"
{
  ""Serilog"": {
    ""Using"": [
      ""Serilog.Enrichers.Sensitive""
    ],
    ""Enrich"": [
      {
        ""Name"": ""WithSensitiveDataMasking"",
        ""Args"": {
          ""options"": {
            ""MaskValue"": ""**SECRET**"",
            ""MaskProperties"": [
              ""secret""
            ]
          }
        }
      }
    ]
  }
}";

        var memoryStream = new MemoryStream();
        memoryStream.Write(Encoding.UTF8.GetBytes(jsonConfiguration));
        memoryStream.Seek(0, SeekOrigin.Begin);

        var configuration = new ConfigurationBuilder()
            .AddJsonStream(memoryStream)
            .Build();

        var inMemorySink = new InMemorySink();

        var logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .WriteTo.Sink(inMemorySink)
            .CreateLogger();

        logger.Information("A test message {secret}", "this is secret");

        inMemorySink
            .Should()
            .HaveMessage("A test message {secret}")
            .Appearing().Once()
            .WithProperty("secret")
            .WithValue("**SECRET**");
    }

If you can provide a simple console app that reproduces the problem I'm happy to have a look but with what you've supplied it all seems to work fine.

popolony2k commented 1 year ago

Here's a repro test case I just made and that seems to work fine:

    [Fact]
    public void ReproCaseIssue25()
    {
        const string jsonConfiguration = @"
{
  ""Serilog"": {
    ""Using"": [
      ""Serilog.Enrichers.Sensitive""
    ],
    ""Enrich"": [
      {
        ""Name"": ""WithSensitiveDataMasking"",
        ""Args"": {
          ""options"": {
            ""MaskValue"": ""**SECRET**"",
            ""MaskProperties"": [
              ""secret""
            ]
          }
        }
      }
    ]
  }
}";

        var memoryStream = new MemoryStream();
        memoryStream.Write(Encoding.UTF8.GetBytes(jsonConfiguration));
        memoryStream.Seek(0, SeekOrigin.Begin);

        var configuration = new ConfigurationBuilder()
            .AddJsonStream(memoryStream)
            .Build();

        var inMemorySink = new InMemorySink();

        var logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .WriteTo.Sink(inMemorySink)
            .CreateLogger();

        logger.Information("A test message {secret}", "this is secret");

        inMemorySink
            .Should()
            .HaveMessage("A test message {secret}")
            .Appearing().Once()
            .WithProperty("secret")
            .WithValue("**SECRET**");
    }

If you can provide a simple console app that reproduces the problem I'm happy to have a look but with what you've supplied it all seems to work fine.

Hi friend.

I'm using your code and I'm facing the same issue @alexandries98 has reported before.

Below I'm attaching the image with exception, running your code (The version I'm using, is 1.7.1)

image

popolony2k commented 1 year ago

I think the reason isn't working anymore is because you changed SensitiveDataEnricherOptions in 1.7.1 to a new implementation with default parameters and internally maybe the injection dependency mechanism doesn't know how to deal with this constructor, because in version 1.7.0 there was no constructors, so when don't implement a constructor in C# it consider an implicit consctuctor with no parameters and for this reason it can deal with a "no parameter" constructor. Check images 1.7.0 and 1.7.1 below with decompiled code of both versions.

1.7.0 (With implicit default constructor (no parameter)

image

1.7.1 (With the new constrcutor with all parameters with default values)

image

SViradiya-MarutiTech commented 1 year ago

@popolony2k Did you find solution?

sandermvanvliet commented 1 year ago

It's interesting that the unit test (which is on the latest commit) works while a repro application fails.

sandermvanvliet commented 1 year ago

I've found the problem and also why I wasn't able to reproduce it at first.

If the Microsoft.Extensions.Configuration.Binder package is installed in your project it works fine. When it's not present you get the error as reported.

I'm checking why this is the case because it's not very intuitive.

sandermvanvliet commented 1 year ago

@SViradiya-MarutiTech @popolony2k in the meantime a workaround is to install Microsoft.Extensions.Configuration.Binder and it'll work without any additional configuration or code change required.

sandermvanvliet commented 1 year ago

More specifically you need version ... or higher. It was actually introduced in this commit: https://github.com/dotnet/runtime/commit/d78f3648b45aeccdcfc198525eff1c3a2ade6e05

Serilog.Settings.Configuration uses version 2.0,0 of the binder package which doesn't support types with a "complex" constructor and that's what is triggering this issue.

sandermvanvliet commented 1 year ago

Ok this is "fixed" in release 1.7.2

However... configuring which masking operators to use has changed slightly. Instead of setting MaskingOperators in your JSON config you will need to use Operators. The Serilog.Settings.Configuration package does not know how to handle collections of custom types (or at least, I haven't been able to get it to work at all) so there is now a workaround that actually works.

If you have a custom masking operator that requires configuration through a constructor parameter, that won't work and you will have to configure via code instead.

sandermvanvliet commented 1 year ago

@alexandries98 ☝️