pub const MAX_INPUT_SIZE: usize = 5_242_880;
pub const MAX_STRING_LENGTH: usize = 1_048_576;
pub const MAX_ARRAY_ELEMENTS: usize = 10_000;
pub const MAX_NESTING_DEPTH: usize = 32;
pub fn pre_validate(raw_json: &str, value: &serde_json::Value) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if raw_json.len() > MAX_INPUT_SIZE {
errors.push(format!(
"input size {} bytes exceeds maximum of {} bytes",
raw_json.len(),
MAX_INPUT_SIZE
));
}
if !value.is_object() {
errors.push(format!(
"expected JSON object at root, found {}",
value_type_name(value)
));
}
check_value(value, "", &mut errors, 0);
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn pre_validate_value(value: &serde_json::Value) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if !value.is_object() {
errors.push(format!(
"expected JSON object at root, found {}",
value_type_name(value)
));
}
check_value(value, "", &mut errors, 0);
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
fn check_value(value: &serde_json::Value, path: &str, errors: &mut Vec<String>, depth: usize) {
if depth > MAX_NESTING_DEPTH {
errors.push(format!(
"{}: nesting depth exceeds maximum of {}",
if path.is_empty() { "(root)" } else { path },
MAX_NESTING_DEPTH
));
return;
}
match value {
serde_json::Value::String(s) if s.len() > MAX_STRING_LENGTH => {
errors.push(format!(
"{}: string length {} exceeds maximum of {} bytes",
if path.is_empty() { "(root)" } else { path },
s.len(),
MAX_STRING_LENGTH
));
}
serde_json::Value::Array(arr) => {
if arr.len() > MAX_ARRAY_ELEMENTS {
errors.push(format!(
"{}: array has {} elements, maximum is {}",
if path.is_empty() { "(root)" } else { path },
arr.len(),
MAX_ARRAY_ELEMENTS
));
}
for (i, item) in arr.iter().enumerate() {
let item_path = format!("{}[{}]", if path.is_empty() { "(root)" } else { path }, i);
check_value(item, &item_path, errors, depth + 1);
}
}
serde_json::Value::Object(map) => {
for (key, val) in map {
let field_path = if path.is_empty() {
key.clone()
} else {
format!("{}.{}", path, key)
};
check_value(val, &field_path, errors, depth + 1);
}
}
_ => {}
}
}
fn value_type_name(value: &serde_json::Value) -> &'static str {
match value {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "bool",
serde_json::Value::Number(_) => "number",
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pre_validate_valid() {
let json = r#"{"name": "Test", "value": 42}"#;
let value: serde_json::Value = serde_json::from_str(json).unwrap();
assert!(pre_validate(json, &value).is_ok());
}
#[test]
fn test_pre_validate_not_object() {
let json = "[1, 2, 3]";
let value: serde_json::Value = serde_json::from_str(json).unwrap();
let err = pre_validate(json, &value).unwrap_err();
assert!(err.iter().any(|e| e.contains("expected JSON object")));
}
#[test]
fn test_pre_validate_string_too_long() {
let long_string = "x".repeat(MAX_STRING_LENGTH + 1);
let json = format!(r#"{{"name": "{}"}}"#, long_string);
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
let err = pre_validate(&json, &value).unwrap_err();
assert!(err.iter().any(|e| e.contains("string length")));
}
#[test]
fn test_pre_validate_array_too_large() {
let elements: Vec<String> = (0..MAX_ARRAY_ELEMENTS + 1)
.map(|i| format!("\"x{}\"", i))
.collect();
let json = format!(r#"{{"items": [{}]}}"#, elements.join(","));
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
let err = pre_validate(&json, &value).unwrap_err();
assert!(err.iter().any(|e| e.contains("array has")));
}
#[test]
fn test_pre_validate_nesting_too_deep() {
let mut json = String::from(r#"{"a":"ok"}"#);
for _ in 0..MAX_NESTING_DEPTH + 1 {
json = format!(r#"{{"nested": {}}}"#, json);
}
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
let err = pre_validate(&json, &value).unwrap_err();
assert!(err.iter().any(|e| e.contains("nesting depth")));
}
#[test]
fn test_pre_validate_collects_all_errors() {
let long_string = "x".repeat(MAX_STRING_LENGTH + 1);
let elements: Vec<String> = (0..MAX_ARRAY_ELEMENTS + 1)
.map(|i| format!("\"x{}\"", i))
.collect();
let json = format!(
r#"{{"big": "{}", "many": [{}]}}"#,
long_string,
elements.join(",")
);
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
let err = pre_validate(&json, &value).unwrap_err();
assert!(
err.len() >= 2,
"Expected at least 2 errors, got {}: {:?}",
err.len(),
err
);
}
#[test]
fn test_pre_validate_input_too_large() {
let padding = "x".repeat(MAX_INPUT_SIZE);
let json = format!(r#"{{"data": "{}"}}"#, padding);
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
let err = pre_validate(&json, &value).unwrap_err();
assert!(err.iter().any(|e| e.contains("input size")));
}
#[test]
fn test_pre_validate_value_string_too_long() {
let long_string = "x".repeat(MAX_STRING_LENGTH + 1);
let value = serde_json::json!({"name": long_string});
let err = pre_validate_value(&value).unwrap_err();
assert!(err.iter().any(|e| e.contains("string length")));
}
#[test]
fn test_pre_validate_value_valid() {
let value = serde_json::json!({"name": "Test", "value": 42});
assert!(pre_validate_value(&value).is_ok());
}
}