1use crate::{
4 config::SecurityConfig,
5 error::{Error, Result},
6};
7
8pub mod compression_bomb;
9pub mod rate_limit;
10
11pub use compression_bomb::{
12 CompressionBombConfig, CompressionBombDetector, CompressionBombError, CompressionBombProtector,
13 CompressionStats,
14};
15pub use rate_limit::{
16 RateLimitConfig, RateLimitError, RateLimitGuard, RateLimitStats, WebSocketRateLimiter,
17};
18
19#[derive(Debug, Clone)]
21pub struct SecurityValidator {
22 config: SecurityConfig,
23}
24
25impl SecurityValidator {
26 pub fn new(config: SecurityConfig) -> Self {
28 Self { config }
29 }
30
31 pub fn validate_input_size(&self, size: usize) -> Result<()> {
33 if size > self.config.json.max_input_size {
34 return Err(Error::Other(format!(
35 "Input size {} exceeds maximum allowed {} bytes",
36 size, self.config.json.max_input_size
37 )));
38 }
39 Ok(())
40 }
41
42 pub fn validate_json_depth(&self, depth: usize) -> Result<()> {
44 if depth > self.config.json.max_depth {
45 return Err(Error::Other(format!(
46 "JSON nesting depth {} exceeds maximum allowed {}",
47 depth, self.config.json.max_depth
48 )));
49 }
50 Ok(())
51 }
52
53 pub fn validate_json_depth_bytes(&self, input: &[u8]) -> Result<()> {
63 let mut depth: usize = 0;
64 let mut max_depth: usize = 0;
65 let mut in_string = false;
66 let mut escaped = false;
67
68 for &byte in input {
69 if in_string {
70 if escaped {
71 escaped = false;
72 } else if byte == b'\\' {
73 escaped = true;
74 } else if byte == b'"' {
75 in_string = false;
76 }
77 continue;
78 }
79
80 match byte {
81 b'"' => in_string = true,
82 b'{' | b'[' => {
83 depth += 1;
84 max_depth = max_depth.max(depth);
85 self.validate_json_depth(max_depth)?;
86 }
87 b'}' | b']' => depth = depth.saturating_sub(1),
88 _ => {}
89 }
90 }
91
92 Ok(())
93 }
94
95 pub fn validate_array_length(&self, length: usize) -> Result<()> {
97 if length > self.config.json.max_array_length {
98 return Err(Error::Other(format!(
99 "Array length {} exceeds maximum allowed {}",
100 length, self.config.json.max_array_length
101 )));
102 }
103 Ok(())
104 }
105
106 pub fn validate_object_keys(&self, key_count: usize) -> Result<()> {
108 if key_count > self.config.json.max_object_keys {
109 return Err(Error::Other(format!(
110 "Object key count {} exceeds maximum allowed {}",
111 key_count, self.config.json.max_object_keys
112 )));
113 }
114 Ok(())
115 }
116
117 pub fn validate_string_length(&self, length: usize) -> Result<()> {
119 if length > self.config.json.max_string_length {
120 return Err(Error::Other(format!(
121 "String length {} exceeds maximum allowed {}",
122 length, self.config.json.max_string_length
123 )));
124 }
125 Ok(())
126 }
127
128 pub fn validate_session_id(&self, session_id: &str) -> Result<()> {
130 let len = session_id.len();
131
132 if len < self.config.sessions.min_session_id_length {
133 return Err(Error::Other(format!(
134 "Session ID too short: {} characters (minimum {})",
135 len, self.config.sessions.min_session_id_length
136 )));
137 }
138
139 if len > self.config.sessions.max_session_id_length {
140 return Err(Error::Other(format!(
141 "Session ID too long: {} characters (maximum {})",
142 len, self.config.sessions.max_session_id_length
143 )));
144 }
145
146 if !session_id
148 .chars()
149 .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
150 {
151 return Err(Error::Other(
152 "Session ID contains invalid characters (only alphanumeric, hyphens and underscores allowed)".to_string()
153 ));
154 }
155
156 Ok(())
157 }
158
159 pub fn validate_websocket_frame_size(&self, size: usize) -> Result<()> {
161 if size > self.config.network.max_websocket_frame_size {
162 return Err(Error::Other(format!(
163 "WebSocket frame size {} exceeds maximum allowed {}",
164 size, self.config.network.max_websocket_frame_size
165 )));
166 }
167 Ok(())
168 }
169
170 pub fn validate_buffer_size(&self, size: usize) -> Result<()> {
172 if size > self.config.buffers.max_buffer_size {
173 return Err(Error::Other(format!(
174 "Buffer size {} exceeds maximum allowed {}",
175 size, self.config.buffers.max_buffer_size
176 )));
177 }
178 Ok(())
179 }
180}
181
182impl Default for SecurityValidator {
183 fn default() -> Self {
184 Self::new(SecurityConfig::default())
185 }
186}
187
188pub struct DepthTracker {
190 current_depth: usize,
191 max_depth: usize,
192}
193
194impl DepthTracker {
195 pub fn from_config(config: &SecurityConfig) -> Self {
197 Self {
198 current_depth: 0,
199 max_depth: config.json.max_depth,
200 }
201 }
202
203 pub fn with_max_depth(max_depth: usize) -> Self {
205 Self {
206 current_depth: 0,
207 max_depth,
208 }
209 }
210
211 pub fn enter(&mut self) -> Result<()> {
213 if self.current_depth >= self.max_depth {
214 return Err(Error::Other(format!(
215 "JSON nesting depth {} would exceed maximum allowed {}",
216 self.current_depth + 1,
217 self.max_depth
218 )));
219 }
220 self.current_depth += 1;
221 Ok(())
222 }
223
224 pub fn exit(&mut self) {
226 if self.current_depth > 0 {
227 self.current_depth -= 1;
228 }
229 }
230
231 pub fn current_depth(&self) -> usize {
233 self.current_depth
234 }
235}
236
237impl Default for DepthTracker {
238 fn default() -> Self {
239 Self::with_max_depth(64) }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_security_validator_default() {
249 let validator = SecurityValidator::default();
250
251 assert!(validator.validate_input_size(1024).is_ok());
252 assert!(validator.validate_json_depth(10).is_ok());
253 assert!(validator.validate_session_id("valid-session-123").is_ok());
254 }
255
256 #[test]
257 fn test_validate_json_depth_bytes_ignores_delimiters_in_strings() {
258 let validator = SecurityValidator::new(SecurityConfig {
259 json: crate::config::security::JsonLimits {
260 max_depth: 3,
261 ..SecurityConfig::default().json
262 },
263 ..SecurityConfig::default()
264 });
265
266 let json = br#"["{[{[{[{[{[", "}}}}}}}}}}"]"#;
269 assert!(validator.validate_json_depth_bytes(json).is_ok());
270 }
271
272 #[test]
273 fn test_validate_json_depth_bytes_respects_escaped_quotes() {
274 let validator = SecurityValidator::default();
275
276 let json = br#"["a\"]", [1, 2, 3]]"#;
279 assert!(validator.validate_json_depth_bytes(json).is_ok());
280 }
281
282 #[test]
283 fn test_validate_json_depth_bytes_rejects_real_nesting() {
284 let validator = SecurityValidator::new(SecurityConfig {
285 json: crate::config::security::JsonLimits {
286 max_depth: 3,
287 ..SecurityConfig::default().json
288 },
289 ..SecurityConfig::default()
290 });
291
292 let json = b"[[[[1]]]]"; assert!(validator.validate_json_depth_bytes(json).is_err());
294 }
295
296 #[test]
297 fn test_validate_json_depth_bytes_exactly_at_limit_is_ok() {
298 let validator = SecurityValidator::new(SecurityConfig {
299 json: crate::config::security::JsonLimits {
300 max_depth: 3,
301 ..SecurityConfig::default().json
302 },
303 ..SecurityConfig::default()
304 });
305
306 let json = b"[[[1]]]"; assert!(validator.validate_json_depth_bytes(json).is_ok());
308
309 let json_over = b"[[[[1]]]]"; assert!(validator.validate_json_depth_bytes(json_over).is_err());
311 }
312
313 #[test]
314 fn test_validate_json_depth_bytes_trailing_lone_backslash_does_not_panic() {
315 let validator = SecurityValidator::default();
316
317 let json = br#"["abc\"#;
320 assert!(validator.validate_json_depth_bytes(json).is_ok());
321 }
322
323 #[test]
324 fn test_validate_json_depth_bytes_unterminated_string_does_not_panic() {
325 let validator = SecurityValidator::default();
326
327 let json = br#"["abc"#;
331 assert!(validator.validate_json_depth_bytes(json).is_ok());
332 }
333
334 #[test]
335 fn test_validate_json_depth_bytes_consecutive_backslashes_toggle_correctly() {
336 let validator = SecurityValidator::new(SecurityConfig {
337 json: crate::config::security::JsonLimits {
338 max_depth: 2,
339 ..SecurityConfig::default().json
340 },
341 ..SecurityConfig::default()
342 });
343
344 let json = br#"["a\\",[1,2,3]]"#;
348 assert!(validator.validate_json_depth_bytes(json).is_ok());
349
350 let json_over = br#"[["a\\",[1,2,3]]]"#;
353 assert!(validator.validate_json_depth_bytes(json_over).is_err());
354 }
355
356 #[test]
357 fn test_security_validator_with_config() {
358 let config = SecurityConfig::low_memory();
359 let validator = SecurityValidator::new(config.clone());
360
361 assert!(
363 validator
364 .validate_input_size(config.json.max_input_size)
365 .is_ok()
366 );
367 assert!(
368 validator
369 .validate_input_size(config.json.max_input_size + 1)
370 .is_err()
371 );
372
373 assert!(validator.validate_json_depth(config.json.max_depth).is_ok());
375 assert!(
376 validator
377 .validate_json_depth(config.json.max_depth + 1)
378 .is_err()
379 );
380 }
381
382 #[test]
383 fn test_validate_session_id() {
384 let validator = SecurityValidator::default();
385
386 assert!(validator.validate_session_id("session-123").is_ok());
388 assert!(validator.validate_session_id("abcd1234-5678-90ef").is_ok());
389 assert!(validator.validate_session_id("test_session_id").is_ok());
390
391 assert!(validator.validate_session_id("ab").is_err()); assert!(validator.validate_session_id(&"a".repeat(200)).is_err()); assert!(validator.validate_session_id("session@123").is_err()); assert!(validator.validate_session_id("session 123").is_err()); }
397
398 #[test]
399 fn test_depth_tracker() {
400 let mut tracker = DepthTracker::with_max_depth(64);
401
402 assert_eq!(tracker.current_depth(), 0);
403
404 assert!(tracker.enter().is_ok());
405 assert_eq!(tracker.current_depth(), 1);
406
407 assert!(tracker.enter().is_ok());
408 assert_eq!(tracker.current_depth(), 2);
409
410 tracker.exit();
411 assert_eq!(tracker.current_depth(), 1);
412
413 tracker.exit();
414 assert_eq!(tracker.current_depth(), 0);
415 }
416
417 #[test]
418 fn test_depth_tracker_limit() {
419 let mut tracker = DepthTracker::with_max_depth(2);
420
421 assert!(tracker.enter().is_ok());
422 assert!(tracker.enter().is_ok());
423 assert!(tracker.enter().is_err()); }
425
426 #[test]
427 fn test_depth_tracker_from_config() {
428 let config = SecurityConfig::low_memory();
429 let mut tracker = DepthTracker::from_config(&config);
430
431 for _ in 0..config.json.max_depth {
433 assert!(tracker.enter().is_ok());
434 }
435 assert!(tracker.enter().is_err()); }
437
438 #[test]
439 fn test_high_throughput_config() {
440 let config = SecurityConfig::high_throughput();
441 let _validator = SecurityValidator::new(config.clone());
442
443 let default_config = SecurityConfig::default();
445 assert!(config.json.max_input_size >= default_config.json.max_input_size);
446 assert!(config.buffers.max_total_memory >= default_config.buffers.max_total_memory);
447 }
448
449 #[test]
450 fn test_validate_array_length() {
451 let config = SecurityConfig::low_memory();
452 let max_len = config.json.max_array_length;
453 let validator = SecurityValidator::new(config);
454
455 assert!(validator.validate_array_length(100).is_ok());
457
458 let result = validator.validate_array_length(max_len + 1);
460 assert!(result.is_err());
461 }
462
463 #[test]
464 fn test_validate_object_keys() {
465 let validator = SecurityValidator::default();
466
467 assert!(validator.validate_object_keys(10).is_ok());
469
470 let config = SecurityConfig::low_memory();
472 let max_keys = config.json.max_object_keys;
473 let validator = SecurityValidator::new(config);
474 let result = validator.validate_object_keys(max_keys + 1);
475 assert!(result.is_err());
476 }
477
478 #[test]
479 fn test_validate_string_length() {
480 let validator = SecurityValidator::default();
481
482 assert!(validator.validate_string_length(100).is_ok());
484
485 let config = SecurityConfig::low_memory();
487 let max_str_len = config.json.max_string_length;
488 let validator = SecurityValidator::new(config);
489 let result = validator.validate_string_length(max_str_len + 1);
490 assert!(result.is_err());
491 }
492
493 #[test]
494 fn test_validate_websocket_frame_size() {
495 let validator = SecurityValidator::default();
496
497 assert!(validator.validate_websocket_frame_size(1024).is_ok());
499
500 let config = SecurityConfig::low_memory();
502 let max_frame = config.network.max_websocket_frame_size;
503 let validator = SecurityValidator::new(config);
504 let result = validator.validate_websocket_frame_size(max_frame + 1);
505 assert!(result.is_err());
506 }
507
508 #[test]
509 fn test_validate_buffer_size() {
510 let validator = SecurityValidator::default();
511
512 assert!(validator.validate_buffer_size(4096).is_ok());
514
515 let config = SecurityConfig::low_memory();
517 let max_buf = config.buffers.max_buffer_size;
518 let validator = SecurityValidator::new(config);
519 let result = validator.validate_buffer_size(max_buf + 1);
520 assert!(result.is_err());
521 }
522
523 #[test]
524 fn test_validate_input_size_boundary() {
525 let config = SecurityConfig::low_memory();
526 let max_input = config.json.max_input_size;
527 let validator = SecurityValidator::new(config);
528
529 assert!(validator.validate_input_size(max_input).is_ok());
531
532 let result = validator.validate_input_size(max_input + 1);
534 assert!(result.is_err());
535 }
536
537 #[test]
538 fn test_validate_json_depth_boundary() {
539 let config = SecurityConfig::low_memory();
540 let max_depth = config.json.max_depth;
541 let validator = SecurityValidator::new(config);
542
543 assert!(validator.validate_json_depth(max_depth).is_ok());
545
546 let result = validator.validate_json_depth(max_depth + 1);
548 assert!(result.is_err());
549 }
550
551 #[test]
552 fn test_session_id_length_boundaries() {
553 let validator = SecurityValidator::default();
554
555 let result = validator.validate_session_id("a");
557 assert!(result.is_err());
558
559 let long_id = "a".repeat(200);
561 let result = validator.validate_session_id(&long_id);
562 assert!(result.is_err());
563
564 assert!(
566 validator
567 .validate_session_id("valid-session-id-123")
568 .is_ok()
569 );
570
571 assert!(
573 validator
574 .validate_session_id("valid_session_id_123")
575 .is_ok()
576 );
577 }
578
579 #[test]
580 fn test_session_id_invalid_characters() {
581 let validator = SecurityValidator::default();
582
583 let result = validator.validate_session_id("session@123");
585 assert!(result.is_err());
586
587 let result = validator.validate_session_id("session 123");
589 assert!(result.is_err());
590
591 let result = validator.validate_session_id("session.123");
593 assert!(result.is_err());
594
595 assert!(validator.validate_session_id("session123").is_ok());
597 }
598
599 #[test]
600 fn test_depth_tracker_boundary_cases() {
601 let mut tracker = DepthTracker::with_max_depth(1);
602
603 assert!(tracker.enter().is_ok());
605 assert_eq!(tracker.current_depth(), 1);
606
607 assert!(tracker.enter().is_err());
609 assert_eq!(tracker.current_depth(), 1); tracker.exit();
613 assert_eq!(tracker.current_depth(), 0);
614
615 assert!(tracker.enter().is_ok());
617 }
618
619 #[test]
620 fn test_depth_tracker_exit_at_zero() {
621 let mut tracker = DepthTracker::with_max_depth(64);
622
623 assert_eq!(tracker.current_depth(), 0);
625
626 tracker.exit();
628 assert_eq!(tracker.current_depth(), 0);
629
630 assert!(tracker.enter().is_ok());
632 }
633
634 #[test]
635 fn test_depth_tracker_multiple_cycles() {
636 let mut tracker = DepthTracker::with_max_depth(3);
637
638 assert!(tracker.enter().is_ok());
640 tracker.exit();
641 assert_eq!(tracker.current_depth(), 0);
642
643 assert!(tracker.enter().is_ok());
645 assert!(tracker.enter().is_ok());
646 tracker.exit();
647 assert_eq!(tracker.current_depth(), 1);
648 tracker.exit();
649 assert_eq!(tracker.current_depth(), 0);
650
651 for _ in 0..3 {
653 assert!(tracker.enter().is_ok());
654 }
655 assert_eq!(tracker.current_depth(), 3);
656 }
657}