pjson_rs/config/
security.rs

1//! Security configuration and limits
2
3use crate::security::compression_bomb::CompressionBombConfig;
4use serde::{Deserialize, Serialize};
5use std::time::Duration;
6
7/// Security configuration for the PJS system
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct SecurityConfig {
10    /// JSON processing limits
11    pub json: JsonLimits,
12
13    /// Buffer management limits
14    pub buffers: BufferLimits,
15
16    /// Network and connection limits
17    pub network: NetworkLimits,
18
19    /// Session management limits
20    pub sessions: SessionLimits,
21}
22
23/// JSON processing security limits
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct JsonLimits {
26    /// Maximum JSON input size in bytes
27    pub max_input_size: usize,
28
29    /// Maximum JSON nesting depth
30    pub max_depth: usize,
31
32    /// Maximum number of keys in a JSON object
33    pub max_object_keys: usize,
34
35    /// Maximum array length
36    pub max_array_length: usize,
37
38    /// Maximum string length in JSON
39    pub max_string_length: usize,
40}
41
42/// Buffer management security limits
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct BufferLimits {
45    /// Maximum individual buffer size
46    pub max_buffer_size: usize,
47
48    /// Maximum number of buffers in pool
49    pub max_pool_size: usize,
50
51    /// Maximum total memory for all buffer pools
52    pub max_total_memory: usize,
53
54    /// Buffer time-to-live before cleanup
55    pub buffer_ttl_secs: u64,
56
57    /// Maximum buffers per size bucket
58    pub max_buffers_per_bucket: usize,
59}
60
61/// Network and connection security limits
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct NetworkLimits {
64    /// Maximum WebSocket frame size
65    pub max_websocket_frame_size: usize,
66
67    /// Maximum number of concurrent connections
68    pub max_concurrent_connections: usize,
69
70    /// Connection timeout in seconds
71    pub connection_timeout_secs: u64,
72
73    /// Maximum request rate per connection (requests per second)
74    pub max_requests_per_second: u32,
75
76    /// Maximum payload size for HTTP requests
77    pub max_http_payload_size: usize,
78
79    /// Rate limiting configuration
80    pub rate_limiting: RateLimitingConfig,
81
82    /// Compression bomb protection configuration
83    pub compression_bomb: CompressionBombConfig,
84}
85
86/// Rate limiting configuration for DoS protection
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct RateLimitingConfig {
89    /// Maximum requests per time window per IP
90    pub max_requests_per_window: u32,
91
92    /// Time window for rate limiting in seconds
93    pub window_duration_secs: u64,
94
95    /// Maximum concurrent connections per IP
96    pub max_connections_per_ip: usize,
97
98    /// Maximum WebSocket messages per second per connection
99    pub max_messages_per_second: u32,
100
101    /// Burst allowance (extra messages above rate)
102    pub burst_allowance: u32,
103
104    /// Enable rate limiting
105    pub enabled: bool,
106}
107
108/// Session management security limits
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct SessionLimits {
111    /// Maximum session ID length
112    pub max_session_id_length: usize,
113
114    /// Minimum session ID length
115    pub min_session_id_length: usize,
116
117    /// Maximum streams per session
118    pub max_streams_per_session: usize,
119
120    /// Session idle timeout in seconds
121    pub session_timeout_secs: u64,
122
123    /// Maximum session data size
124    pub max_session_data_size: usize,
125}
126
127impl Default for JsonLimits {
128    fn default() -> Self {
129        Self {
130            max_input_size: 100 * 1024 * 1024, // 100MB
131            max_depth: 64,
132            max_object_keys: 10_000,
133            max_array_length: 1_000_000,
134            max_string_length: 10 * 1024 * 1024, // 10MB
135        }
136    }
137}
138
139impl Default for BufferLimits {
140    fn default() -> Self {
141        Self {
142            max_buffer_size: 256 * 1024 * 1024, // 256MB
143            max_pool_size: 1000,
144            max_total_memory: 512 * 1024 * 1024, // 512MB
145            buffer_ttl_secs: 300,                // 5 minutes
146            max_buffers_per_bucket: 50,
147        }
148    }
149}
150
151impl Default for NetworkLimits {
152    fn default() -> Self {
153        Self {
154            max_websocket_frame_size: 16 * 1024 * 1024, // 16MB
155            max_concurrent_connections: 10_000,
156            connection_timeout_secs: 30,
157            max_requests_per_second: 100,
158            max_http_payload_size: 50 * 1024 * 1024, // 50MB
159            rate_limiting: RateLimitingConfig::default(),
160            compression_bomb: CompressionBombConfig::default(),
161        }
162    }
163}
164
165impl Default for RateLimitingConfig {
166    fn default() -> Self {
167        Self {
168            max_requests_per_window: 100,
169            window_duration_secs: 60,
170            max_connections_per_ip: 10,
171            max_messages_per_second: 30,
172            burst_allowance: 5,
173            enabled: true,
174        }
175    }
176}
177
178impl Default for SessionLimits {
179    fn default() -> Self {
180        Self {
181            max_session_id_length: 128,
182            min_session_id_length: 8,
183            max_streams_per_session: 100,
184            session_timeout_secs: 3600,               // 1 hour
185            max_session_data_size: 100 * 1024 * 1024, // 100MB
186        }
187    }
188}
189
190impl SecurityConfig {
191    /// Create a configuration optimized for high-throughput scenarios
192    pub fn high_throughput() -> Self {
193        Self {
194            json: JsonLimits {
195                max_input_size: 500 * 1024 * 1024, // 500MB
196                max_depth: 128,
197                max_object_keys: 50_000,
198                max_array_length: 5_000_000,
199                max_string_length: 50 * 1024 * 1024, // 50MB
200            },
201            buffers: BufferLimits {
202                max_buffer_size: 1024 * 1024 * 1024, // 1GB
203                max_pool_size: 5000,
204                max_total_memory: 2 * 1024 * 1024 * 1024, // 2GB
205                buffer_ttl_secs: 600,                     // 10 minutes
206                max_buffers_per_bucket: 200,
207            },
208            network: NetworkLimits {
209                max_websocket_frame_size: 100 * 1024 * 1024, // 100MB
210                max_concurrent_connections: 50_000,
211                connection_timeout_secs: 60,
212                max_requests_per_second: 1000,
213                max_http_payload_size: 200 * 1024 * 1024, // 200MB
214                rate_limiting: RateLimitingConfig {
215                    max_requests_per_window: 1000,
216                    window_duration_secs: 60,
217                    max_connections_per_ip: 50,
218                    max_messages_per_second: 100,
219                    burst_allowance: 20,
220                    enabled: true,
221                },
222                compression_bomb: CompressionBombConfig::high_throughput(),
223            },
224            sessions: SessionLimits {
225                max_session_id_length: 256,
226                min_session_id_length: 16,
227                max_streams_per_session: 1000,
228                session_timeout_secs: 7200,               // 2 hours
229                max_session_data_size: 500 * 1024 * 1024, // 500MB
230            },
231        }
232    }
233
234    /// Create a configuration optimized for low-memory environments
235    pub fn low_memory() -> Self {
236        Self {
237            json: JsonLimits {
238                max_input_size: 10 * 1024 * 1024, // 10MB
239                max_depth: 32,
240                max_object_keys: 1_000,
241                max_array_length: 100_000,
242                max_string_length: 1024 * 1024, // 1MB
243            },
244            buffers: BufferLimits {
245                max_buffer_size: 10 * 1024 * 1024, // 10MB
246                max_pool_size: 100,
247                max_total_memory: 50 * 1024 * 1024, // 50MB
248                buffer_ttl_secs: 60,                // 1 minute
249                max_buffers_per_bucket: 10,
250            },
251            network: NetworkLimits {
252                max_websocket_frame_size: 1024 * 1024, // 1MB
253                max_concurrent_connections: 1_000,
254                connection_timeout_secs: 15,
255                max_requests_per_second: 10,
256                max_http_payload_size: 5 * 1024 * 1024, // 5MB
257                rate_limiting: RateLimitingConfig {
258                    max_requests_per_window: 20,
259                    window_duration_secs: 60,
260                    max_connections_per_ip: 2,
261                    max_messages_per_second: 5,
262                    burst_allowance: 2,
263                    enabled: true,
264                },
265                compression_bomb: CompressionBombConfig::low_memory(),
266            },
267            sessions: SessionLimits {
268                max_session_id_length: 64,
269                min_session_id_length: 8,
270                max_streams_per_session: 10,
271                session_timeout_secs: 900,               // 15 minutes
272                max_session_data_size: 10 * 1024 * 1024, // 10MB
273            },
274        }
275    }
276
277    /// Create a configuration optimized for development/testing
278    pub fn development() -> Self {
279        Self {
280            json: JsonLimits {
281                max_input_size: 50 * 1024 * 1024, // 50MB
282                max_depth: 64,
283                max_object_keys: 5_000,
284                max_array_length: 500_000,
285                max_string_length: 5 * 1024 * 1024, // 5MB
286            },
287            buffers: BufferLimits {
288                max_buffer_size: 100 * 1024 * 1024, // 100MB
289                max_pool_size: 500,
290                max_total_memory: 200 * 1024 * 1024, // 200MB
291                buffer_ttl_secs: 120,                // 2 minutes
292                max_buffers_per_bucket: 25,
293            },
294            network: NetworkLimits {
295                max_websocket_frame_size: 10 * 1024 * 1024, // 10MB
296                max_concurrent_connections: 1_000,
297                connection_timeout_secs: 30,
298                max_requests_per_second: 50,
299                max_http_payload_size: 25 * 1024 * 1024, // 25MB
300                rate_limiting: RateLimitingConfig {
301                    max_requests_per_window: 200,
302                    window_duration_secs: 60,
303                    max_connections_per_ip: 20,
304                    max_messages_per_second: 50,
305                    burst_allowance: 10,
306                    enabled: true,
307                },
308                compression_bomb: CompressionBombConfig::default(),
309            },
310            sessions: SessionLimits {
311                max_session_id_length: 128,
312                min_session_id_length: 8,
313                max_streams_per_session: 50,
314                session_timeout_secs: 1800,              // 30 minutes
315                max_session_data_size: 50 * 1024 * 1024, // 50MB
316            },
317        }
318    }
319
320    /// Get buffer TTL as Duration
321    pub fn buffer_ttl(&self) -> Duration {
322        Duration::from_secs(self.buffers.buffer_ttl_secs)
323    }
324
325    /// Get connection timeout as Duration
326    pub fn connection_timeout(&self) -> Duration {
327        Duration::from_secs(self.network.connection_timeout_secs)
328    }
329
330    /// Get session timeout as Duration
331    pub fn session_timeout(&self) -> Duration {
332        Duration::from_secs(self.sessions.session_timeout_secs)
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[test]
341    fn test_default_security_config() {
342        let config = SecurityConfig::default();
343
344        // Test reasonable defaults
345        assert!(config.json.max_input_size > 0);
346        assert!(config.buffers.max_buffer_size > 0);
347        assert!(config.network.max_concurrent_connections > 0);
348        assert!(config.sessions.max_session_id_length >= config.sessions.min_session_id_length);
349    }
350
351    #[test]
352    fn test_high_throughput_config() {
353        let config = SecurityConfig::high_throughput();
354        let default = SecurityConfig::default();
355
356        // High throughput should have higher limits
357        assert!(config.json.max_input_size >= default.json.max_input_size);
358        assert!(config.buffers.max_total_memory >= default.buffers.max_total_memory);
359        assert!(
360            config.network.max_concurrent_connections >= default.network.max_concurrent_connections
361        );
362    }
363
364    #[test]
365    fn test_low_memory_config() {
366        let config = SecurityConfig::low_memory();
367        let default = SecurityConfig::default();
368
369        // Low memory should have lower limits
370        assert!(config.json.max_input_size <= default.json.max_input_size);
371        assert!(config.buffers.max_total_memory <= default.buffers.max_total_memory);
372        assert!(config.buffers.max_buffers_per_bucket <= default.buffers.max_buffers_per_bucket);
373    }
374
375    #[test]
376    fn test_duration_conversions() {
377        let config = SecurityConfig::default();
378
379        assert!(config.buffer_ttl().as_secs() > 0);
380        assert!(config.connection_timeout().as_secs() > 0);
381        assert!(config.session_timeout().as_secs() > 0);
382    }
383}