Repository navigation
Added masking of errors caused by unexpected exceptions - #632
Draft
xperiandri wants to merge 3 commits into
Draft
xperiandri wants to merge 3 commits into
xperiandri wants to merge 3 commits into
Conversation
An exception thrown by a field resolver reached HTTP and graphql-transport-ws clients with its raw message, and HTTP responses also carried the raw message of the exception behind a request error, which the WebSocket transport already replaced with a generic one. - Added the internal ErrorMasking module: an error whose exception is neither a GraphQLException nor an IGQLError (nor an aggregate of only such exceptions) is reported as "Unexpected error", keeps its path, locations and kind extension, and its exception is logged at the error level instead - Added GraphQLOptions.MaskUnexpectedErrors, true by default, to turn masking off for development (breaking: a new field of the options record) - The HTTP handler masks the errors of direct, deferred and request error responses; the WebSocket sender, the only writer of the socket, masks every next and error message, including incremental payloads, which replaces the request-error-only sanitizeRequestError - A failed subscription source, or an exception while an operation starts, keeps the "Unexpected error during subscription" message unless masking is off, and a GraphQL error thrown while an operation starts keeps its message Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The exceptions masked in one response or message are logged once at the error level, with their number, their first paths and the first exception, and each one at the debug level: an entry with a stack trace per masked error let a client multiply the error entries of the log through aliases or a list of failing items. - An error of an IGQLError exception that the default ParseError reports by the message of the exception gets the message the exception declares for clients, as a failed subscription source already did; the internal message of such an exception reached clients. - A graphql-transport-ws operation whose query does not parse is answered with the message of the parser, as over HTTP, instead of being reported and logged as an unexpected error. - The graphql-transport-ws handler logs the errors of an operation at the debug level: its warnings repeated every masked exception with its stack trace before the sender logged it. - A payload without errors to mask is no longer copied. - Tests for logging once, the IGQLError message over HTTP and WebSocket, syntax errors over WebSocket, a GraphQL error thrown while an operation starts, the errors of a completed deferred fragment, and a failed source with masking disabled; corrected the docs and the release notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test Results 9 files 9 suites 14m 19s ⏱️ Results for commit bcbec91. ♻️ This comment has been updated with latest results. |
The tests open the namespaces instead of qualifying names and assert the absence of an extension with `Assert.DoesNotContain`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Problem
The raw message of an exception thrown by a field resolver reached HTTP and
graphql-transport-wsclients. HTTP responses also carried the raw message of the exception behind a request error, which the WebSocket transport already replaced with a generic one. Such messages can disclose connection strings, SQL or internal paths.Changes
GraphQLExceptionnor anIGQLError, nor an aggregate of only such exceptions, reaches the client asUnexpected error:kindextension;nextanderrormessage ofgraphql-transport-ws, with the incremental and completed entries of@deferand@stream.GraphQLOptions.MaskUnexpectedErrorsturns masking off for development.IGQLErrormessages. An error of anIGQLErrorexception gets the message the exception declares for clients. The defaultParseErrorreports the exception's own message, which could leak.Unexpected error during subscription;Tests
The tests are xUnit, in
AspNetCore/, over HTTP, the WebSocket connection and the subscription worker:IGQLErrormessages, syntax errors and errors at operation start.The behaviour fixes were checked to fail with the previous code.
An independent review ran the real handler and WebSocket connection with failing resolvers, streams and subscription sources. No masked message leaked, and deliberate errors, validation and coercion errors kept their messages.
Verification:
Notes
extensions.code: INTERNAL_SERVER_ERROR; adding one would let clients tell masked errors apart.RootFactorystill escapesHandleAsyncas a 500, as before, while WebSocket masks it.🤖 Generated with Claude Code