Stop JSON parser errors from echoing peer payload - #211
Merged
Conversation
JSON::ParserError#message quotes the offending token — "expected object key, got 'SECRET-123' at line 1 column 2" — so interpolating it puts peer-controlled bytes into logs and exception messages. #210 replaced the payload-bearing log lines but kept the parser message, on the wrong assumption that it carried only a position. The leak was reachable on every JSON receive path: the GET events stream (ERROR), the POST SSE response and legacy SSE parsers (WARN, which the default logger emits), the OAuth token-refresh warning, and the "Invalid JSON response from server" TransportError raised by all four transports. describe_parse_error keeps what actually helps diagnose a broken server - the line/column and the payload size - and drops the quoted content. The regression test added in #210 passed only by accident: its sentinel sat after the first invalid token, so the parser never quoted it. The new tests place the sentinel FIRST on each path. Found by Codex follow-up review of the 2.1.0 release PR (#208). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
simonx1
added a commit
that referenced
this pull request
Aug 4, 2026
Picks up the parser-error payload leak fix and folds it into the "logs no longer contain payloads" entry, which was previously true only of the paths that log a parsed message.
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 Codex in a follow-up review of the 2.1.0 release PR (#208), verified before fixing.
JSON::ParserError#messagequotes the offending token:#210 replaced the log lines that printed payloads directly, but kept
e.message— on my assumption that it carried only a position. It doesn't, so peer-controlled bytes still reached logs.The leak was reachable on every JSON receive path:
server_streamable_http.rb)logger.errorserver_streamable_http/json_rpc_transport.rb)logger.warn— default-visibleserver_sse/sse_parser.rb)logger.warn— default-visibleauth/oauth_provider.rb)logger.warn— and the payload there is a token responseInvalid JSON response from serverTransportErrormessage, which hosts routinely logThe test that gave false assurance
Codex also spotted that the regression test I added in #210 passed by accident: its sentinel appeared after the first invalid token, so the parser never quoted it. That is the more useful half of the finding — the fix looked verified when it wasn't.
The new tests place the sentinel as the first invalid token on each path, which is the case that actually exercises the quoting.
Fix
describe_parse_error(inJsonRpcCommon, shared by every transport) keeps what makes a parse failure diagnosable — theline N column Mposition and the payload byte size — and drops the quoted content:I kept the position deliberately rather than logging only a class name: debugging a server that emits subtly broken JSON is painful without it, and the position is not peer content.
The
Invalid JSON response from server:prefix is unchanged, so existing rescues and the nine specs matching on it are unaffected.Testing
New specs assert no sentinel reaches the log on the GET, POST-SSE and legacy-SSE paths, none reaches the raised
TransportErrorfor a malformed HTTP body, and that the position and size survive. Full suite 1699 examples / 0 failures, RuboCop clean.🤖 Generated with Claude Code