Skip to content

Suppress SignalR bearer tokens from hosting logs - #2420

Merged
Chris0Jeky merged 4 commits into
mainfrom
issue-2351/signalr-token-log-suppression
Sep 3, 2026
Merged

Suppress SignalR bearer tokens from hosting logs#2420
Chris0Jeky merged 4 commits into
mainfrom
issue-2351/signalr-token-log-suppression

Conversation

@Chris0Jeky

Copy link
Copy Markdown
Owner

Summary

  • suppress routine Microsoft.AspNetCore.Hosting.Diagnostics request-target logs below Warning
  • keep other ASP.NET Core Information diagnostics available
  • prove a real SignalR request cannot place its query-string bearer token in any configured log provider, even under a hostile Trace override
  • document the fixed logging boundary

This is a bounded security slice of #2351. It does not close the umbrella; persisted agent/CLI errors, MCP/CLI inventory, provider health, and future-flow guards remain.

Threat boundary

Supported SignalR browser clients send bearer credentials through the access_token query parameter. Hosting.Diagnostics renders the full request target at Information before Taskdeck middleware can redact it. A replayable JWT can therefore enter console, CI, or hosted log sinks. The exact category is now fixed at Warning while other framework categories retain configured levels.

Verification

  • failing-first: a real /hubs/boards?...&access_token=<synthetic JWT> request produced both request-start and request-finish Information entries containing the full marker before the fix
  • dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release -m:1 --filter FullyQualifiedName~LoggingProviderConfigurationTests|FullyQualifiedName~OAuthTokenLifecycleTests.SignalR_QueryStringAuth: 6 passed
  • dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release -m:1: 2,765 passed, 4 declared skips, 1 unrelated untouched warmed-query-shape probe failure
  • exact failed probe rerun: 1 passed; containing BatchExecuteProposalsApiTests class rerun: 18 passed
  • node scripts/check-docs-governance.mjs: passed
  • node scripts/check-golden-principles.mjs: passed
  • git diff --check origin/main...HEAD: passed

Not verified

  • full backend solution
  • live Kestrel with the console provider outside the API test host
  • hosted CI and exact-head review are pending

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T06:33:35.973191Z 74032cf New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a385c20440

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/src/Taskdeck.Api/Program.cs Outdated
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Review finding: HIGH, merge-blocking

The current provider-agnostic Hosting.Diagnostics rule can be bypassed by a provider-specific logging override. .NET gives provider-specific rules precedence. With a provider-specific Information override for the test recording provider, the real SignalR request regression failed and captured both request-start and request-finish records containing the valid synthetic JWT query.

This directly contradicts the claimed fixed floor. The value was scrubbed from review output.

Bounded fix round:

  • enforce the exact category in IPostConfigureOptions<LoggerFilterOptions> after all normal configuration rules
  • add a Warning fallback and wrap every existing rule so this exact category is denied below Warning for every provider while preserving original behavior elsewhere
  • make the regression use a provider-specific Trace override
  • compute the leak boolean before assertion so a failed test cannot print the synthetic credential

No other CRITICAL/HIGH finding was reported by the security lens. This head must not merge.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Review fix pushed at 5975908457515f81fdfe5121d7d615882087740d.

The simple provider-agnostic filter is replaced by a post-configuration guard over LoggerFilterOptions. It adds the exact Warning fallback and wraps every configured rule, including provider-specific winners, so this exact category cannot emit below Warning while original thresholds and filters remain effective elsewhere. The regression now installs a provider-specific Trace rule for the recording provider and checks only a token occurrence count on failure.

Scoped fix verification:

  • logging plus SignalR query-auth filter: 6/6 passed
  • docs governance: passed
  • golden principles: passed
  • git diff --check origin/main...HEAD: passed

The pushed head matches the remote and remains based on current main 07534d9c653e91e827ffc19a78ada78b731bc498. Hosted CI restarted. One fresh exact-head review is now running; this is the second and final review round.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Final exact-head review receipt

Head: 5975908457515f81fdfe5121d7d615882087740d
Base: 07534d9c653e91e827ffc19a78ada78b731bc498

No CRITICAL/HIGH findings. The prior provider-specific bypass is fixed and the hostile Trace regression passes.

Tracked on umbrella #2351, not fixed in a third round:

  • MEDIUM: the exact Warning fallback can outrank a broader operator Error/None rule and therefore re-enable Warning events for Hosting.Diagnostics. The reviewed .NET 8 category has no Warning+ request-target event, so this does not restore the Information-level query-token leak.
  • LOW: dynamic configuration reload and preservation of broader Error/None settings are not exercised directly.

The statements that stricter generic thresholds remain effective are therefore not proven and are included in the #2351 residual. Per the two-round ceiling, these non-blocking findings do not start another fix cascade.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Current-base refresh pushed at 74032cfd85c7d62bedd0bc016f61e18931fbae7d.

origin/main advanced to b74b8d688d63b7b5c56d0afe2808df03c00b24b3 through #2413. The landed delta is confined to six frontend review-queue and locale files and does not overlap the SignalR logging boundary. It merged conflict-free.

Affected proof at the new head:

  • logging plus SignalR query-auth filter: 6/6 passed
  • docs governance: passed
  • golden principles: passed
  • git diff --check origin/main...HEAD: passed

Hosted CI and the three-minute aging window restarted. A fresh read-only interaction review is checking only the base movement; the prior final review disposition and tracked non-blocking #2351 residuals are unchanged.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Current-base interaction review at 74032cfd85c7d62bedd0bc016f61e18931fbae7d against b74b8d688d63b7b5c56d0afe2808df03c00b24b3: no CRITICAL/HIGH finding.

The merge preserves byte-equivalent patch sets on both parents. The PR diff remains the four SignalR logging files; the landed #2413 side remains six frontend review-queue files and does not touch authentication, SignalR transport, logging, API contracts, or persistence. git diff --check b74b8d68..74032cfd passed in the fresh read-only lens.

The prior final-review disposition and tracked MEDIUM/LOW #2351 residuals remain unchanged. Merge now waits only for exact-head CI.

@Chris0Jeky
Chris0Jeky merged commit db679b8 into main Sep 3, 2026
35 checks passed
@github-project-automation github-project-automation Bot moved this from Review to Done in Taskdeck Execution Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant