Allow unescaped control chars at the JSON-RPC response parser (#819)

The previous fix added ALLOW_UNQUOTED_CONTROL_CHARS only to the lenient
parser used inside the upstream-settings detector. In production that
parser is never reached: the response body is parsed first by
ResponseParser.parseInternal with a default JsonFactory, which rejects
the raw LF embedded in Moca's web3_clientVersion result and turns the
response into an error before it ever reaches the detector.

Enable ALLOW_UNQUOTED_CONTROL_CHARS on the JsonFactory used by
ResponseParser so the RPC envelope parses, the result bytes flow
through, and the existing lenient detector path handles them as
before.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Vadim Filin
2026-04-30 18:14:38 +02:00
committed by GitHub
parent aeebca7248
commit c653cd9925
2 changed files with 23 additions and 1 deletions

View File

@@ -32,7 +32,15 @@ abstract class ResponseParser<T> {
private val log = LoggerFactory.getLogger(ResponseParser::class.java)
}
private val jsonFactory = JsonFactory()
// Some upstreams (e.g. Moca's Tendermint EVM) embed raw control characters
// such as LF inside JSON string values (notably in `web3_clientVersion`
// results). Default Jackson rejects those with "Illegal unquoted character"
// before the response ever reaches the upstream-settings detector, so we
// relax just this one rule at the response-parsing layer to keep the
// payload flowing.
private val jsonFactory = JsonFactory().apply {
enable(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)
}
abstract fun build(state: Preparsed): T

View File

@@ -235,4 +235,18 @@ class ResponseRpcParserSpec extends Specification {
!act.hasResult()
}
// Regression: Moca's Tendermint EVM returns a web3_clientVersion result
// that contains a raw, unescaped LF (CTRL-CHAR, code 10) inside the JSON
// string. Default Jackson rejects that with "Illegal unquoted character",
// which used to break upstream node-type detection.
def "Parse string response with unescaped control chars"() {
setup:
def json = '{"jsonrpc":"2.0","id":1,"result":"Version dev ()\nCompiled at using Go go1.23.11 (amd64)"}'
when:
def act = parser.parse(json.getBytes("UTF-8"))
then:
act.error == null
new String(act.result) == '"Version dev ()\nCompiled at using Go go1.23.11 (amd64)"'
}
}