Preserve JSON number precision in exec-server RPC messages (#33031)

## Why

Exec-server JSON-RPC payloads can contain decimals, exponent notation, and
integers outside the native 64-bit range. Decoding these values should preserve
their original JSON representation without weakening the existing message
complexity limit.

## What changed

- Enable `serde_json` arbitrary-precision number and raw-value support for the
  exec-server protocol.
- Decode serde's number and raw-value wrappers in the bounded JSON visitor,
  charging nested raw values against the 256K-value limit.
- Select the JSON-RPC envelope variant from its fields after bounded decoding so
  arbitrary-precision values survive message deserialization.

## Testing

Add coverage for exact arbitrary-precision number round trips and for enforcing
the value limit inside raw-value wrappers.

GitOrigin-RevId: a6e0a3fba6f88e4e8a6ff414832f078beb2d2a70
This commit is contained in:
jif authored and copyberry committed 2026-07-14 10:26:52 +00:00
1 parent 64c0e2fa1b
commit 7790524d6b
3 files changed
+105 -24

No files matched your search

@@ -1,4 +1,5 @@
use pretty_assertions::assert_eq;
use serde_json::Value;
use serde_json::json;
use super::JSONRPCError;
@@ -9,6 +10,7 @@ use super::JSONRPCRequest;
use super::JSONRPCResponse;
use super::MAX_JSONRPC_VALUE_NODES;
use super::RequestId;
use super::SERDE_JSON_RAW_VALUE_TOKEN;
#[test]
fn round_trips_every_jsonrpc_message_variant() -> serde_json::Result<()> {
@@ -46,6 +48,51 @@ fn round_trips_every_jsonrpc_message_variant() -> serde_json::Result<()> {
Ok(())
}
#[test]
fn round_trips_arbitrary_precision_numbers() -> serde_json::Result<()> {
let encoded = r#"{"method":"numbers","params":{"decimal":1.5,"exponent":1e100,"largeInteger":18446744073709551616}}"#;
let expected = serde_json::from_str::<Value>(encoded)?;
let message = serde_json::from_str::<JSONRPCMessage>(encoded)?;
let actual = serde_json::to_value(message)?;
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn applies_value_limit_to_raw_value_wrapper() -> serde_json::Result<()> {
let encoded =
format!(r#"{{"method":"raw","params":{{"{SERDE_JSON_RAW_VALUE_TOKEN}":"[0,1]"}}}}"#);
let actual = serde_json::from_str::<JSONRPCMessage>(&encoded)?;
let expected = JSONRPCMessage::Notification(JSONRPCNotification {
method: "raw".to_string(),
params: Some(json!([0, 1])),
});
assert_eq!(actual, expected);
let mut wrapped = String::with_capacity(2 * MAX_JSONRPC_VALUE_NODES + 1);
wrapped.push('[');
for index in 0..MAX_JSONRPC_VALUE_NODES {
if index != 0 {
wrapped.push(',');
}
wrapped.push('0');
}
wrapped.push(']');
let encoded =
format!(r#"{{"method":"raw","params":{{"{SERDE_JSON_RAW_VALUE_TOKEN}":"{wrapped}"}}}}"#);
let error = serde_json::from_str::<JSONRPCMessage>(&encoded)
.expect_err("raw value wrapper should not bypass the JSON value limit");
let expected_error = format!("exceeds the limit of {MAX_JSONRPC_VALUE_NODES} JSON values");
assert!(
error.to_string().contains(&expected_error),
"unexpected error: {error}"
);
Ok(())
}
#[test]
fn accepts_large_scalar_payload() -> serde_json::Result<()> {
let expected = JSONRPCMessage::Notification(JSONRPCNotification {