cli_testing_specialist/utils/
safe_deserialize.rs

1use crate::error::{CliTestError, Result};
2use serde::de::DeserializeOwned;
3use std::io::Read;
4
5/// Maximum allowed size for JSON/YAML deserialization (10MB)
6const MAX_DESERIALIZE_SIZE: usize = 10 * 1024 * 1024;
7
8/// Maximum recursion depth for JSON/YAML deserialization (16 levels)
9const MAX_RECURSION_DEPTH: usize = 16;
10
11/// Safe JSON deserialization with size and depth limits
12///
13/// This function provides protection against:
14/// - Memory exhaustion (10MB size limit)
15/// - Stack overflow (16-level recursion depth limit)
16/// - Denial of service attacks via malicious payloads
17///
18/// # Security
19///
20/// - **Size limit**: Rejects payloads larger than 10MB
21/// - **Depth limit**: Enforced by serde_json (default max depth ~128, we validate structure)
22/// - **Performance**: O(1) size check before parsing
23///
24/// # Example
25///
26/// ```rust
27/// use cli_testing_specialist::utils::deserialize_json_safe;
28/// use serde::Deserialize;
29///
30/// #[derive(Deserialize)]
31/// struct Config {
32///     name: String,
33///     value: i32,
34/// }
35///
36/// let json = r#"{"name": "test", "value": 42}"#;
37/// let config: Config = deserialize_json_safe(json).unwrap();
38/// ```
39pub fn deserialize_json_safe<T: DeserializeOwned>(input: &str) -> Result<T> {
40    // Check size limit before parsing
41    if input.len() > MAX_DESERIALIZE_SIZE {
42        return Err(CliTestError::Validation(format!(
43            "JSON payload too large: {} bytes (max: {} bytes)",
44            input.len(),
45            MAX_DESERIALIZE_SIZE
46        )));
47    }
48
49    // Check for empty input
50    if input.trim().is_empty() {
51        return Err(CliTestError::Validation(
52            "JSON payload is empty".to_string(),
53        ));
54    }
55
56    // Deserialize with serde_json (has built-in recursion depth protection)
57    let value: T = serde_json::from_str(input)
58        .map_err(|e| CliTestError::Validation(format!("JSON deserialization failed: {}", e)))?;
59
60    // Validate depth after deserialization (additional safety check)
61    let json_value: serde_json::Value = serde_json::from_str(input)?;
62    let depth = calculate_json_depth(&json_value);
63
64    if depth > MAX_RECURSION_DEPTH {
65        return Err(CliTestError::Validation(format!(
66            "JSON depth too deep: {} levels (max: {} levels)",
67            depth, MAX_RECURSION_DEPTH
68        )));
69    }
70
71    Ok(value)
72}
73
74/// Safe JSON deserialization from reader with size limit
75///
76/// Similar to `deserialize_json_safe` but reads from a `Read` trait object.
77/// Enforces the same 10MB size limit by reading into a buffer first.
78pub fn deserialize_json_safe_from_reader<R: Read, T: DeserializeOwned>(reader: R) -> Result<T> {
79    let mut buffer = Vec::new();
80
81    // Read with size limit
82    reader
83        .take(MAX_DESERIALIZE_SIZE as u64 + 1)
84        .read_to_end(&mut buffer)?;
85
86    if buffer.len() > MAX_DESERIALIZE_SIZE {
87        return Err(CliTestError::Validation(format!(
88            "JSON payload too large: exceeds {} bytes",
89            MAX_DESERIALIZE_SIZE
90        )));
91    }
92
93    let input = String::from_utf8(buffer)
94        .map_err(|e| CliTestError::Validation(format!("Invalid UTF-8 in JSON payload: {}", e)))?;
95
96    deserialize_json_safe(&input)
97}
98
99/// Safe YAML deserialization with size and depth limits
100///
101/// This function provides protection against:
102/// - Memory exhaustion (10MB size limit)
103/// - Stack overflow (16-level recursion depth limit)
104/// - YAML bombs (deeply nested structures)
105/// - Denial of service attacks
106///
107/// # Security
108///
109/// - **Size limit**: Rejects payloads larger than 10MB
110/// - **Depth limit**: Validates structure depth after parsing
111/// - **YAML bombs**: Protected by size and depth limits
112///
113/// # Example
114///
115/// ```rust
116/// use cli_testing_specialist::utils::deserialize_yaml_safe;
117/// use serde::Deserialize;
118///
119/// #[derive(Deserialize)]
120/// struct Config {
121///     name: String,
122///     value: i32,
123/// }
124///
125/// let yaml = "name: test\nvalue: 42";
126/// let config: Config = deserialize_yaml_safe(yaml).unwrap();
127/// ```
128pub fn deserialize_yaml_safe<T: DeserializeOwned>(input: &str) -> Result<T> {
129    // Check size limit before parsing
130    if input.len() > MAX_DESERIALIZE_SIZE {
131        return Err(CliTestError::Validation(format!(
132            "YAML payload too large: {} bytes (max: {} bytes)",
133            input.len(),
134            MAX_DESERIALIZE_SIZE
135        )));
136    }
137
138    // Check for empty input
139    if input.trim().is_empty() {
140        return Err(CliTestError::Validation(
141            "YAML payload is empty".to_string(),
142        ));
143    }
144
145    // Deserialize with serde_yaml
146    let value: T = serde_yaml::from_str(input)
147        .map_err(|e| CliTestError::Validation(format!("YAML deserialization failed: {}", e)))?;
148
149    // Validate depth after deserialization (convert YAML to JSON for depth check)
150    let yaml_value: serde_yaml::Value = serde_yaml::from_str(input)?;
151    let json_value = yaml_to_json_value(&yaml_value)?;
152    let depth = calculate_json_depth(&json_value);
153
154    if depth > MAX_RECURSION_DEPTH {
155        return Err(CliTestError::Validation(format!(
156            "YAML depth too deep: {} levels (max: {} levels)",
157            depth, MAX_RECURSION_DEPTH
158        )));
159    }
160
161    Ok(value)
162}
163
164/// Safe YAML deserialization from reader with size limit
165pub fn deserialize_yaml_safe_from_reader<R: Read, T: DeserializeOwned>(reader: R) -> Result<T> {
166    let mut buffer = Vec::new();
167
168    // Read with size limit
169    reader
170        .take(MAX_DESERIALIZE_SIZE as u64 + 1)
171        .read_to_end(&mut buffer)?;
172
173    if buffer.len() > MAX_DESERIALIZE_SIZE {
174        return Err(CliTestError::Validation(format!(
175            "YAML payload too large: exceeds {} bytes",
176            MAX_DESERIALIZE_SIZE
177        )));
178    }
179
180    let input = String::from_utf8(buffer)
181        .map_err(|e| CliTestError::Validation(format!("Invalid UTF-8 in YAML payload: {}", e)))?;
182
183    deserialize_yaml_safe(&input)
184}
185
186/// Calculate the maximum depth of a JSON value tree
187fn calculate_json_depth(value: &serde_json::Value) -> usize {
188    match value {
189        serde_json::Value::Object(map) => {
190            1 + map.values().map(calculate_json_depth).max().unwrap_or(0)
191        }
192        serde_json::Value::Array(arr) => {
193            1 + arr.iter().map(calculate_json_depth).max().unwrap_or(0)
194        }
195        _ => 1,
196    }
197}
198
199/// Convert YAML value to JSON value for depth calculation
200fn yaml_to_json_value(yaml: &serde_yaml::Value) -> Result<serde_json::Value> {
201    match yaml {
202        serde_yaml::Value::Null => Ok(serde_json::Value::Null),
203        serde_yaml::Value::Bool(b) => Ok(serde_json::Value::Bool(*b)),
204        serde_yaml::Value::Number(n) => {
205            if let Some(i) = n.as_i64() {
206                Ok(serde_json::Value::Number(i.into()))
207            } else if let Some(f) = n.as_f64() {
208                serde_json::Number::from_f64(f)
209                    .map(serde_json::Value::Number)
210                    .ok_or_else(|| CliTestError::Validation("Invalid YAML number".to_string()))
211            } else {
212                Err(CliTestError::Validation("Invalid YAML number".to_string()))
213            }
214        }
215        serde_yaml::Value::String(s) => Ok(serde_json::Value::String(s.clone())),
216        serde_yaml::Value::Sequence(arr) => {
217            let json_arr: Result<Vec<_>> = arr.iter().map(yaml_to_json_value).collect();
218            Ok(serde_json::Value::Array(json_arr?))
219        }
220        serde_yaml::Value::Mapping(map) => {
221            let mut json_map = serde_json::Map::new();
222            for (k, v) in map {
223                let key = match k {
224                    serde_yaml::Value::String(s) => s.clone(),
225                    _ => {
226                        return Err(CliTestError::Validation(
227                            "YAML map key must be string".to_string(),
228                        ))
229                    }
230                };
231                json_map.insert(key, yaml_to_json_value(v)?);
232            }
233            Ok(serde_json::Value::Object(json_map))
234        }
235        serde_yaml::Value::Tagged(tagged) => yaml_to_json_value(&tagged.value),
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242    use serde::{Deserialize, Serialize};
243
244    #[derive(Debug, Serialize, Deserialize, PartialEq)]
245    struct TestStruct {
246        name: String,
247        value: i32,
248    }
249
250    // ========== JSON Tests ==========
251
252    #[test]
253    fn test_json_deserialize_safe_success() {
254        let json = r#"{"name": "test", "value": 42}"#;
255        let result: Result<TestStruct> = deserialize_json_safe(json);
256
257        assert!(result.is_ok());
258        let data = result.unwrap();
259        assert_eq!(data.name, "test");
260        assert_eq!(data.value, 42);
261    }
262
263    #[test]
264    fn test_json_deserialize_safe_empty_input() {
265        let json = "";
266        let result: Result<TestStruct> = deserialize_json_safe(json);
267
268        assert!(result.is_err());
269        assert!(result
270            .unwrap_err()
271            .to_string()
272            .contains("JSON payload is empty"));
273    }
274
275    #[test]
276    fn test_json_deserialize_safe_size_limit() {
277        // Create a JSON string larger than 10MB
278        let large_json = format!(r#"{{"data": "{}"}}"#, "x".repeat(MAX_DESERIALIZE_SIZE + 1));
279
280        let result: Result<serde_json::Value> = deserialize_json_safe(&large_json);
281
282        assert!(result.is_err());
283        assert!(result
284            .unwrap_err()
285            .to_string()
286            .contains("JSON payload too large"));
287    }
288
289    #[test]
290    fn test_json_deserialize_safe_depth_limit() {
291        // Create deeply nested JSON (17 levels, exceeds limit of 16)
292        let mut nested_json = String::from(r#"{"a":"value"}"#);
293        for _ in 0..17 {
294            nested_json = format!(r#"{{"nested":{}}}"#, nested_json);
295        }
296
297        let result: Result<serde_json::Value> = deserialize_json_safe(&nested_json);
298
299        assert!(result.is_err());
300        let err_msg = result.unwrap_err().to_string();
301        assert!(err_msg.contains("depth too deep") || err_msg.contains("17 levels"));
302    }
303
304    #[test]
305    fn test_json_deserialize_safe_valid_depth() {
306        // Create nested JSON within limit (10 levels)
307        let mut nested_json = String::from(r#"{"a":"value"}"#);
308        for _ in 0..10 {
309            nested_json = format!(r#"{{"nested":{}}}"#, nested_json);
310        }
311
312        let result: Result<serde_json::Value> = deserialize_json_safe(&nested_json);
313
314        assert!(result.is_ok());
315    }
316
317    #[test]
318    fn test_json_deserialize_safe_invalid_syntax() {
319        let json = r#"{"name": "test", "value": }"#; // Invalid JSON
320
321        let result: Result<TestStruct> = deserialize_json_safe(json);
322
323        assert!(result.is_err());
324        assert!(result
325            .unwrap_err()
326            .to_string()
327            .contains("JSON deserialization failed"));
328    }
329
330    #[test]
331    fn test_json_deserialize_from_reader() {
332        let json = r#"{"name": "test", "value": 42}"#;
333        let reader = json.as_bytes();
334
335        let result: Result<TestStruct> = deserialize_json_safe_from_reader(reader);
336
337        assert!(result.is_ok());
338        let data = result.unwrap();
339        assert_eq!(data.name, "test");
340        assert_eq!(data.value, 42);
341    }
342
343    // ========== YAML Tests ==========
344
345    #[test]
346    fn test_yaml_deserialize_safe_success() {
347        let yaml = "name: test\nvalue: 42";
348        let result: Result<TestStruct> = deserialize_yaml_safe(yaml);
349
350        assert!(result.is_ok());
351        let data = result.unwrap();
352        assert_eq!(data.name, "test");
353        assert_eq!(data.value, 42);
354    }
355
356    #[test]
357    fn test_yaml_deserialize_safe_empty_input() {
358        let yaml = "";
359        let result: Result<TestStruct> = deserialize_yaml_safe(yaml);
360
361        assert!(result.is_err());
362        assert!(result
363            .unwrap_err()
364            .to_string()
365            .contains("YAML payload is empty"));
366    }
367
368    #[test]
369    fn test_yaml_deserialize_safe_size_limit() {
370        // Create a YAML string larger than 10MB
371        let large_yaml = format!("data: {}", "x".repeat(MAX_DESERIALIZE_SIZE + 1));
372
373        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&large_yaml);
374
375        assert!(result.is_err());
376        assert!(result
377            .unwrap_err()
378            .to_string()
379            .contains("YAML payload too large"));
380    }
381
382    #[test]
383    fn test_yaml_deserialize_safe_depth_limit() {
384        // Create deeply nested YAML (17 levels)
385        let mut nested_yaml = String::from("a: value");
386        for i in 0..17 {
387            nested_yaml = format!("level{}:\n  {}", i, nested_yaml.replace('\n', "\n  "));
388        }
389
390        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&nested_yaml);
391
392        assert!(result.is_err());
393        assert!(result.unwrap_err().to_string().contains("depth too deep"));
394    }
395
396    #[test]
397    fn test_yaml_deserialize_safe_valid_depth() {
398        // Create nested YAML within limit (10 levels)
399        let mut nested_yaml = String::from("a: value");
400        for i in 0..10 {
401            nested_yaml = format!("level{}:\n  {}", i, nested_yaml.replace('\n', "\n  "));
402        }
403
404        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&nested_yaml);
405
406        assert!(result.is_ok());
407    }
408
409    #[test]
410    fn test_yaml_deserialize_from_reader() {
411        let yaml = "name: test\nvalue: 42";
412        let reader = yaml.as_bytes();
413
414        let result: Result<TestStruct> = deserialize_yaml_safe_from_reader(reader);
415
416        assert!(result.is_ok());
417        let data = result.unwrap();
418        assert_eq!(data.name, "test");
419        assert_eq!(data.value, 42);
420    }
421
422    // ========== Depth Calculation Tests ==========
423
424    #[test]
425    fn test_calculate_json_depth_simple() {
426        let json = serde_json::json!({"a": 1});
427        assert_eq!(calculate_json_depth(&json), 2); // object + leaf
428    }
429
430    #[test]
431    fn test_calculate_json_depth_nested() {
432        let json = serde_json::json!({
433            "level1": {
434                "level2": {
435                    "level3": "value"
436                }
437            }
438        });
439        assert_eq!(calculate_json_depth(&json), 4); // 3 objects + 1 leaf
440    }
441
442    #[test]
443    fn test_calculate_json_depth_array() {
444        let json = serde_json::json!([1, 2, [3, 4, [5]]]);
445        assert_eq!(calculate_json_depth(&json), 4); // 3 arrays + 1 leaf
446    }
447
448    // ========== Malicious Payload Tests ==========
449
450    #[test]
451    fn test_json_bomb_protection() {
452        // Attempt to create a "billion laughs" style payload
453        // Each level doubles the size, but we're limited by 10MB
454        let payload = r#"{"a":["x","x","x","x","x"]}"#.repeat(1000);
455
456        let result: Result<serde_json::Value> = deserialize_json_safe(&payload);
457
458        // Should fail due to size limit
459        assert!(result.is_err());
460    }
461
462    #[test]
463    fn test_yaml_bomb_protection() {
464        // YAML anchor/alias bomb attempt (simplified)
465        let yaml_bomb = format!("a: &anchor {}\nb: *anchor\n", "x".repeat(1000));
466        let repeated = yaml_bomb.repeat(1000);
467
468        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&repeated);
469
470        // Should fail due to size limit
471        assert!(result.is_err());
472    }
473
474    // ========== Boundary Value Tests ==========
475
476    #[test]
477    fn test_json_size_exactly_at_limit() {
478        // Create JSON exactly at MAX_DESERIALIZE_SIZE (10MB)
479        // {"data":"xxx..."} where total is exactly MAX_DESERIALIZE_SIZE bytes
480        let data_size = MAX_DESERIALIZE_SIZE - 11; // Account for {"data":""} = 11 bytes
481        let json = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
482
483        assert_eq!(json.len(), MAX_DESERIALIZE_SIZE);
484
485        let result: Result<serde_json::Value> = deserialize_json_safe(&json);
486        assert!(result.is_ok());
487    }
488
489    #[test]
490    fn test_json_size_one_byte_over_limit() {
491        // Create JSON exactly MAX_DESERIALIZE_SIZE + 1 bytes
492        let data_size = MAX_DESERIALIZE_SIZE - 10; // {"data":""} = 10 bytes, +1 extra = +11 total
493        let json = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
494
495        assert_eq!(json.len(), MAX_DESERIALIZE_SIZE + 1);
496
497        let result: Result<serde_json::Value> = deserialize_json_safe(&json);
498        assert!(result.is_err());
499        assert!(result
500            .unwrap_err()
501            .to_string()
502            .contains("JSON payload too large"));
503    }
504
505    #[test]
506    fn test_json_size_one_byte_under_limit() {
507        // Create JSON exactly MAX_DESERIALIZE_SIZE - 1 bytes
508        let data_size = MAX_DESERIALIZE_SIZE - 12; // Account for {"data":""} = 11 bytes
509        let json = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
510
511        assert_eq!(json.len(), MAX_DESERIALIZE_SIZE - 1);
512
513        let result: Result<serde_json::Value> = deserialize_json_safe(&json);
514        assert!(result.is_ok());
515    }
516
517    #[test]
518    fn test_yaml_size_exactly_at_limit() {
519        // Create YAML exactly at MAX_DESERIALIZE_SIZE
520        let data_size = MAX_DESERIALIZE_SIZE - 6; // "data: " = 6 bytes
521        let yaml = format!("data: {}", "x".repeat(data_size));
522
523        assert_eq!(yaml.len(), MAX_DESERIALIZE_SIZE);
524
525        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&yaml);
526        assert!(result.is_ok());
527    }
528
529    #[test]
530    fn test_yaml_size_one_byte_over_limit() {
531        // Create YAML exactly MAX_DESERIALIZE_SIZE + 1 bytes
532        let data_size = MAX_DESERIALIZE_SIZE - 5; // "data: " = 6 bytes, -1 = 5
533        let yaml = format!("data: {}", "x".repeat(data_size));
534
535        assert_eq!(yaml.len(), MAX_DESERIALIZE_SIZE + 1);
536
537        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&yaml);
538        assert!(result.is_err());
539        assert!(result
540            .unwrap_err()
541            .to_string()
542            .contains("YAML payload too large"));
543    }
544
545    // ========== Exact Recursion Depth Tests ==========
546
547    #[test]
548    fn test_json_depth_exactly_16_levels() {
549        // Create JSON with exactly 16 levels (at the limit)
550        // Start with a leaf value (depth 1), then nest 15 times
551        let mut nested_json = String::from(r#""value""#); // Leaf value = depth 1
552        for i in 0..15 {
553            nested_json = format!(r#"{{"level{}":{}}}"#, i, nested_json);
554        }
555
556        let result: Result<serde_json::Value> = deserialize_json_safe(&nested_json);
557        let value = result.expect("16 levels should be allowed");
558        assert_eq!(calculate_json_depth(&value), 16);
559    }
560
561    #[test]
562    fn test_json_depth_exactly_17_levels() {
563        // Create JSON with exactly 17 levels (exceeds limit by 1)
564        let mut nested_json = String::from(r#""value""#); // Leaf value = depth 1
565        for i in 0..16 {
566            nested_json = format!(r#"{{"level{}":{}}}"#, i, nested_json);
567        }
568
569        let result: Result<serde_json::Value> = deserialize_json_safe(&nested_json);
570        assert!(result.is_err());
571        let err_msg = result.unwrap_err().to_string();
572        assert!(err_msg.contains("17 levels") || err_msg.contains("depth too deep"));
573    }
574
575    #[test]
576    fn test_yaml_depth_exactly_16_levels() {
577        // Create YAML with exactly 16 levels
578        // Start with a string value, then nest 15 times
579        let mut nested_yaml = String::from("value"); // Leaf value = depth 1
580        for i in 0..15 {
581            nested_yaml = format!("level{}:\n  {}", i, nested_yaml.replace('\n', "\n  "));
582        }
583
584        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&nested_yaml);
585        assert!(result.is_ok());
586    }
587
588    #[test]
589    fn test_yaml_depth_exactly_17_levels() {
590        // Create YAML with exactly 17 levels (exceeds limit by 1)
591        let mut nested_yaml = String::from("value"); // Leaf value = depth 1
592        for i in 0..16 {
593            nested_yaml = format!("level{}:\n  {}", i, nested_yaml.replace('\n', "\n  "));
594        }
595
596        let result: Result<serde_yaml::Value> = deserialize_yaml_safe(&nested_yaml);
597        assert!(result.is_err());
598        assert!(result.unwrap_err().to_string().contains("depth too deep"));
599    }
600
601    // ========== Arithmetic Operator Mutation Tests ==========
602
603    #[test]
604    fn test_json_reader_size_arithmetic_plus() {
605        // Test that MAX_DESERIALIZE_SIZE + 1 is used correctly in take()
606        // If mutated to *, the limit would be much larger
607        // Use valid JSON that's exactly at the size limit
608        let data_size = MAX_DESERIALIZE_SIZE - 11; // Account for {"data":""}
609        let exact_limit_json = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
610        let reader = exact_limit_json.as_bytes();
611
612        let result: Result<serde_json::Value> = deserialize_json_safe_from_reader(reader);
613        assert!(result.is_ok());
614    }
615
616    #[test]
617    fn test_yaml_reader_size_arithmetic_plus() {
618        // Test that MAX_DESERIALIZE_SIZE + 1 is used correctly in take()
619        // Use valid YAML that's exactly at the size limit
620        let data_size = MAX_DESERIALIZE_SIZE - 6; // Account for "data: "
621        let exact_limit_yaml = format!("data: {}", "x".repeat(data_size));
622        let reader = exact_limit_yaml.as_bytes();
623
624        let result: Result<serde_yaml::Value> = deserialize_yaml_safe_from_reader(reader);
625        assert!(result.is_ok());
626    }
627
628    #[test]
629    fn test_depth_calculation_arithmetic() {
630        // Test that depth calculation uses + correctly (1 + max depth)
631        // If mutated to *, depth would be incorrectly calculated
632        let json = serde_json::json!({
633            "a": {
634                "b": {
635                    "c": "value"
636                }
637            }
638        });
639
640        let depth = calculate_json_depth(&json);
641        assert_eq!(depth, 4); // Verify correct arithmetic: 1 + 1 + 1 + 1
642    }
643
644    // ========== Comparison Operator Mutation Tests ==========
645
646    #[test]
647    fn test_json_size_comparison_greater_than() {
648        // Test that > is used correctly (not >= or ==)
649        // Size exactly at limit should succeed (using valid JSON)
650        let data_size = MAX_DESERIALIZE_SIZE - 11; // Account for {"data":""}
651        let json_at_limit = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
652        assert_eq!(json_at_limit.len(), MAX_DESERIALIZE_SIZE);
653
654        let result: Result<serde_json::Value> = deserialize_json_safe(&json_at_limit);
655        assert!(result.is_ok(), "Size at limit should succeed");
656
657        // Size over limit should fail
658        let data_size_over = MAX_DESERIALIZE_SIZE - 10;
659        let json_over_limit = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size_over));
660        assert_eq!(json_over_limit.len(), MAX_DESERIALIZE_SIZE + 1);
661
662        let result: Result<serde_json::Value> = deserialize_json_safe(&json_over_limit);
663        assert!(result.is_err(), "Size over limit should fail");
664    }
665
666    #[test]
667    fn test_json_depth_comparison_greater_than() {
668        // Test that > is used correctly for depth check (not >= or ==)
669        // Depth exactly at limit (16) should succeed
670        let mut at_limit = String::from(r#"1"#); // Start with depth 1
671        for _ in 0..15 {
672            at_limit = format!(r#"{{"n":{}}}"#, at_limit);
673        }
674
675        let result: Result<serde_json::Value> = deserialize_json_safe(&at_limit);
676        assert!(result.is_ok(), "Depth at limit should succeed");
677
678        // Depth over limit (17) should fail
679        let mut over_limit = String::from(r#"1"#); // Start with depth 1
680        for _ in 0..16 {
681            over_limit = format!(r#"{{"n":{}}}"#, over_limit);
682        }
683
684        let result: Result<serde_json::Value> = deserialize_json_safe(&over_limit);
685        assert!(result.is_err(), "Depth over limit should fail");
686    }
687
688    #[test]
689    fn test_reader_buffer_length_comparison() {
690        // Test that buffer.len() > MAX_DESERIALIZE_SIZE is checked correctly
691        // Exactly at limit should succeed (using valid JSON)
692        let data_size = MAX_DESERIALIZE_SIZE - 11;
693        let at_limit = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size));
694        let result: Result<serde_json::Value> =
695            deserialize_json_safe_from_reader(at_limit.as_bytes());
696        assert!(result.is_ok());
697
698        // Over limit should fail
699        let data_size_over = MAX_DESERIALIZE_SIZE - 10;
700        let over_limit = format!(r#"{{"data":"{}"}}"#, "x".repeat(data_size_over));
701        let result: Result<serde_json::Value> =
702            deserialize_json_safe_from_reader(over_limit.as_bytes());
703        assert!(result.is_err());
704        assert!(result.unwrap_err().to_string().contains("too large"));
705    }
706
707    // ========== Constant Value Verification Tests ==========
708
709    #[test]
710    fn test_max_deserialize_size_constant_value() {
711        // Test that MAX_DESERIALIZE_SIZE is exactly 10MB (10 * 1024 * 1024)
712        // If mutated to use + instead of *, the value would be incorrect
713        assert_eq!(MAX_DESERIALIZE_SIZE, 10485760); // 10 * 1024 * 1024 = 10485760
714
715        // Verify it's not 10 + 1024 * 1024 = 1048586
716        assert_ne!(MAX_DESERIALIZE_SIZE, 1048586);
717
718        // Verify it's not 10 * 1024 + 1024 = 11264
719        assert_ne!(MAX_DESERIALIZE_SIZE, 11264);
720    }
721
722    #[test]
723    fn test_max_deserialize_size_boundary_enforcement() {
724        // Test that the exact 10MB limit is enforced
725        // This verifies the constant is calculated correctly with multiplication
726
727        // Create data exactly 10MB - should succeed
728        let exact_10mb_data = "x".repeat(MAX_DESERIALIZE_SIZE - 11);
729        let json_10mb = format!(r#"{{"data":"{}"}}"#, exact_10mb_data);
730        assert_eq!(json_10mb.len(), 10485760); // Exactly 10 * 1024 * 1024
731
732        let result: Result<serde_json::Value> = deserialize_json_safe(&json_10mb);
733        assert!(result.is_ok(), "10MB exact should be accepted");
734
735        // Create data 10MB + 1 byte - should fail
736        let over_10mb_data = "x".repeat(MAX_DESERIALIZE_SIZE - 10);
737        let json_over_10mb = format!(r#"{{"data":"{}"}}"#, over_10mb_data);
738        assert_eq!(json_over_10mb.len(), 10485761); // 10MB + 1
739
740        let result: Result<serde_json::Value> = deserialize_json_safe(&json_over_10mb);
741        assert!(result.is_err(), "10MB + 1 should be rejected");
742    }
743
744    // ========== YAML Reader Arithmetic Tests ==========
745
746    #[test]
747    fn test_yaml_reader_take_plus_one() {
748        // Test that reader.take(MAX_DESERIALIZE_SIZE + 1) works correctly
749        // This allows reading exactly MAX_DESERIALIZE_SIZE bytes to detect overflow
750
751        // Create YAML exactly at MAX_DESERIALIZE_SIZE - should succeed
752        let data_size = MAX_DESERIALIZE_SIZE - 6;
753        let exact_limit_yaml = format!("data: {}", "x".repeat(data_size));
754        assert_eq!(exact_limit_yaml.len(), MAX_DESERIALIZE_SIZE);
755
756        let result: Result<serde_yaml::Value> =
757            deserialize_yaml_safe_from_reader(exact_limit_yaml.as_bytes());
758        assert!(
759            result.is_ok(),
760            "Exactly MAX_DESERIALIZE_SIZE should succeed"
761        );
762
763        // Create YAML exactly MAX_DESERIALIZE_SIZE + 1 - should fail
764        let data_size_over = MAX_DESERIALIZE_SIZE - 5;
765        let over_limit_yaml = format!("data: {}", "x".repeat(data_size_over));
766        assert_eq!(over_limit_yaml.len(), MAX_DESERIALIZE_SIZE + 1);
767
768        let result: Result<serde_yaml::Value> =
769            deserialize_yaml_safe_from_reader(over_limit_yaml.as_bytes());
770        assert!(result.is_err(), "MAX_DESERIALIZE_SIZE + 1 should fail");
771        assert!(result.unwrap_err().to_string().contains("too large"));
772    }
773
774    #[test]
775    fn test_yaml_reader_buffer_overflow_detection() {
776        // Test that the +1 in take() is necessary to detect overflow
777        // If mutated to * or -, overflow detection would fail
778
779        // Create a YAML payload that's exactly MAX_DESERIALIZE_SIZE + 1 bytes
780        // This should be caught by the buffer length check
781        let oversized_data = "x".repeat(MAX_DESERIALIZE_SIZE + 1);
782        let reader = oversized_data.as_bytes();
783
784        let result: Result<serde_yaml::Value> = deserialize_yaml_safe_from_reader(reader);
785
786        // Should fail with "too large" error, not a parsing error
787        assert!(result.is_err());
788        let err_msg = result.unwrap_err().to_string();
789        assert!(
790            err_msg.contains("too large") || err_msg.contains("Invalid UTF-8"),
791            "Should detect size overflow, got: {}",
792            err_msg
793        );
794    }
795
796    #[test]
797    #[cfg_attr(
798        all(target_os = "linux", not(target_env = "musl")),
799        ignore = "Requires >20MB memory allocation, fails in CI environments"
800    )]
801    fn test_json_reader_buffer_overflow_detection() {
802        // Same test for JSON reader to ensure consistent behavior
803        let oversized_data = "x".repeat(MAX_DESERIALIZE_SIZE + 1);
804        let reader = oversized_data.as_bytes();
805
806        let result: Result<serde_json::Value> = deserialize_json_safe_from_reader(reader);
807
808        assert!(result.is_err());
809        let err_msg = result.unwrap_err().to_string();
810        assert!(
811            err_msg.contains("too large") || err_msg.contains("Invalid UTF-8"),
812            "Should detect size overflow, got: {}",
813            err_msg
814        );
815    }
816}