1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
//! Tests for updated dependency functionality and session management
#[cfg(test)]
mod tests {
use crate::http::session::{extract_session_id_from_cookie, generate_session_id};
use std::collections::HashSet;
#[test]
fn test_session_id_generation_with_updated_rand() {
// Test that session ID generation works with the updated rand crate (0.9.x)
let session_id = generate_session_id();
// Session ID should be 32 characters long (from Alphanumeric)
assert_eq!(session_id.len(), 32);
// Should only contain alphanumeric characters (letters and digits)
assert!(session_id.chars().all(|c| c.is_ascii_alphanumeric()));
}
#[test]
fn test_session_id_uniqueness() {
// Generate multiple session IDs to ensure they're unique
let mut session_ids = HashSet::new();
for _ in 0..1000 {
let session_id = generate_session_id();
assert!(
session_ids.insert(session_id),
"Session ID collision detected"
);
}
assert_eq!(session_ids.len(), 1000);
}
#[test]
fn test_session_id_randomness_distribution() {
// Test that generated session IDs have good entropy distribution
let mut char_counts = std::collections::HashMap::new();
// Generate many session IDs and count character frequency
for _ in 0..100 {
let session_id = generate_session_id();
for ch in session_id.chars() {
*char_counts.entry(ch).or_insert(0) += 1;
}
}
// Should have reasonable distribution across all hex characters
assert!(char_counts.len() >= 10); // Should use most hex characters
// No character should be extremely over-represented
let total_chars: usize = char_counts.values().sum();
for &count in char_counts.values() {
let frequency = count as f64 / total_chars as f64;
assert!(frequency < 0.15); // No character > 15% frequency
}
}
#[test]
fn test_cookie_parsing_with_various_formats() {
// Test various cookie header formats (function expects mcp_session= not session_id=)
let test_cases = vec![
(
Some("mcp_session=ABC123DEF456".to_string()),
Some("ABC123DEF456".to_string()),
),
(
Some("other=value; mcp_session=XYZ789; another=test".to_string()),
Some("XYZ789".to_string()),
),
(Some("mcp_session=".to_string()), Some("".to_string())), // Empty value
(Some("MCP_SESSION=should_not_match".to_string()), None), // Case sensitive
(
Some("mcp_session=value_with_equals=sign".to_string()),
Some("value_with_equals=sign".to_string()),
),
(
Some("mcp_session=value; path=/; secure".to_string()),
Some("value".to_string()),
),
(
Some("multiple=cookies; mcp_session=target_value; more=data".to_string()),
Some("target_value".to_string()),
),
(Some("no_session_cookie=present".to_string()), None),
(Some("".to_string()), None), // Empty cookie header
(None, None), // No cookie header
];
for (cookie_header, expected) in test_cases {
let result = extract_session_id_from_cookie(&cookie_header);
assert_eq!(result, expected, "Failed for cookie: '{:?}'", cookie_header);
}
}
#[test]
fn test_session_id_generation_format() {
let session_id = generate_session_id();
// Should be exactly 32 characters from Alphanumeric distribution
assert_eq!(session_id.len(), 32);
// Should only contain alphanumeric characters
assert!(session_id.chars().all(|c| c.is_ascii_alphanumeric()));
}
#[test]
fn test_concurrent_session_id_generation() {
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
let session_ids = Arc::new(Mutex::new(HashSet::new()));
let mut handles = vec![];
// Generate session IDs concurrently from multiple threads
for _ in 0..10 {
let session_ids_clone = Arc::clone(&session_ids);
let handle = thread::spawn(move || {
for _ in 0..100 {
let session_id = generate_session_id();
let mut set = session_ids_clone.lock().unwrap();
assert!(
set.insert(session_id),
"Duplicate session ID in concurrent test"
);
}
});
handles.push(handle);
}
// Wait for all threads to complete
for handle in handles {
handle.join().unwrap();
}
// Should have generated 1000 unique session IDs
let final_set = session_ids.lock().unwrap();
assert_eq!(final_set.len(), 1000);
}
#[test]
fn test_session_id_format_compliance() {
// Test that session IDs meet security requirements
for _ in 0..100 {
let session_id = generate_session_id();
// Should be exactly 32 characters (alphanumeric)
assert_eq!(session_id.len(), 32);
// Should only contain alphanumeric characters
assert!(session_id.chars().all(|c| c.is_ascii_alphanumeric()));
// Should not be all the same character
let first_char = session_id.chars().next().unwrap();
assert!(!session_id.chars().all(|c| c == first_char));
// Should have reasonable entropy (not too many repeated characters)
let unique_chars: HashSet<char> = session_id.chars().collect();
assert!(unique_chars.len() >= 5); // At least 5 different characters
}
}
#[test]
fn test_cookie_edge_cases_and_security() {
// Test potential security issues with cookie parsing
let long_value = format!("session_id={}", "A".repeat(10000));
let malicious_cases = vec![
"session_id=<script>alert('xss')</script>", // XSS attempt
"session_id=../../etc/passwd", // Path traversal attempt
&long_value, // Extremely long value
"session_id=\r\n\r\nHTTP/1.1 200 OK", // HTTP response injection
"session_id=\u{0000}\u{0000}\u{0000}\u{0000}", // Null bytes
"session_id=unicode_🦀_chars", // Unicode characters
];
for cookie_header in malicious_cases {
let cookie_opt = Some(cookie_header.to_string());
let result = extract_session_id_from_cookie(&cookie_opt);
// Should either reject malicious input or sanitize it
if let Some(session_id) = result {
// If accepted, should not contain dangerous characters
assert!(!session_id.contains("<script>"));
assert!(!session_id.contains("../"));
assert!(!session_id.contains("\r\n"));
assert!(!session_id.contains('\0'));
// Should be reasonable length
assert!(session_id.len() < 1000);
}
}
}
#[test]
fn test_updated_rand_api_compatibility() {
// Ensure we're using the updated rand 0.9 API correctly
use rand::{random, Rng};
// Test direct random generation
let random_u32: u32 = random();
let random_u64: u64 = random();
// Should generate different values
assert_ne!(random_u32 as u64, random_u64);
// Test rng usage (updated API)
let mut rng = rand::rng();
let value1: u32 = rng.random();
let value2: u32 = rng.random();
// Should generate different values
assert_ne!(value1, value2);
// Test range generation
let range_value = rng.random_range(0..100);
assert!(range_value < 100);
}
#[test]
fn test_session_lifecycle_integration() {
// Test a complete session lifecycle
let session_id = generate_session_id();
// Simulate cookie creation
let cookie_header = format!("mcp_session={}", session_id);
let cookie_opt = Some(cookie_header);
// Test extraction
let extracted_id = extract_session_id_from_cookie(&cookie_opt);
assert_eq!(extracted_id, Some(session_id.clone()));
// Test that session ID format is correct
assert_eq!(session_id.len(), 32);
assert!(session_id.chars().all(|c| c.is_ascii_alphanumeric()));
}
#[tokio::test]
async fn test_session_management_under_load() {
// Test session management under concurrent load
use std::sync::Arc;
use std::sync::Mutex;
use tokio::task;
let session_map = Arc::new(Mutex::new(std::collections::HashMap::new()));
let mut tasks = vec![];
// Simulate multiple concurrent clients creating sessions
for client_id in 0..50 {
let session_map_clone = Arc::clone(&session_map);
let task = task::spawn(async move {
for _ in 0..10 {
let session_id = generate_session_id();
let created_at = std::time::SystemTime::now();
let mut map = session_map_clone.lock().unwrap();
map.insert(session_id, (client_id, created_at));
}
});
tasks.push(task);
}
// Wait for all tasks to complete
for task in tasks {
task.await.unwrap();
}
// Should have created 500 unique sessions
let final_map = session_map.lock().unwrap();
assert_eq!(final_map.len(), 500);
// All session IDs should be unique
let session_ids: HashSet<_> = final_map.keys().collect();
assert_eq!(session_ids.len(), 500);
}
}