From 61645acf1734926ddfb5b85693908e78e34626c4 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 1 Oct 2026 13:26:01 +0200 Subject: [PATCH] fix(worker): drop SignalR hub invocation events so PATs stay out of the worker log Co-Authored-By: Claude Sonnet 5.5 --- src/ClaudeDo.Worker/Logging/HubLogFilter.cs | 15 +++++++ src/ClaudeDo.Worker/Program.cs | 2 + .../Logging/HubLogFilterTests.cs | 39 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/ClaudeDo.Worker/Logging/HubLogFilter.cs create mode 100644 tests/ClaudeDo.Worker.Tests/Logging/HubLogFilterTests.cs diff --git a/src/ClaudeDo.Worker/Logging/HubLogFilter.cs b/src/ClaudeDo.Worker/Logging/HubLogFilter.cs new file mode 100644 index 00000000..f1567155 --- /dev/null +++ b/src/ClaudeDo.Worker/Logging/HubLogFilter.cs @@ -0,0 +1,15 @@ +using Serilog.Events; + +namespace ClaudeDo.Worker.Logging; + +/// Logger-level filter keeping hub invocation arguments (PATs) out of every sink. +public static class HubLogFilter +{ + private const string Dispatcher = "Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher"; + + /// True for the DefaultHubDispatcher events that print the invocation message including its arguments. + public static bool IsInvocationWithArguments(LogEvent e) => + e.Properties.TryGetValue("SourceContext", out var src) && src is ScalarValue { Value: Dispatcher } + && e.Properties.TryGetValue("EventId", out var id) && id is StructureValue sv + && sv.Properties.FirstOrDefault(p => p.Name == "Name")?.Value is ScalarValue { Value: "ReceivedHubInvocation" or "ReceivedStreamHubInvocation" }; +} diff --git a/src/ClaudeDo.Worker/Program.cs b/src/ClaudeDo.Worker/Program.cs index 4541a5cc..1ebec23e 100644 --- a/src/ClaudeDo.Worker/Program.cs +++ b/src/ClaudeDo.Worker/Program.cs @@ -65,6 +65,8 @@ builder.Host.UseSerilog((ctx, lc) => lc // see it either, since SignalR rejects the invocation while parsing the message, before any // hub method (and its filter pipeline) is dispatched. .MinimumLevel.Override("Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher", Serilog.Events.LogEventLevel.Debug) + // The same Debug override logs every invocation's arguments (PATs); drop just those events for all sinks. + .Filter.ByExcluding(ClaudeDo.Worker.Logging.HubLogFilter.IsInvocationWithArguments) .WriteTo.File( System.IO.Path.Combine(logRoot, "worker-.log"), rollingInterval: RollingInterval.Day, diff --git a/tests/ClaudeDo.Worker.Tests/Logging/HubLogFilterTests.cs b/tests/ClaudeDo.Worker.Tests/Logging/HubLogFilterTests.cs new file mode 100644 index 00000000..fd618f1c --- /dev/null +++ b/tests/ClaudeDo.Worker.Tests/Logging/HubLogFilterTests.cs @@ -0,0 +1,39 @@ +using ClaudeDo.Worker.Logging; +using Serilog.Events; +using Serilog.Parsing; + +namespace ClaudeDo.Worker.Tests.Logging; + +public class HubLogFilterTests +{ + private const string Dispatcher = "Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher"; + + private static LogEvent Evt(string source, int eventId, string eventName, string message) => new( + DateTimeOffset.UtcNow, LogEventLevel.Debug, null, new MessageTemplateParser().Parse(message), + [ + new LogEventProperty("SourceContext", new ScalarValue(source)), + new LogEventProperty("EventId", new StructureValue( + [new LogEventProperty("Id", new ScalarValue(eventId)), new LogEventProperty("Name", new ScalarValue(eventName))])), + ]); + + [Fact] + public void Received_invocation_with_arguments_is_excluded() + { + var e = Evt(Dispatcher, 1, "ReceivedHubInvocation", "Received hub invocation: Target: SetAzureDevOpsToken, Arguments: [ secret ]"); + Assert.True(HubLogFilter.IsInvocationWithArguments(e)); + } + + [Fact] + public void Binding_failure_from_same_source_passes() + { + var e = Evt(Dispatcher, 3, "InvalidHubParameters", "Parameters to hub method 'X' are incorrect."); + Assert.False(HubLogFilter.IsInvocationWithArguments(e)); + } + + [Fact] + public void Same_event_name_from_other_source_passes() + { + var e = Evt("Other.Source", 1, "ReceivedHubInvocation", "x"); + Assert.False(HubLogFilter.IsInvocationWithArguments(e)); + } +}