veracode_platform/
json_validator.rs1use serde_json::Value;
7
8pub const MAX_JSON_DEPTH: usize = 32;
18
19pub fn validate_json_depth(json_str: &str, max_depth: usize) -> Result<(), String> {
57 let value: Value =
59 serde_json::from_str(json_str).map_err(|e| format!("Invalid JSON: {}", e))?;
60
61 let depth = calculate_depth(&value);
63
64 if depth > max_depth {
65 return Err(format!(
66 "JSON nesting depth {} exceeds maximum allowed depth of {}",
67 depth, max_depth
68 ));
69 }
70
71 Ok(())
72}
73
74fn calculate_depth(value: &Value) -> usize {
84 match value {
85 Value::Array(arr) => {
86 if arr.is_empty() {
87 1
88 } else {
89 1_usize.saturating_add(arr.iter().map(calculate_depth).max().unwrap_or(0))
90 }
91 }
92 Value::Object(obj) => {
93 if obj.is_empty() {
94 1
95 } else {
96 1_usize.saturating_add(obj.values().map(calculate_depth).max().unwrap_or(0))
97 }
98 }
99 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => 0,
101 }
102}
103
104#[cfg(test)]
105#[allow(clippy::expect_used)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_calculate_depth_scalar() {
111 let value = serde_json::json!("test");
112 assert_eq!(calculate_depth(&value), 0);
113
114 let value = serde_json::json!(42);
115 assert_eq!(calculate_depth(&value), 0);
116
117 let value = serde_json::json!(true);
118 assert_eq!(calculate_depth(&value), 0);
119
120 let value = serde_json::json!(null);
121 assert_eq!(calculate_depth(&value), 0);
122 }
123
124 #[test]
125 fn test_calculate_depth_simple_object() {
126 let value = serde_json::json!({"key": "value"});
127 assert_eq!(calculate_depth(&value), 1);
128 }
129
130 #[test]
131 fn test_calculate_depth_simple_array() {
132 let value = serde_json::json!([1, 2, 3]);
133 assert_eq!(calculate_depth(&value), 1);
134 }
135
136 #[test]
137 fn test_calculate_depth_nested_object() {
138 let value = serde_json::json!({
139 "user": {
140 "profile": {
141 "settings": {
142 "theme": "dark"
143 }
144 }
145 }
146 });
147 assert_eq!(calculate_depth(&value), 4);
148 }
149
150 #[test]
151 fn test_calculate_depth_nested_array() {
152 let value = serde_json::json!([[[1, 2], [3, 4]]]);
153 assert_eq!(calculate_depth(&value), 3);
154 }
155
156 #[test]
157 fn test_calculate_depth_mixed() {
158 let value = serde_json::json!({
159 "data": [
160 {"nested": [1, 2, 3]},
161 {"nested": [4, 5, 6]}
162 ]
163 });
164 assert_eq!(calculate_depth(&value), 4);
166 }
167
168 #[test]
169 fn test_calculate_depth_empty_structures() {
170 let value = serde_json::json!({});
171 assert_eq!(calculate_depth(&value), 1);
172
173 let value = serde_json::json!([]);
174 assert_eq!(calculate_depth(&value), 1);
175 }
176
177 #[test]
178 fn test_validate_json_depth_valid() {
179 let json = r#"{"user": {"profile": {"name": "test"}}}"#;
180 assert!(validate_json_depth(json, MAX_JSON_DEPTH).is_ok());
181 }
182
183 #[test]
184 fn test_validate_json_depth_at_limit() {
185 let mut json = String::from("{");
187 for i in 0..MAX_JSON_DEPTH - 1 {
188 json.push_str(&format!("\"level{}\":{{", i));
189 }
190 json.push_str("\"value\":42");
191 json.push_str(&"}".repeat(MAX_JSON_DEPTH));
192
193 assert!(validate_json_depth(&json, MAX_JSON_DEPTH).is_ok());
194 }
195
196 #[test]
197 fn test_validate_json_depth_exceeds_limit() {
198 let mut json = String::from("{");
200 for i in 0..MAX_JSON_DEPTH + 5 {
201 json.push_str(&format!("\"level{}\":{{", i));
202 }
203 json.push_str("\"value\":42");
204 json.push_str(&"}".repeat(MAX_JSON_DEPTH + 6));
205
206 let result = validate_json_depth(&json, MAX_JSON_DEPTH);
207 assert!(result.is_err());
208 assert!(
209 result
210 .expect_err("should fail on deeply nested json")
211 .contains("exceeds maximum allowed depth")
212 );
213 }
214
215 #[test]
216 fn test_validate_json_depth_invalid_json() {
217 let json = r#"{"invalid": json}"#;
218 let result = validate_json_depth(json, MAX_JSON_DEPTH);
219 assert!(result.is_err());
220 assert!(
221 result
222 .expect_err("should fail on invalid json")
223 .contains("Invalid JSON")
224 );
225 }
226
227 #[test]
228 fn test_validate_json_depth_deeply_nested_array() {
229 let mut json = String::new();
231 for _ in 0..50 {
232 json.push('[');
233 }
234 json.push_str("42");
235 for _ in 0..50 {
236 json.push(']');
237 }
238
239 let result = validate_json_depth(&json, MAX_JSON_DEPTH);
240 assert!(result.is_err());
241 assert!(
242 result
243 .expect_err("should fail on deeply nested json")
244 .contains("exceeds maximum allowed depth")
245 );
246 }
247
248 #[test]
249 fn test_validate_json_depth_custom_limit() {
250 let json = r#"{"a": {"b": {"c": {"d": "value"}}}}"#;
251
252 assert!(validate_json_depth(json, 5).is_ok());
254
255 let result = validate_json_depth(json, 3);
257 assert!(result.is_err());
258 }
259
260 #[test]
261 fn test_realistic_api_response() {
262 let json = r#"{
264 "_embedded": {
265 "applications": [
266 {
267 "id": 123,
268 "profile": {
269 "name": "TestApp",
270 "settings": {
271 "scan": {
272 "enabled": true
273 }
274 }
275 }
276 }
277 ]
278 }
279 }"#;
280
281 assert!(validate_json_depth(json, MAX_JSON_DEPTH).is_ok());
282 }
283
284 #[test]
285 fn test_dos_payload_detection() {
286 let depth = 100;
289 let mut json = String::new();
290
291 for i in 0..depth {
293 json.push_str(&format!("{{\"level_{}\":", i));
294 }
295 json.push_str("null");
296 for _ in 0..depth {
297 json.push('}');
298 }
299
300 let result = validate_json_depth(&json, MAX_JSON_DEPTH);
301 assert!(result.is_err());
302 assert!(
303 result
304 .expect_err("should fail on deeply nested json")
305 .contains("exceeds maximum allowed depth")
306 );
307 }
308}