fix(observability): SQL leaked to Sentry a second way — EF Core error events - #446
Merged
Conversation
… events Yesterday's breadcrumb fix (#445) closed one channel, not the class. EF Core also logs a FAILED command at Error level, and Sentry's ILogger integration turns any Error into an event whose message carries the statement — so the first live capture of a PUT /me/progress failure arrived with `INSERT INTO reading_progresses (id, chapter_id, ...) VALUES (@p0, ...)` inline. No parameter values ever left the process (EnableSensitiveDataLogging is off → `@p0`/'?'; Npgsql redacts its own Detail). What leaked was statement shape and schema. Still a promise this integration made and broke. Scrub() now drops events whose Logger starts with Microsoft.EntityFrameworkCore, plus a message probe for "Executed DbCommand" / "Failed executing DbCommand". Dropping loses no signal: the same failure is reported by ExceptionMiddleware as a DbUpdateException with stack trace, SQLSTATE 23505 and the constraint name. 1284 unit tests green (3 new). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Found by the Sentry integration's own first day in production, not by a test.
PR #445 found and fixed EF Core breadcrumbs carrying
Executed DbCommand … SELECT …. That fix was real but closed one channel, not the class. EF Core also logs a failed command at Error level, and Sentry'sILoggerintegration turns any Error into an event — so the first live capture of aPUT /me/progressfailure arrived with the statement inline in the event message:Two channels, one shared wrong assumption: that the SQL lived in the structured
databag. It never did — EF interpolates the statement into the human-readable text on both paths.Scope of the exposure
EnableSensitiveDataLoggingis off, so EF renders@p0/'?', and Npgsql writes "Detail redacted as it may contain sensitive data" on the innerPostgresException.Still a promise #445 explicitly made and broke.
Changes
SentryScrubber.Scrubdrops any event whoseLoggerstarts withMicrosoft.EntityFrameworkCore, plus a message probe forExecuted DbCommand/Failed executing DbCommandso a re-categorised logger can't reopen it (IsDatabaseCommandEvent).Dropping loses no signal — that's what makes it right rather than a redaction exercise. The same failure is already reported by
ExceptionMiddlewareas aDbUpdateExceptionwith a full stack trace, SQLSTATE23505and the violated constraint name. Everything needed to debug; none of the SQL.Tests
Scrub_EfCoreCommandEvent_ReturnsNull— the exact shape that leakedScrub_DbCommandInMessageWithoutEfLogger_ReturnsNull— logger re-categorisation can't reopen itScrub_DbUpdateExceptionFromMiddleware_IsKept— locks that the signal survivesdotnet format --verify-no-changesclean.Rollback plan
git revert. No migration, no config, no schema. UnsettingSENTRY_DSNdisables the whole path regardless.Notes
The generalisable lesson: a scrubber written against one egress path will be bypassed by the next one. #445 rejected Sentry's OpenTelemetry exporter precisely because it bypasses
BeforeSend— then shipped with two log-pipeline channels doing the same thing from the inside. Unit tests were green through both, because they asserted the scrubber's behaviour on the input shape we imagined rather than what the SDK actually assembles. Only reading a real captured event found either.Follow-up (separate PR): the underlying
23505onix_reading_progresses_user_id_site_id_edition_id— an upsert race inUserDataEndpoints.UpsertProgresslosing readers' positions — which is the genuine production bug this event was reporting.🤖 Generated with Claude Code