File size: 4,811 Bytes
854994d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use pretty_assertions::assert_eq;
use serde_json::Value;
use serde_json::json;

use super::JSONRPCError;
use super::JSONRPCErrorError;
use super::JSONRPCMessage;
use super::JSONRPCNotification;
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<()> {
    let messages = [
        JSONRPCMessage::Request(JSONRPCRequest {
            id: RequestId::Integer(1),
            method: "request".to_string(),
            params: Some(json!({"items": [1, 2, 3]})),
            trace: None,
        }),
        JSONRPCMessage::Notification(JSONRPCNotification {
            method: "notification".to_string(),
            params: Some(json!({"enabled": true})),
        }),
        JSONRPCMessage::Response(JSONRPCResponse {
            id: RequestId::String("response".to_string()),
            result: json!({"value": "ok"}),
        }),
        JSONRPCMessage::Error(JSONRPCError {
            error: JSONRPCErrorError {
                code: -32603,
                data: Some(json!({"retryable": false})),
                message: "failed".to_string(),
            },
            id: RequestId::Integer(2),
        }),
    ];

    for expected in messages {
        let encoded = serde_json::to_string(&expected)?;
        let actual = serde_json::from_str::<JSONRPCMessage>(&encoded)?;
        assert_eq!(actual, expected);
    }

    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 {
        method: "large".to_string(),
        params: Some(json!({"data": "x".repeat(MAX_JSONRPC_VALUE_NODES + 1)})),
    });

    let encoded = serde_json::to_string(&expected)?;
    let actual = serde_json::from_str::<JSONRPCMessage>(&encoded)?;

    assert_eq!(actual, expected);
    Ok(())
}

#[test]
fn rejects_duplicate_object_keys() {
    let error = serde_json::from_str::<JSONRPCMessage>(r#"{"method":"safe","method":"dangerous"}"#)
        .expect_err("duplicate JSON object keys should be rejected");

    assert!(
        error
            .to_string()
            .contains("duplicate JSON object key `method`"),
        "unexpected error: {error}"
    );
}

#[test]
fn rejects_compact_array_heap_amplification() {
    const REPRO_VALUE_COUNT: usize = 2_097_137;
    const REPRO_MESSAGE_BYTES: usize = 4_194_303;

    let mut encoded = String::with_capacity(REPRO_MESSAGE_BYTES);
    encoded.push_str(r#"{"method":"probe","params":["#);
    for index in 0..REPRO_VALUE_COUNT {
        if index != 0 {
            encoded.push(',');
        }
        encoded.push('0');
    }
    encoded.push_str("]}");
    assert_eq!(encoded.len(), REPRO_MESSAGE_BYTES);

    let error = serde_json::from_str::<JSONRPCMessage>(&encoded)
        .expect_err("amplification payload should exceed 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}"
    );
}